From 3509664fd33c0e326a328469fdd2a8fe6cea56f5 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Mon, 8 Apr 2024 22:53:53 -0700 Subject: [PATCH 01/22] implement initial route --- backend/src/controllers/user.ts | 13 ++++++++++++- backend/src/routes/user.ts | 2 ++ backend/src/services/user.ts | 4 ++++ frontend/src/pages/UnitDetails.tsx | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/backend/src/controllers/user.ts b/backend/src/controllers/user.ts index e1910bb..7f8e210 100644 --- a/backend/src/controllers/user.ts +++ b/backend/src/controllers/user.ts @@ -6,7 +6,7 @@ import { RequestHandler } from "express"; import { asyncHandler } from "./wrappers"; -import { createUser, elevateUser, getUserByID, getUsers } from "@/services/user"; +import { createUser, demoteUser, elevateUser, getUserByID, getUsers } from "@/services/user"; export const getUsersHandler: RequestHandler = asyncHandler(async (_req, res, _next) => { const users = await getUsers(); @@ -42,3 +42,14 @@ export const elevateUserHandler: RequestHandler = asyncHandler(async (req, res, res.status(200).json(newUser); } }); + +export const demoteUserHandler: RequestHandler = asyncHandler(async (req, res, _) => { + const id = req.params.id; + const response = await demoteUser(id); + if (response === null) { + res.status(404); + } else { + const newUser = await getUserByID(id); + res.status(200).json(newUser); + } +}); diff --git a/backend/src/routes/user.ts b/backend/src/routes/user.ts index 6398006..fa5088c 100644 --- a/backend/src/routes/user.ts +++ b/backend/src/routes/user.ts @@ -19,4 +19,6 @@ router.post("/", validateWith(UserValidator.createUser), UserController.createUs router.put("/:id/elevate", requireHousingLocator, UserController.elevateUserHandler); +router.put("/:id/demote", requireHousingLocator, UserController.demoteUserHandler); + export default router; diff --git a/backend/src/services/user.ts b/backend/src/services/user.ts index 8daa91c..1abc029 100644 --- a/backend/src/services/user.ts +++ b/backend/src/services/user.ts @@ -48,3 +48,7 @@ export async function getUserByID(id: string) { export async function elevateUser(id: string) { return await UserModel.findByIdAndUpdate(id, { isHousingLocator: true }); } + +export async function demoteUser(id: string) { + return await UserModel.findByIdAndUpdate(id, { isHousingLocator: false }); +} diff --git a/frontend/src/pages/UnitDetails.tsx b/frontend/src/pages/UnitDetails.tsx index 5d3a2ac..5e7acf8 100644 --- a/frontend/src/pages/UnitDetails.tsx +++ b/frontend/src/pages/UnitDetails.tsx @@ -255,7 +255,7 @@ export function UnitDetails() { Where Was Unit Found: {unit.whereFound} Additional Rules and Regulation: - {additionalRules} + {additionalRules} Notes from Housing Locator: From 16ede2d32d62c744e701b511aeb54e26f36d87b4 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Tue, 23 Apr 2024 18:34:45 -0700 Subject: [PATCH 02/22] fixed paginator --- backend/bun.lockb | Bin 203744 -> 202563 bytes backend/package.json | 2 +- backend/src/controllers/user.ts | 6 +- frontend/bun.lockb | Bin 235719 -> 235719 bytes frontend/public/trash-can.svg | 3 + frontend/src/api/users.ts | 10 ++ .../src/components/HousingLocatorTable.tsx | 128 ++++++++++++++++++ frontend/src/pages/Profile.tsx | 25 +--- 8 files changed, 150 insertions(+), 24 deletions(-) create mode 100644 frontend/public/trash-can.svg create mode 100644 frontend/src/components/HousingLocatorTable.tsx diff --git a/backend/bun.lockb b/backend/bun.lockb index 2a1bd22de16d690c5e890496164ca71ed06ff301..77599baaad9339cd5fd73a5071393084dcd50165 100755 GIT binary patch delta 21607 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 aa*FbkP4o^sPtEJe|EjQO$pv}pbN>V2$hi6d delta 22755 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 diff --git a/backend/package.json b/backend/package.json index 53ddd06..441a05b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -46,7 +46,7 @@ "husky": "^8.0.3", "nodemon": "^3.0.1", "prettier": "^3.1.1", - "ts-node": "^10.9.1", + "ts-node": "10.4.0", "tsc-alias": "^1.8.8", "typescript": "^5.1.6" }, diff --git a/backend/src/controllers/user.ts b/backend/src/controllers/user.ts index 8aaee38..755706e 100644 --- a/backend/src/controllers/user.ts +++ b/backend/src/controllers/user.ts @@ -47,9 +47,9 @@ export const demoteUserHandler: RequestHandler = asyncHandler(async (req, res, _ const id = req.params.id; const response = await demoteUser(id); if (response === null) { - res.status(404).json("User not found"); + res.status(404); } else { - const newUser = await getUserByID(id); - res.status(200).json(newUser); + const demotedUser = await getUserByID(id); + res.status(200).json(demotedUser); } }); diff --git a/frontend/bun.lockb b/frontend/bun.lockb index ffcac76c25342440a957742f9c720b3aa431f673..7c864adaa96dbb0b17d1fd856f10cd654609cbf0 100755 GIT binary patch delta 4061 zcmXBNS@h=i9LI70wo(j15qnf=s3rE;Q){Y&=t9QQ#?p$VL@BY9SXwEnF^p!cq1wiU zbK=ZR-1I!h)iZaw%vHr66hRStkk9+(^ZI??C+Fnk3G?I$^Qe2~;iKnYAh+ zcVER4Ch*TttYC)Veu_0L5Z+(0ffb?$D7Ii&x?WZNQ^DM;#c3?e9F@-(Y zk554Th5I#e(ffXW8u?6Fq zx?58EP^Tmn@?7(`7VhVe(U#gfv4^E(%!vOAiiUo||y-cx$3HiKbV7*>3g+17BP|TnQ=Mu#n25>J` zEMNpLRxDux|BZ?j%n-auv4#c0H!C)Ye!XE5IF@qkQ zw<+c@fctjE0!HxOp;*EM{yP;bm?5}Av4#c0cPTcoLiBFM7K~I^3>}#7QA}V5)|HAW z?7@DoVg@}p?^DcS0QV}z0!HxOuUNta{s$B*m?6j%Ygi!spkf0nL?2RY!T7MQ7&>C#T*83zol5f2;R39 zOPIjFNwI<%g6}BSut0dTVgoBgwv4$S7@$nl%Le{k`!E6+ady4#wAAKccQ Ia_G+g0V#Q8dH?_b delta 4063 zcmXBNS@8De9LDka*DID1d+bq5V{2oNJ(d!CP^8w_V<}Nvszr3s)EcFlSGA&5;wv=~f;_u&hbaeT8<5~aT_@`z&?WRp8Q+loSGxJWh zgVw&uluSEnou8YMYj;}r7p4^2z1I7sDW&$H^?zkbrJc0F&8F1aSsTu#G}=WQ{o0gP zyK3X#n9^xCZSq@Fdad<4^G>yc*8aUInRe7Ve=sH2?zHY7O)0c{t+$v`Y7bifPo`Ab zNgMpxlv+D$!@ro)XcujCiz%&k)y97{rPFTO;BV}Lc7;`|1zc29<=_yO{uh#Hn`Q4T03jQ|CrKf7j1N#DXn(Z#+xafcGD*RHKjLu zg>_^$)edHl+eadU5uB40bJ&4a6)Tt^(7%t@Fhh6;ec8YQ(H#|A zSRuZXVh0-}cUJ7dI$2i=1K4*_%wPoPu8KMAz_k?%*n@XB#S#wS-(9hS34(hl)-XeO zPsIini0-A>!V2-d6+74& zm>_tNVhuBd4_0hofyh;CVTJf1iXCi_oTAu+^-x_Y3}8P@F@q7Dhb!i=1NRY%1?<5) zRk4Hv_?}_~69kV`tYL=mQHl*L5ItJ4g%#q*D0Z+x@>sFFw3>6z#AbO5s3oFFWRqSAcWu3;D24Qf(e2v6l<6vY!n+6g$`;xl*wQ>zle#7{I%*)R?_1LA?cSk?GzB}pUTmA=e CXljB0 diff --git a/frontend/public/trash-can.svg b/frontend/public/trash-can.svg new file mode 100644 index 0000000..b7647e9 --- /dev/null +++ b/frontend/public/trash-can.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/api/users.ts b/frontend/src/api/users.ts index 3713bd0..ac6de58 100644 --- a/frontend/src/api/users.ts +++ b/frontend/src/api/users.ts @@ -53,3 +53,13 @@ export async function elevateUser(user: User): Promise> { return handleAPIError(error); } } + +export async function demoteUser(user: User): Promise> { + try { + const response = await put(`/users/${user._id}/demote`, user); + const json = (await response.json()) as User; + return { success: true, data: json }; + } catch (error) { + return handleAPIError(error); + } +} diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx new file mode 100644 index 0000000..a5e1867 --- /dev/null +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -0,0 +1,128 @@ +import React, { useContext, useEffect, useState } from "react"; +import styled from "styled-components"; + +import { ReferralTablePagination } from "./ReferralTablePagination"; + +import { User, demoteUser } from "@/api/users"; +import { DataContext } from "@/contexts/DataContext"; + +const ENTRIES_PER_PAGE = 7; + +const HLTableWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + width: 75%; + margin-bottom: 10%; +`; + +const HLTableContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: left; + background-color: white; + border-radius: 8px; + gap: 20px; +`; + +const HLTableHeader = styled.div` + display: flex; + justify-content: left; + background-color: white; + margin-top: 24px; + margin-bottom: 24px; + margin-left: 19px; + margin-right: 19px; + font-weight: 700; + font-size: 16px; + + border-bottom-color: black; + border-weight: 10px; +`; + +const DeleteButton = styled.button` + align-items: center; + width: 20px; + height: 22px; + + border-width: 0px; + background-color: none; + + cursor: pointer; + transition-duration: 300ms; + &:hover { + color: pink; // fix this + } +`; + +const HLRow = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + + font-family: Montserrat; + font-size: 16px; + line-height: 24px; + + margin-left: 24px; + margin-right: 24px; + margin-bottom: 20px; +`; + +const HLTableFooter = styled.div` + padding-left: 85%; + margin: 1vh 0vw 3vh 0vw; +`; + +export const HousingLocatorTable = () => { + const dataContext = useContext(DataContext); + const [housingLocators, setHousingLocators] = useState([]); + const [pageNumber, setPageNumber] = useState(1); + + useEffect(() => { + setHousingLocators(dataContext.allHousingLocators); + }, [dataContext.allHousingLocators]); + + const handleDemote = (user: User) => { + demoteUser(user) + .then((value) => { + if (value.success) { + console.log(value.data); + dataContext.refetchData(); + } + }) + .catch((error) => { + console.log(error); + }); + }; + + return ( + + + Name + {dataContext.allHousingLocators + .slice((pageNumber - 1) * ENTRIES_PER_PAGE, pageNumber * ENTRIES_PER_PAGE) + .map((locator: User, index: number) => ( + + {locator.firstName} {locator.lastName} + { + handleDemote(locator); + }} + > + {" "} + + + + ))} + + + + + + ); +}; diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 332a758..1b58b82 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -5,6 +5,7 @@ import styled from "styled-components"; import { User, elevateUser } from "@/api/users"; import { Page } from "@/components"; import { Button } from "@/components/Button"; +import { HousingLocatorTable } from "@/components/HousingLocatorTable"; import { NavBar } from "@/components/NavBar"; import { UserDropdown } from "@/components/UserDropdown"; import { AuthContext } from "@/contexts/AuthContext"; @@ -76,18 +77,6 @@ const AssignWrapper = styled.div` max-width: 100%; `; -const HLWrapper = styled.div` - width: 100vw; - display: flex; - flex-flow: wrap; - row-gap: 12px; -`; - -const HLDiv = styled.div` - width: 27vw; - font-size: 16px; -`; - const SearchRow = styled.div` display: flex; flex-direction: row; @@ -188,14 +177,6 @@ export function Profile() { {dataContext.currentUser?.isHousingLocator && ( - - Current Housing Locators: - - {allHousingLocators.map((HS, index) => ( - {HS.firstName + " " + HS.lastName} - ))} - - Assign Housing Locators: @@ -229,6 +210,10 @@ export function Profile() { )} + + Current Housing Locators: + + )} From 31c6c1b70f45043997c0471c4901cb825d407767 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Tue, 23 Apr 2024 20:21:07 -0700 Subject: [PATCH 03/22] hover effect --- .../src/components/HousingLocatorTable.tsx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx index a5e1867..4734a8d 100644 --- a/frontend/src/components/HousingLocatorTable.tsx +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -40,18 +40,16 @@ const HLTableHeader = styled.div` border-weight: 10px; `; -const DeleteButton = styled.button` +const DeleteIcon = styled.img` align-items: center; width: 20px; height: 22px; - border-width: 0px; - background-color: none; - cursor: pointer; - transition-duration: 300ms; + transition: filter 0.3s; + &:hover { - color: pink; // fix this + filter: brightness(1.4); } `; @@ -105,14 +103,12 @@ export const HousingLocatorTable = () => { .map((locator: User, index: number) => ( {locator.firstName} {locator.lastName} - { handleDemote(locator); }} - > - {" "} - - + > ))} From 40defd7d4645c7991bfd6c0de0c0994f1bf43c8e Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Wed, 24 Apr 2024 07:32:53 -0700 Subject: [PATCH 04/22] fix lint errors --- frontend/src/components/HousingLocatorTable.tsx | 3 +-- frontend/src/pages/Profile.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx index 4734a8d..5a3aa83 100644 --- a/frontend/src/components/HousingLocatorTable.tsx +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import styled from "styled-components"; import { ReferralTablePagination } from "./ReferralTablePagination"; @@ -85,7 +85,6 @@ export const HousingLocatorTable = () => { demoteUser(user) .then((value) => { if (value.success) { - console.log(value.data); dataContext.refetchData(); } }) diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 1b58b82..5c3be33 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -124,7 +124,7 @@ const XButton = styled.div` export function Profile() { const [popup, setPopup] = useState(false); const [allReferringStaff, setAllReferringStaff] = useState([]); - const [allHousingLocators, setAllHousingLocators] = useState([]); + const [_allHousingLocators, setAllHousingLocators] = useState([]); const [currentRS, setCurrentRS] = useState(); //tracks current RS selected (for assignment) const [assignedRS, setAssignedRS] = useState(); //tracks last RS elevated (for popup) const [resetSelect, setResetSelect] = useState(false); //resets select component when state changes From e2a8aed087efa9591df8bdb58c24b43c0cabb5c0 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Wed, 24 Apr 2024 15:32:24 -0700 Subject: [PATCH 05/22] added pop-ups --- frontend/public/dark_green_check.svg | 3 + .../src/components/HousingLocatorTable.tsx | 192 +++++++++++++++++- 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 frontend/public/dark_green_check.svg diff --git a/frontend/public/dark_green_check.svg b/frontend/public/dark_green_check.svg new file mode 100644 index 0000000..d4556ff --- /dev/null +++ b/frontend/public/dark_green_check.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx index 5a3aa83..7d7ebf2 100644 --- a/frontend/src/components/HousingLocatorTable.tsx +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -1,6 +1,7 @@ import { useContext, useEffect, useState } from "react"; import styled from "styled-components"; +import { Button } from "./Button"; import { ReferralTablePagination } from "./ReferralTablePagination"; import { User, demoteUser } from "@/api/users"; @@ -72,16 +73,121 @@ const HLTableFooter = styled.div` margin: 1vh 0vw 3vh 0vw; `; +const Overlay = styled.div` + width: 100vw; + height: 100vh; + top: 0; + left: 0; + right: 0; + bottom: 0; + position: fixed; + background: rgba(0, 0, 0, 0.25); + z-index: 2; +`; + +const Modal = styled.div` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 612px; + height: 360px; + border-radius: 20px; + background: #fff; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + gap: 50px; + z-index: 2; +`; + +const HeadingWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 25px; + font-weight: 600; + font-size: 20px; + line-height: 30px; +`; + +const WarningMessageWrapper = styled.div` + display: inline; + align-items: center; + margin-left: 15%; + margin-right: 15%; + text-align: center; + margin-bottom: 0; +`; + +const ConfirmDelete = styled(Button)` + border-radius: 8px; + padding: 10px 24px; + font-size: 16px; + width: 117px; + height: 40px; + border-radius: 12px; +`; + +const ButtonsWrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + margin-left: 15%; + margin-right: 15%; + gap: 240px; +`; + +const XWrapper = styled.div` + width: 100%; + display: flex; + flex-direction: row; + justify-content: flex-end; + padding: 10px 27px; + font-size: 30px; +`; + +const XButton = styled.div` + &:hover { + cursor: pointer; + } + height: 10px; + width: 10px; +`; + +const DoneMessageHeader = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 28px; + font-size: 20px; + line-height: 30px; +`; + +const DoneMessageHeaderWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 25px; +`; + export const HousingLocatorTable = () => { const dataContext = useContext(DataContext); const [housingLocators, setHousingLocators] = useState([]); const [pageNumber, setPageNumber] = useState(1); + const [popup, setPopup] = useState(false); + const [selectedUser, setSelectedUser] = useState(null); + const [sucessfulDeletionPopup, setSucessfulDeletionPopup] = useState(false); useEffect(() => { setHousingLocators(dataContext.allHousingLocators); }, [dataContext.allHousingLocators]); const handleDemote = (user: User) => { + setSelectedUser(user); // Set the selected user before showing the pop-up + setPopup(true); demoteUser(user) .then((value) => { if (value.success) { @@ -105,12 +211,96 @@ export const HousingLocatorTable = () => { { - handleDemote(locator); + setSelectedUser(locator); + setPopup(true); }} > ))} + {popup && selectedUser && ( + <> + + + + { + setPopup(false); + }} + > + × + + + Remove Housing Locator + + Are you sure you want to remove{" "} + + {selectedUser.firstName} {selectedUser.lastName} + {" "} + as a housing locator? They will still have access as a referring staff after removal. + + + { + setPopup(false); + }} + > + Cancel + + + { + handleDemote(selectedUser); + setPopup(false); + setSucessfulDeletionPopup(true); + }} + > + Demote + + + + + )} + + {sucessfulDeletionPopup && ( + <> + + + + { + setSucessfulDeletionPopup(false); + }} + > + × + + + + + Complete + + + {selectedUser.firstName} {selectedUser.lastName} + {" "} + has been removed as a housing locator. + + + + { + setSucessfulDeletionPopup(false); + }} + > + Done + + + + + + )} Date: Tue, 30 Apr 2024 09:49:58 -0700 Subject: [PATCH 06/22] fixed lint errors --- frontend/src/components/HousingLocatorTable.tsx | 11 +++++------ frontend/src/pages/Profile.tsx | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx index 7d7ebf2..7a2a8d3 100644 --- a/frontend/src/components/HousingLocatorTable.tsx +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -211,8 +211,10 @@ export const HousingLocatorTable = () => { { - setSelectedUser(locator); - setPopup(true); + if (locator !== null) { + setSelectedUser(locator); + setPopup(true); + } }} > @@ -281,10 +283,7 @@ export const HousingLocatorTable = () => { Complete - - {selectedUser.firstName} {selectedUser.lastName} - {" "} - has been removed as a housing locator. + has been removed as a housing locator. diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 5c3be33..c0957a0 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -124,7 +124,7 @@ const XButton = styled.div` export function Profile() { const [popup, setPopup] = useState(false); const [allReferringStaff, setAllReferringStaff] = useState([]); - const [_allHousingLocators, setAllHousingLocators] = useState([]); + const [, setAllHousingLocators] = useState([]); const [currentRS, setCurrentRS] = useState(); //tracks current RS selected (for assignment) const [assignedRS, setAssignedRS] = useState(); //tracks last RS elevated (for popup) const [resetSelect, setResetSelect] = useState(false); //resets select component when state changes From e97fff232fed9440c2804c224b760be444c9b8ef Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Tue, 7 May 2024 22:07:51 -0700 Subject: [PATCH 07/22] fix name issue --- frontend/src/components/HousingLocatorTable.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx index 7a2a8d3..a8a349d 100644 --- a/frontend/src/components/HousingLocatorTable.tsx +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -283,7 +283,10 @@ export const HousingLocatorTable = () => { Complete - has been removed as a housing locator. + + {selectedUser?.firstName} {selectedUser?.lastName} + {" "} + has been removed as a housing locator. From 97d00c31c1747acbb650289b2ce3093916f5f208 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn <35088854+PoliteUnicorn@users.noreply.github.com> Date: Tue, 7 May 2024 22:09:05 -0700 Subject: [PATCH 08/22] Update frontend/src/pages/Profile.tsx Co-authored-by: Philip Zhang --- frontend/src/pages/Profile.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index c0957a0..98f0f3b 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -124,7 +124,6 @@ const XButton = styled.div` export function Profile() { const [popup, setPopup] = useState(false); const [allReferringStaff, setAllReferringStaff] = useState([]); - const [, setAllHousingLocators] = useState([]); const [currentRS, setCurrentRS] = useState(); //tracks current RS selected (for assignment) const [assignedRS, setAssignedRS] = useState(); //tracks last RS elevated (for popup) const [resetSelect, setResetSelect] = useState(false); //resets select component when state changes From 78310dcacfff16ebed424bbb2bb6203f788f46d0 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Tue, 7 May 2024 22:11:51 -0700 Subject: [PATCH 09/22] removed setallhousinglocators --- frontend/src/pages/Profile.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 98f0f3b..b434579 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -156,7 +156,6 @@ export function Profile() { useEffect(() => { setAllReferringStaff(dataContext.allReferringStaff); - setAllHousingLocators(dataContext.allHousingLocators); }, [dataContext.allReferringStaff, dataContext.allHousingLocators]); return ( From b6adff76bc989959621e3b022dd540972e896e90 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Wed, 15 May 2024 13:09:44 -0700 Subject: [PATCH 10/22] added paginator --- frontend/public/Group.svg | 3 + frontend/public/card_view_icon_selected.svg | 3 + frontend/public/card_view_icon_unselected.svg | 3 + frontend/public/list_view_icon_selected.svg | 3 + frontend/public/list_view_icon_unselected.svg | 3 + .../src/components/HousingLocatorTable.tsx | 34 ++- frontend/src/components/Pagination.tsx | 216 +++++++++++++++++ frontend/src/components/UnitCardGrid.tsx | 2 +- frontend/src/components/UnitList.tsx | 217 ++++++++++++++++++ frontend/src/pages/Home.tsx | 103 ++++++++- frontend/src/pages/UnitDetails.tsx | 109 +-------- 11 files changed, 569 insertions(+), 127 deletions(-) create mode 100644 frontend/public/Group.svg create mode 100644 frontend/public/card_view_icon_selected.svg create mode 100644 frontend/public/card_view_icon_unselected.svg create mode 100644 frontend/public/list_view_icon_selected.svg create mode 100644 frontend/public/list_view_icon_unselected.svg create mode 100644 frontend/src/components/Pagination.tsx create mode 100644 frontend/src/components/UnitList.tsx diff --git a/frontend/public/Group.svg b/frontend/public/Group.svg new file mode 100644 index 0000000..59365c0 --- /dev/null +++ b/frontend/public/Group.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/card_view_icon_selected.svg b/frontend/public/card_view_icon_selected.svg new file mode 100644 index 0000000..b6db560 --- /dev/null +++ b/frontend/public/card_view_icon_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/card_view_icon_unselected.svg b/frontend/public/card_view_icon_unselected.svg new file mode 100644 index 0000000..9b0abe2 --- /dev/null +++ b/frontend/public/card_view_icon_unselected.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/list_view_icon_selected.svg b/frontend/public/list_view_icon_selected.svg new file mode 100644 index 0000000..f467e81 --- /dev/null +++ b/frontend/public/list_view_icon_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/public/list_view_icon_unselected.svg b/frontend/public/list_view_icon_unselected.svg new file mode 100644 index 0000000..b45c0e0 --- /dev/null +++ b/frontend/public/list_view_icon_unselected.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx index a8a349d..22c7ae5 100644 --- a/frontend/src/components/HousingLocatorTable.tsx +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -2,12 +2,12 @@ import { useContext, useEffect, useState } from "react"; import styled from "styled-components"; import { Button } from "./Button"; -import { ReferralTablePagination } from "./ReferralTablePagination"; +import { Pagination } from "./Pagination"; import { User, demoteUser } from "@/api/users"; import { DataContext } from "@/contexts/DataContext"; -const ENTRIES_PER_PAGE = 7; +const ENTRIES_PER_PAGE = 2; const HLTableWrapper = styled.div` display: flex; @@ -68,9 +68,19 @@ const HLRow = styled.div` margin-bottom: 20px; `; +const HLTableFooterWrapper = styled.div` + display: flex; + flex-direction: row; + margin-right: 24px; + margin-bottom: 20px; + justify-content: right; +`; + const HLTableFooter = styled.div` - padding-left: 85%; - margin: 1vh 0vw 3vh 0vw; + display: flex; + flex-direction: row; + + justify-content: space-between; `; const Overlay = styled.div` @@ -219,6 +229,15 @@ export const HousingLocatorTable = () => { > ))} + + + + + {popup && selectedUser && ( <> @@ -303,13 +322,6 @@ export const HousingLocatorTable = () => { )} - - - ); }; diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx new file mode 100644 index 0000000..faef7b6 --- /dev/null +++ b/frontend/src/components/Pagination.tsx @@ -0,0 +1,216 @@ +import styled from "styled-components"; + +const NumPageButton = styled.button` + width: 25px; + height: 24px; + border-radius: 4px; + border: 1px solid #eeeeee; + opacity: 0px; + background: #f5f5f5; + align-content: center; + + &:hover { + cursor: pointer; + background: #f5f5f5; + } + + &.active { + background: #ec8537; + color: #ffffff; + border-color: #ec8537; + } +`; + +const ButtonWrapper = styled.div` + display: flex; + flex-direction: row; + //width: 341.87px; + height: 24px; + justify-content: space-between; + gap: 20px; + opacity: 0px; +`; + +const NavButton = styled(NumPageButton)``; + +const NavButtonIcon = styled.img` + width: 24px; + height: 24px; + flex-shrink: 0; + border-radius: 4px; + border: 0; +`; + +const EllipsesWrapper = styled.div` + width: 24px; + height: 24px; +`; + +type PaginationProps = { + totalPages: number; + currPage: number; + setPageNumber: (newPageNumber: number) => void; +}; + +const BUTTONS_PER_PAGE = 5; + +export const Pagination = (props: PaginationProps) => { + const pages = []; + + const handleClick = (increase: boolean): void => { + if (increase && props.currPage !== props.totalPages) { + props.setPageNumber(props.currPage + 1); + } + + if (!increase && props.currPage > 1) { + props.setPageNumber(props.currPage - 1); + } + }; + + //case 1: num pages < buttons per page -> show all + if (props.totalPages <= BUTTONS_PER_PAGE) { + for (let i = 1; i <= props.totalPages; i++) { + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + } + } + //case 2: on page 1-3 -> show first 4 ... and last + else if (props.currPage <= BUTTONS_PER_PAGE - 2) { + for (let i = 1; i <= props.totalPages; i++) { + //add button for first three pages + if (i <= BUTTONS_PER_PAGE - 1) { + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + } else if (i === props.totalPages) { + pages.push(...); + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + } + } + } + //case 3: on page end - 4 + 1-> show first ... last 4 + else if (props.currPage > props.totalPages - BUTTONS_PER_PAGE + 2) { + //add button for first three pages + for (let i = 1; i <= props.totalPages; i++) { + if (i >= props.totalPages - BUTTONS_PER_PAGE + 2) { + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + } else if (i === 1) { + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + pages.push(...); + } + } + } + //case 4: middle -> show first ... middle - 1, middle, middle + 1, ... last + else { + for (let i = 1; i <= props.totalPages; i++) { + if (i === 1) { + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + pages.push(...); + } else if (i === props.currPage - 1 || i === props.currPage || i === props.currPage + 1) { + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + } else if (i === props.totalPages) { + pages.push(...); + pages.push( + { + props.setPageNumber(i); + }} + className={props.currPage === i ? "active" : ""} + > + {i} + , + ); + } + } + } + + return ( + + + { + handleClick(false); + }} + /> + + {pages} + + { + handleClick(true); + }} + /> + + + ); +}; diff --git a/frontend/src/components/UnitCardGrid.tsx b/frontend/src/components/UnitCardGrid.tsx index d26cdec..bfc054f 100644 --- a/frontend/src/components/UnitCardGrid.tsx +++ b/frontend/src/components/UnitCardGrid.tsx @@ -23,6 +23,7 @@ const PropertiesRow = styled.span` font-size: 27px; font-weight: 700; margin-bottom: 15px; + justify-content: space-between; `; const HeaderText = styled.span` @@ -43,7 +44,6 @@ const ButtonsWrapper = styled.div` display: flex; flex-direction: row; left: 74vw; - position: absolute; `; const PendingButton = styled.div<{ selected: boolean }>` diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx new file mode 100644 index 0000000..3eb53c5 --- /dev/null +++ b/frontend/src/components/UnitList.tsx @@ -0,0 +1,217 @@ +import { useContext, useState } from "react"; +import { Link } from "react-router-dom"; +import styled from "styled-components"; + +import { Unit } from "@/api/units"; +import { DataContext } from "@/contexts/DataContext"; + +const UnitTableWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; + margin: 95px; + margin-top: 50px; + margin-right: 5%; + gap: 30px; +`; + +const UnitTable = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + margin-bottom: 30px; + background-color: white; +`; + +const UnitTableHeaderRow = styled.div` + display: flex; + flex-direction: row; + margin: 44px; + margin-top: 32px; + margin-bottom: 17px; + justify-content: space-between; + gap: 20px; //causes problems when shrinking, status overflows out of table + border-bottom: 0.4px solid #cdcacacc; +`; + +const UnitTableRow = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + font-family: Montserrat; + color: black; + margin: 44px; + margin-bottom: 17px; + margin-top: 0px; + padding-top: 17px; + padding-bottom: 17px; + + border-bottom: 0.4px solid #cdcacacc; + gap: 20px; + &:hover { + background-color: #ec85371a; + } +`; + +const UnitItemWrapper = styled.div` + display: flex; + flex-direction: column; + font-size: 16px; + font-family: "Montserrat"; + line-height: 18.4px; + align-items: center; + width: 150px; +`; + +const ListingAddressWrapper = styled.div` + display: flex; + flex-direction: column; + font-size: 16px; + font-family: "Montserrat"; + line-height: 18.4px; + width: 150px; + align-items: center; +`; + +const UnitHeaderItemWrapper = styled.div` + display: flex; + flex-direction: column; + font-family: "Poppins"; + font-size: 14px; + line-height: 21px; + color: #909090; + align-items: center; + width: 150px; +`; + +// problem when shrinking screen +const PropertiesRow = styled.span` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + color: black; + font-family: "Montserrat"; + font-size: 27px; + font-weight: 700; + margin-bottom: 15px; +`; + +const ButtonsWrapper = styled.div` + display: flex; + flex-direction: row; + left: 72vw; +`; + +const PendingButton = styled.div<{ selected: boolean }>` + display: flex; + width: 164px; + height: 55px; + align-items: center; + justify-content: center; + border-radius: ${(props) => (props.selected ? "12px" : "12px 0px 0px 12px")}; + border: 1px solid ${(props) => (props.selected ? "rgba(162, 61, 4, 0.80)" : "#EEE")}; + background: ${(props) => (props.selected ? "#B64201" : "#EEE")}; + color: ${(props) => (props.selected ? "#EEE" : "#2E2E2E")}; + font-family: Poppins; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: normal; + z-index: ${(props) => (props.selected ? 1 : 0)}; + cursor: pointer; +`; + +const ListingsButton = styled(PendingButton)` + border-radius: ${(props) => (props.selected ? "12px" : "0px 12px 12px 0px")}; + position: relative; + left: -10px; +`; + +const HeaderText = styled.span` + font-family: "Neutraface Text"; + font-size: 32px; +`; + +export type UnitListProps = { + units: Unit[]; + showPendingUnits?: boolean; + refreshUnits: (approved: "pending" | "approved") => void; +}; + +export const UnitList = ({ units, refreshUnits, showPendingUnits = false }: UnitListProps) => { + const [pendingSelected, setPendingSelected] = useState(showPendingUnits); + + const dataContext = useContext(DataContext); + + return ( + + + {pendingSelected ? ( + Pending Approval + ) : ( + Available Properties + )} + {dataContext.currentUser?.isHousingLocator && ( + + { + setPendingSelected(true); + refreshUnits("pending"); + }} + selected={pendingSelected} + > + Pending Listings + + { + setPendingSelected(false); + refreshUnits("approved"); + }} + selected={!pendingSelected} + > + All Listings + + + )} + + + + Listing Address + Price + Beds + Baths + Sqft + Status + + <> + {units.length > 0 && + units.map((unit: Unit, index) => ( + + + {unit.listingAddress} + ${unit.monthlyRent} + {unit.numBeds} + {unit.numBaths} + {unit.sqft} + + {" "} + {unit.availableNow && unit.approved ? ( + Available + ) : !unit.approved ? ( + Pending + ) : unit.leasedStatus !== undefined ? ( + Leased + ) : ( + Not Available + )} + + + + ))} + {units.length === 0 && No matching units found} + + + + ); +}; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index c47d070..d82bc20 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,12 +1,54 @@ import React, { useEffect, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useLocation } from "react-router-dom"; +import styled from "styled-components"; import { FilterParams, Unit, getUnits } from "@/api/units"; import { FilterDropdown } from "@/components/FilterDropdown"; import { NavBar } from "@/components/NavBar"; import { Page } from "@/components/Page"; import { UnitCardGrid } from "@/components/UnitCardGrid"; +import { UnitList } from "@/components/UnitList"; + +const ButtonsWrapper = styled.div` + display: flex; + flex: row; + justify-content: end; + margin: 0; + margin-right: 100px; +`; + +const ToggleButtonWrapper = styled.div` + display: flex; + padding: 0; + width: 195px; + height: 140px; + gap: 0px; + border-radius: 100px 0px 0px 0px; + opacity: 0px; +`; + +const CardViewButton = styled.img<{ selected: boolean }>` + width: 100px; + height: 50px; + padding: 7px 33px 8px 32px; + gap: 8px; + border-radius: 100px 0px 0px 100px; + opacity: 0px; + background: ${(props) => (props.selected ? "#ec85371a" : "#EEEEEE")}; + + &:hover { + opacity: 0px; + background: #ec85371a; + cursor: pointer; + } +`; + +const ListViewButton = styled(CardViewButton)` + padding: 7px 32px 7px 32px; + border-radius: 0px 100px 100px 0px; + background: ${(props) => (props.selected ? "#ec85371a" : "#EEEEEE")}; +`; export const FiltersContext = React.createContext({ filters: {} as FilterParams, @@ -21,6 +63,7 @@ export function Home() { approved: "approved", }, ); + const [viewMode, setViewMode] = useState("card"); const fetchUnits = (filterParams: FilterParams) => { getUnits(filterParams) @@ -36,6 +79,14 @@ export function Home() { fetchUnits(filters); }, [filters]); + const handleCardView = () => { + setViewMode("card"); + }; + + const handleListView = () => { + setViewMode("list"); + }; + return ( @@ -50,15 +101,49 @@ export function Home() { setFilters(filterParams); }} > - { - const newFilters = { ...filters, approved }; - fetchUnits(newFilters); - setFilters(newFilters); - }} - /> + + + + + + + {viewMode === "card" ? ( + { + const newFilters = { ...filters, approved }; + fetchUnits(newFilters); + setFilters(newFilters); + }} + /> + ) : ( + { + const newFilters = { ...filters, approved }; + fetchUnits(newFilters); + setFilters(newFilters); + }} + /> + )} ); diff --git a/frontend/src/pages/UnitDetails.tsx b/frontend/src/pages/UnitDetails.tsx index 610e133..e877330 100644 --- a/frontend/src/pages/UnitDetails.tsx +++ b/frontend/src/pages/UnitDetails.tsx @@ -1,13 +1,11 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useState } from "react"; import { Helmet } from "react-helmet-async"; -import { Carousel } from "react-responsive-carousel"; import { useParams } from "react-router"; import { Link, useLocation, useNavigate } from "react-router-dom"; import styled from "styled-components"; import { Loading } from "./Loading"; -import { getFileURLS } from "@/api/images"; import { FilterParams, Unit, approveUnit, getUnit, updateUnit } from "@/api/units"; import { Page } from "@/components"; import { Banner } from "@/components/Banner"; @@ -18,8 +16,6 @@ import { NavBar } from "@/components/NavBar"; import { ReferralTable } from "@/components/ReferralTable"; import { DataContext } from "@/contexts/DataContext"; -import "react-responsive-carousel/lib/styles/carousel.min.css"; - const Section = styled.div` display: flex; flex-direction: column; @@ -70,7 +66,7 @@ const MainColumn = styled.div` const DetailsColumn = styled(MainColumn)` margin: 32px 160px; - gap: 40px; + gap: 60px; `; const RentPerMonth = styled.h1` @@ -269,46 +265,6 @@ const Heading = styled.h1` font-family: "Neutraface Text"; `; -const LeftArrowWrapper = styled.div` - position: absolute; - top: 23vh; - left: 0.5vw; - z-index: 2; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - user-select: none; -`; - -const RightArrowWrapper = styled.div` - position: absolute; - z-index: 2; - top: 23vh; - right: 0.5vw; - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - user-select: none; -`; - -const CarouselImage = styled.img` - object-fit: cover; - height: 50vh; - max-width: 40vw; - user-select: none; - padding: 0px 7.5px; -`; - -const CarouselVideo = styled.video` - object-fit: cover; - height: 50vh; - max-width: 40vw; - user-select: none; - padding: 0px 7.5px; -`; - export function UnitDetails() { const filters = useLocation().state as FilterParams; const navigate = useNavigate(); @@ -334,27 +290,6 @@ export function UnitDetails() { //checks for which view to return const [isEditing, setIsEditing] = useState(false); - const [imgUrls, setImgUrls] = useState([]); - const [vidUrls, setVidUrls] = useState([]); - - const handleGetFiles = () => { - getFileURLS(id ?? "", "images") - .then((urls) => { - setImgUrls(urls); - }) - .catch(console.error); - - getFileURLS(id ?? "", "videos") - .then((urls) => { - setVidUrls(urls); - }) - .catch(console.error); - }; - - useEffect(() => { - handleGetFiles(); - }, [isEditing]); - const toggleEditing = () => { setIsEditing((prevState) => !prevState); }; @@ -547,6 +482,7 @@ export function UnitDetails() { )} + - 1} - centerSlidePercentage={50} - showStatus={false} - swipeable={false} - transitionTime={400} - renderArrowPrev={(clickHandler, hasPrev) => - hasPrev && ( - - Arrow - - ) - } - renderArrowNext={(clickHandler, hasNext) => - hasNext && ( - - Arrow - - ) - } - > - {imgUrls - .map((url, index) => ) - .concat( - vidUrls.map((url, index) => ( - - - - - )), - )} - -
From 838b4767dc2929f71abb8507e501c3366b48965a Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Wed, 22 May 2024 12:36:02 -0700 Subject: [PATCH 11/22] cosmetic changes --- .../src/components/HousingLocatorTable.tsx | 2 +- frontend/src/components/UnitCardGrid.tsx | 5 +- frontend/src/components/UnitList.tsx | 103 ++++++++++++------ frontend/src/pages/Home.tsx | 67 +++++++----- 4 files changed, 111 insertions(+), 66 deletions(-) diff --git a/frontend/src/components/HousingLocatorTable.tsx b/frontend/src/components/HousingLocatorTable.tsx index 22c7ae5..0eccf70 100644 --- a/frontend/src/components/HousingLocatorTable.tsx +++ b/frontend/src/components/HousingLocatorTable.tsx @@ -7,7 +7,7 @@ import { Pagination } from "./Pagination"; import { User, demoteUser } from "@/api/users"; import { DataContext } from "@/contexts/DataContext"; -const ENTRIES_PER_PAGE = 2; +const ENTRIES_PER_PAGE = 7; const HLTableWrapper = styled.div` display: flex; diff --git a/frontend/src/components/UnitCardGrid.tsx b/frontend/src/components/UnitCardGrid.tsx index fe88e4a..51a6e4d 100644 --- a/frontend/src/components/UnitCardGrid.tsx +++ b/frontend/src/components/UnitCardGrid.tsx @@ -7,7 +7,6 @@ import { UnitCard } from "@/components/UnitCard"; import { DataContext } from "@/contexts/DataContext"; const UnitCardLayout = styled.div` - // margin: 95px; display: flex; flex-direction: row; flex-wrap: wrap; @@ -23,7 +22,6 @@ const PropertiesRow = styled.span` font-size: 27px; font-weight: 700; margin-bottom: 15px; - justify-content: space-between; `; const HeaderText = styled.span` @@ -43,7 +41,8 @@ const GridContainer = styled.div` const ButtonsWrapper = styled.div` display: flex; flex-direction: row; - left: 74vw; + position: absolute; + left: 69vw; `; const PendingButton = styled.div<{ selected: boolean }>` diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx index 3eb53c5..b3829ee 100644 --- a/frontend/src/components/UnitList.tsx +++ b/frontend/src/components/UnitList.tsx @@ -2,16 +2,19 @@ import { useContext, useState } from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; +import { Pagination } from "./Pagination"; + import { Unit } from "@/api/units"; import { DataContext } from "@/contexts/DataContext"; +const ENTRIES_PER_PAGE = 6; + const UnitTableWrapper = styled.div` display: flex; flex-direction: column; - justify-content: flex-start; margin: 95px; - margin-top: 50px; - margin-right: 5%; + margin-top: 39px; + margin-right: 1%; gap: 30px; `; @@ -26,9 +29,11 @@ const UnitTable = styled.div` const UnitTableHeaderRow = styled.div` display: flex; flex-direction: row; - margin: 44px; + margin-left: 44px; + margin-right: 44px; margin-top: 32px; - margin-bottom: 17px; + + padding-bottom: 17px; justify-content: space-between; gap: 20px; //causes problems when shrinking, status overflows out of table border-bottom: 0.4px solid #cdcacacc; @@ -40,16 +45,18 @@ const UnitTableRow = styled.div` justify-content: space-between; font-family: Montserrat; color: black; - margin: 44px; - margin-bottom: 17px; - margin-top: 0px; - padding-top: 17px; + margin-left: 44px; + margin-right: 44px; + height: 90px; + padding-bottom: 17px; + padding-top: 17px; border-bottom: 0.4px solid #cdcacacc; gap: 20px; &:hover { background-color: #ec85371a; + border: 0.4px solid #b64201; } `; @@ -94,13 +101,14 @@ const PropertiesRow = styled.span` font-family: "Montserrat"; font-size: 27px; font-weight: 700; + margin: 0; margin-bottom: 15px; `; const ButtonsWrapper = styled.div` display: flex; flex-direction: row; - left: 72vw; + left: 77vw; `; const PendingButton = styled.div<{ selected: boolean }>` @@ -133,6 +141,23 @@ const HeaderText = styled.span` font-size: 32px; `; +const UnitListFooterWrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: right; + margin-left: 44px; + margin-right: 44px; + padding-top: 17px; + height: 90px; +`; + +const UnitListFooter = styled.div` + display: flex; + flex-direction: row; + + justify-content: space-between; +`; + export type UnitListProps = { units: Unit[]; showPendingUnits?: boolean; @@ -141,6 +166,7 @@ export type UnitListProps = { export const UnitList = ({ units, refreshUnits, showPendingUnits = false }: UnitListProps) => { const [pendingSelected, setPendingSelected] = useState(showPendingUnits); + const [pageNumber, setPageNumber] = useState(1); const dataContext = useContext(DataContext); @@ -186,31 +212,42 @@ export const UnitList = ({ units, refreshUnits, showPendingUnits = false }: Unit <> {units.length > 0 && - units.map((unit: Unit, index) => ( - - - {unit.listingAddress} - ${unit.monthlyRent} - {unit.numBeds} - {unit.numBaths} - {unit.sqft} - - {" "} - {unit.availableNow && unit.approved ? ( - Available - ) : !unit.approved ? ( - Pending - ) : unit.leasedStatus !== undefined ? ( - Leased - ) : ( - Not Available - )} - - - - ))} + units + .slice((pageNumber - 1) * ENTRIES_PER_PAGE, pageNumber * ENTRIES_PER_PAGE) + .map((unit: Unit, index) => ( + + + {unit.listingAddress} + ${unit.monthlyRent} + {unit.numBeds} + {unit.numBaths} + {unit.sqft} + + {" "} + {unit.availableNow && unit.approved ? ( + Available + ) : !unit.approved ? ( + Pending + ) : unit.leasedStatus !== undefined ? ( + Leased + ) : ( + Not Available + )} + + + + ))} {units.length === 0 && No matching units found} + + + + + ); diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index fc9bea1..79aca2f 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -16,7 +16,9 @@ const ButtonsWrapper = styled.div` flex: row; justify-content: end; margin: 0; + margin-top: 55px; margin-right: 100px; + margin-left: 310px; `; const ToggleButtonWrapper = styled.div` @@ -51,6 +53,11 @@ const ListViewButton = styled(CardViewButton)` background: ${(props) => (props.selected ? "#ec85371a" : "#EEEEEE")}; `; +const SearchStateWrapper = styled.div` + display: flex; + flex-direction: row; +`; + export const FiltersContext = React.createContext({ filters: {} as FilterParams, }); @@ -111,35 +118,37 @@ export function Home() {
- { - filterParams.approved = filters.approved; - setFilters(filterParams); - }} - > - - - - - - + + { + filterParams.approved = filters.approved; + setFilters(filterParams); + }} + > + + + + + + + {viewMode === "card" ? ( Date: Wed, 22 May 2024 12:48:32 -0700 Subject: [PATCH 12/22] change all paginators --- frontend/src/components/ReferralTable.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/ReferralTable.tsx b/frontend/src/components/ReferralTable.tsx index 6a85e57..6c857d4 100644 --- a/frontend/src/components/ReferralTable.tsx +++ b/frontend/src/components/ReferralTable.tsx @@ -2,7 +2,7 @@ import React, { useContext, useState } from "react"; import styled from "styled-components"; import { Button } from "./Button"; -import { ReferralTablePagination } from "./ReferralTablePagination"; +import { Pagination } from "./Pagination"; import { ReferralTableRow } from "./ReferralTableRow"; import { updateReferral, updateReferralRequest } from "@/api/referrals"; @@ -16,7 +16,7 @@ type ReferralTableProps = { id: string; }; -const ENTRIES_PER_PAGE = 5; +const ENTRIES_PER_PAGE = 3; const TableColumnNames = [ "Name", @@ -104,8 +104,14 @@ const ReferralTableColumnHeaderEnd = styled(ReferralTableColumnHeader)` `; const ReferralTableFooter = styled.div` + display: flex; + flex-direction: row; + justify-content: right; padding-left: 85%; - margin: 1vh 0vw 3vh 0vw; + background: white; + padding-top: 17px; + padding-bottom: 17px; + padding-right: 2vw; `; const ReferralTablePlaceholder = styled.div` @@ -263,7 +269,7 @@ export const ReferralTable = (props: ReferralTableProps) => { /> ))} - Date: Wed, 22 May 2024 12:59:15 -0700 Subject: [PATCH 13/22] border to available and pending --- frontend/src/components/UnitList.tsx | 33 ++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx index b3829ee..247e957 100644 --- a/frontend/src/components/UnitList.tsx +++ b/frontend/src/components/UnitList.tsx @@ -158,6 +158,31 @@ const UnitListFooter = styled.div` justify-content: space-between; `; +const AvailableWrapper = styled.div` + display: flex; + flex-direction: row; + width: 85.45px; + height: 29px; + padding: 4px 12px 4px 12px; + gap: 10px; + border-radius: 4px; + background: #7a923a33; + border: 1px solid #7a923a; + color: #7a923a; + font-family: Poppins; + font-size: 14px; + font-weight: 500; + line-height: 21px; + letter-spacing: -0.01em; + justify-content: center; +`; + +const PendingWrapper = styled(AvailableWrapper)` + background: #b6420133; + border: 1px solid #b64201; + color: #b64201; +`; + export type UnitListProps = { units: Unit[]; showPendingUnits?: boolean; @@ -225,9 +250,13 @@ export const UnitList = ({ units, refreshUnits, showPendingUnits = false }: Unit {" "} {unit.availableNow && unit.approved ? ( - Available + + Available + ) : !unit.approved ? ( - Pending + + Pending + ) : unit.leasedStatus !== undefined ? ( Leased ) : ( From b747b003799462e7a474232cc2f64aea43b4d5c1 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Wed, 22 May 2024 13:07:04 -0700 Subject: [PATCH 14/22] more details --- frontend/src/components/UnitList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx index 247e957..b9f0317 100644 --- a/frontend/src/components/UnitList.tsx +++ b/frontend/src/components/UnitList.tsx @@ -7,7 +7,7 @@ import { Pagination } from "./Pagination"; import { Unit } from "@/api/units"; import { DataContext } from "@/contexts/DataContext"; -const ENTRIES_PER_PAGE = 6; +const ENTRIES_PER_PAGE = 1; const UnitTableWrapper = styled.div` display: flex; From 4083eea676d3eadd856f4dbdcb9ae0e4c75c5007 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Wed, 22 May 2024 13:23:28 -0700 Subject: [PATCH 15/22] fixed all paginators --- frontend/src/components/Table.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Table.tsx b/frontend/src/components/Table.tsx index 05f429b..41a3ecb 100644 --- a/frontend/src/components/Table.tsx +++ b/frontend/src/components/Table.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import styled from "styled-components"; -import { TablePagination } from "./TablePagination"; +import { Pagination } from "./Pagination"; const TableContainer = styled.div` display: flex; @@ -43,6 +43,9 @@ const TableCell = styled.div` `; const TableFooter = styled.div` + display: flex; + flex-direction: row; + justify-content: right; padding-top: 20px; `; @@ -82,7 +85,7 @@ export const Table = (props: TableProps) => { ))} {/* TODO: Replace this with new pagination when it is implemented */} - Date: Wed, 22 May 2024 15:24:09 -0700 Subject: [PATCH 16/22] fix font --- frontend/src/components/UnitCard.tsx | 2 +- frontend/src/components/UnitList.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/UnitCard.tsx b/frontend/src/components/UnitCard.tsx index 79c907f..a6ebf31 100644 --- a/frontend/src/components/UnitCard.tsx +++ b/frontend/src/components/UnitCard.tsx @@ -84,7 +84,7 @@ const RentText = styled(UnitCardText)` font-weight: 700; line-height: 121%; letter-spacing: 0.331px; - font-family: Inter; + font-family: "Neutraface Text"; `; const AddressText = styled(UnitCardText)` diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx index b9f0317..7347c77 100644 --- a/frontend/src/components/UnitList.tsx +++ b/frontend/src/components/UnitList.tsx @@ -7,14 +7,14 @@ import { Pagination } from "./Pagination"; import { Unit } from "@/api/units"; import { DataContext } from "@/contexts/DataContext"; -const ENTRIES_PER_PAGE = 1; +const ENTRIES_PER_PAGE = 6; const UnitTableWrapper = styled.div` display: flex; flex-direction: column; margin: 95px; margin-top: 39px; - margin-right: 1%; + margin-right: 7.5%; gap: 30px; `; From ef333a97f0127f28cbc17bf1335dc655e0b6a079 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn <35088854+PoliteUnicorn@users.noreply.github.com> Date: Wed, 29 May 2024 15:08:01 -0700 Subject: [PATCH 17/22] Update frontend/src/components/ReferralTable.tsx Co-authored-by: Philip Zhang --- frontend/src/components/ReferralTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/ReferralTable.tsx b/frontend/src/components/ReferralTable.tsx index 45b63a6..471e0e0 100644 --- a/frontend/src/components/ReferralTable.tsx +++ b/frontend/src/components/ReferralTable.tsx @@ -1,7 +1,6 @@ import React, { useContext, useState } from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; -// import { Pagination } from "./Pagination"; import { Button } from "./Button"; import { ReferralTableDropDown } from "./ReferralTableDropDown"; From fd78c8020e982fc468cc902d7faef95101a150c3 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Wed, 29 May 2024 15:12:12 -0700 Subject: [PATCH 18/22] fix changes --- frontend/public/Group.svg | 3 --- frontend/src/components/ReferralTable.tsx | 1 - 2 files changed, 4 deletions(-) delete mode 100644 frontend/public/Group.svg diff --git a/frontend/public/Group.svg b/frontend/public/Group.svg deleted file mode 100644 index 59365c0..0000000 --- a/frontend/public/Group.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/components/ReferralTable.tsx b/frontend/src/components/ReferralTable.tsx index 45b63a6..471e0e0 100644 --- a/frontend/src/components/ReferralTable.tsx +++ b/frontend/src/components/ReferralTable.tsx @@ -1,7 +1,6 @@ import React, { useContext, useState } from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; -// import { Pagination } from "./Pagination"; import { Button } from "./Button"; import { ReferralTableDropDown } from "./ReferralTableDropDown"; From dcf11b930e9f8a7250ad3cde1c6fc40e982e06e0 Mon Sep 17 00:00:00 2001 From: PoliteUnicorn Date: Mon, 3 Jun 2024 16:27:41 -0700 Subject: [PATCH 19/22] center aligned --- frontend/src/components/UnitList.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx index 7347c77..be6e428 100644 --- a/frontend/src/components/UnitList.tsx +++ b/frontend/src/components/UnitList.tsx @@ -43,11 +43,12 @@ const UnitTableRow = styled.div` display: flex; flex-direction: row; justify-content: space-between; + align-items: center; font-family: Montserrat; color: black; margin-left: 44px; margin-right: 44px; - height: 90px; + height: 100px; padding-bottom: 17px; padding-top: 17px; @@ -65,7 +66,6 @@ const UnitItemWrapper = styled.div` flex-direction: column; font-size: 16px; font-family: "Montserrat"; - line-height: 18.4px; align-items: center; width: 150px; `; @@ -76,7 +76,8 @@ const ListingAddressWrapper = styled.div` font-size: 16px; font-family: "Montserrat"; line-height: 18.4px; - width: 150px; + width: 200px; + height: 73px; align-items: center; `; @@ -183,6 +184,11 @@ const PendingWrapper = styled(AvailableWrapper)` color: #b64201; `; +const UnitNotFoundWrapper = styled.div` + margin-top: 40px; + align-self: center; +`; + export type UnitListProps = { units: Unit[]; showPendingUnits?: boolean; @@ -266,7 +272,7 @@ export const UnitList = ({ units, refreshUnits, showPendingUnits = false }: Unit ))} - {units.length === 0 && No matching units found} + {units.length === 0 && No matching units found} From 45969556b70fe249302f28ddfc12bbe6e517f7f3 Mon Sep 17 00:00:00 2001 From: Philip Zhang Date: Mon, 3 Jun 2024 20:58:28 -0700 Subject: [PATCH 20/22] refactor --- frontend/src/components/FilterDropdown.tsx | 3 - frontend/src/components/FilterPanel.tsx | 5 +- frontend/src/components/UnitCardGrid.tsx | 112 ++--------- frontend/src/components/UnitList.tsx | 204 ++++++--------------- frontend/src/pages/Home.tsx | 116 +++++++++--- 5 files changed, 159 insertions(+), 281 deletions(-) diff --git a/frontend/src/components/FilterDropdown.tsx b/frontend/src/components/FilterDropdown.tsx index 7f131ee..b3c9f88 100644 --- a/frontend/src/components/FilterDropdown.tsx +++ b/frontend/src/components/FilterDropdown.tsx @@ -9,9 +9,6 @@ const AllFiltersContainer = styled.div` flex-direction: column; justify-content: flex-start; align-items: flex-start; - margin-left: 95px; - margin-right: 95px; - margin-top: 55px; gap: 16px; `; diff --git a/frontend/src/components/FilterPanel.tsx b/frontend/src/components/FilterPanel.tsx index 751333b..ef1a870 100644 --- a/frontend/src/components/FilterPanel.tsx +++ b/frontend/src/components/FilterPanel.tsx @@ -18,12 +18,9 @@ import { FiltersContext } from "@/pages/Home"; const PanelBackground = styled.div` min-width: 284px; + max-height: calc(100vh - 70px); background-color: #fff; - position: fixed; - top: 70px; - bottom: 0; - overflow-y: scroll; overflow-x: hidden; diff --git a/frontend/src/components/UnitCardGrid.tsx b/frontend/src/components/UnitCardGrid.tsx index 51a6e4d..5e8ff78 100644 --- a/frontend/src/components/UnitCardGrid.tsx +++ b/frontend/src/components/UnitCardGrid.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from "react"; +import { useContext } from "react"; import { useNavigate } from "react-router-dom"; import styled from "styled-components"; @@ -13,63 +13,11 @@ const UnitCardLayout = styled.div` gap: 55px; `; -const PropertiesRow = styled.span` - display: flex; - flex-direction: row; - align-items: center; - color: black; - font-family: "Montserrat"; - font-size: 27px; - font-weight: 700; - margin-bottom: 15px; -`; - const HeaderText = styled.span` font-family: "Neutraface Text"; font-size: 32px; `; -const GridContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-start; - margin: 95px; - margin-top: 50px; - gap: 30px; -`; - -const ButtonsWrapper = styled.div` - display: flex; - flex-direction: row; - position: absolute; - left: 69vw; -`; - -const PendingButton = styled.div<{ selected: boolean }>` - display: flex; - width: 164px; - height: 55px; - align-items: center; - justify-content: center; - border-radius: ${(props) => (props.selected ? "12px" : "12px 0px 0px 12px")}; - border: 1px solid ${(props) => (props.selected ? "rgba(162, 61, 4, 0.80)" : "#EEE")}; - background: ${(props) => (props.selected ? "#B64201" : "#EEE")}; - color: ${(props) => (props.selected ? "#EEE" : "#2E2E2E")}; - font-family: Poppins; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: normal; - z-index: ${(props) => (props.selected ? 1 : 0)}; - cursor: pointer; -`; - -const ListingsButton = styled(PendingButton)` - border-radius: ${(props) => (props.selected ? "12px" : "0px 12px 12px 0px")}; - position: relative; - left: -10px; -`; - const AddListings = styled.div` display: flex; flex-direction: column; @@ -103,56 +51,24 @@ export const UnitCardGrid = ({ showPendingUnits = false, }: UnitCardGridProps) => { const navigate = useNavigate(); - const [pendingSelected, setPendingSelected] = useState(showPendingUnits); const dataContext = useContext(DataContext); return ( <> - - - {pendingSelected ? ( - Pending Approval - ) : ( - Available Properties - )} - {dataContext.currentUser?.isHousingLocator && ( - - { - setPendingSelected(true); - refreshUnits("pending"); - }} - selected={pendingSelected} - > - Pending Listings - - { - setPendingSelected(false); - refreshUnits("approved"); - }} - selected={!pendingSelected} - > - All Listings - - - )} - - - {units.length > 0 && - units.map((option, index) => ( - { - refreshUnits(pendingSelected ? "pending" : "approved"); - }} - key={index} - /> - ))} - {units.length === 0 && No matching units found} - - + + {units.length > 0 && + units.map((option, index) => ( + { + refreshUnits(showPendingUnits ? "pending" : "approved"); + }} + key={index} + /> + ))} + {units.length === 0 && No matching units found} + {dataContext.currentUser?.isHousingLocator && ( { diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx index be6e428..0a3a98f 100644 --- a/frontend/src/components/UnitList.tsx +++ b/frontend/src/components/UnitList.tsx @@ -1,23 +1,13 @@ -import { useContext, useState } from "react"; +import { useState } from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; import { Pagination } from "./Pagination"; import { Unit } from "@/api/units"; -import { DataContext } from "@/contexts/DataContext"; const ENTRIES_PER_PAGE = 6; -const UnitTableWrapper = styled.div` - display: flex; - flex-direction: column; - margin: 95px; - margin-top: 39px; - margin-right: 7.5%; - gap: 30px; -`; - const UnitTable = styled.div` display: flex; flex-direction: column; @@ -92,56 +82,6 @@ const UnitHeaderItemWrapper = styled.div` width: 150px; `; -// problem when shrinking screen -const PropertiesRow = styled.span` - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - color: black; - font-family: "Montserrat"; - font-size: 27px; - font-weight: 700; - margin: 0; - margin-bottom: 15px; -`; - -const ButtonsWrapper = styled.div` - display: flex; - flex-direction: row; - left: 77vw; -`; - -const PendingButton = styled.div<{ selected: boolean }>` - display: flex; - width: 164px; - height: 55px; - align-items: center; - justify-content: center; - border-radius: ${(props) => (props.selected ? "12px" : "12px 0px 0px 12px")}; - border: 1px solid ${(props) => (props.selected ? "rgba(162, 61, 4, 0.80)" : "#EEE")}; - background: ${(props) => (props.selected ? "#B64201" : "#EEE")}; - color: ${(props) => (props.selected ? "#EEE" : "#2E2E2E")}; - font-family: Poppins; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: normal; - z-index: ${(props) => (props.selected ? 1 : 0)}; - cursor: pointer; -`; - -const ListingsButton = styled(PendingButton)` - border-radius: ${(props) => (props.selected ? "12px" : "0px 12px 12px 0px")}; - position: relative; - left: -10px; -`; - -const HeaderText = styled.span` - font-family: "Neutraface Text"; - font-size: 32px; -`; - const UnitListFooterWrapper = styled.div` display: flex; flex-direction: row; @@ -191,99 +131,63 @@ const UnitNotFoundWrapper = styled.div` export type UnitListProps = { units: Unit[]; - showPendingUnits?: boolean; - refreshUnits: (approved: "pending" | "approved") => void; }; -export const UnitList = ({ units, refreshUnits, showPendingUnits = false }: UnitListProps) => { - const [pendingSelected, setPendingSelected] = useState(showPendingUnits); +export const UnitList = ({ units }: UnitListProps) => { const [pageNumber, setPageNumber] = useState(1); - const dataContext = useContext(DataContext); - return ( - - - {pendingSelected ? ( - Pending Approval - ) : ( - Available Properties - )} - {dataContext.currentUser?.isHousingLocator && ( - - { - setPendingSelected(true); - refreshUnits("pending"); - }} - selected={pendingSelected} - > - Pending Listings - - { - setPendingSelected(false); - refreshUnits("approved"); - }} - selected={!pendingSelected} - > - All Listings - - - )} - - - - Listing Address - Price - Beds - Baths - Sqft - Status - - <> - {units.length > 0 && - units - .slice((pageNumber - 1) * ENTRIES_PER_PAGE, pageNumber * ENTRIES_PER_PAGE) - .map((unit: Unit, index) => ( - - - {unit.listingAddress} - ${unit.monthlyRent} - {unit.numBeds} - {unit.numBaths} - {unit.sqft} - - {" "} - {unit.availableNow && unit.approved ? ( - - Available - - ) : !unit.approved ? ( - - Pending - - ) : unit.leasedStatus !== undefined ? ( - Leased - ) : ( - Not Available - )} - - - - ))} - {units.length === 0 && No matching units found} - - - - - - - - + + + Listing Address + Price + Beds + Baths + Sqft + Status + + <> + {units.length > 0 && + units + .slice((pageNumber - 1) * ENTRIES_PER_PAGE, pageNumber * ENTRIES_PER_PAGE) + .map((unit: Unit, index) => ( + + + {unit.listingAddress} + ${unit.monthlyRent} + {unit.numBeds} + {unit.numBaths} + {unit.sqft} + + {" "} + {unit.availableNow && unit.approved ? ( + + Available + + ) : !unit.approved ? ( + + Pending + + ) : unit.leasedStatus !== undefined ? ( + Leased + ) : ( + Not Available + )} + + + + ))} + {units.length === 0 && No matching units found} + + + + + + + ); }; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 3024426..bf1d8ea 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useLocation } from "react-router-dom"; import styled from "styled-components"; @@ -10,15 +10,16 @@ import { NavBar } from "@/components/NavBar"; import { Page } from "@/components/Page"; import { UnitCardGrid } from "@/components/UnitCardGrid"; import { UnitList } from "@/components/UnitList"; +import { DataContext } from "@/contexts/DataContext"; const ButtonsWrapper = styled.div` display: flex; - flex: row; justify-content: end; - margin: 0; - margin-top: 55px; - margin-right: 100px; - margin-left: 310px; +`; + +const HeaderText = styled.span` + font-family: "Neutraface Text"; + font-size: 32px; `; const ToggleButtonWrapper = styled.div` @@ -56,6 +57,7 @@ const ListViewButton = styled(CardViewButton)` const SearchStateWrapper = styled.div` display: flex; flex-direction: row; + justify-content: space-between; `; export type FilterContextType = { @@ -65,6 +67,43 @@ export type FilterContextType = { export const FiltersContext = React.createContext({} as FilterContextType); +const PropertiesRow = styled.span` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + color: black; + font-family: "Montserrat"; + font-size: 27px; + font-weight: 700; + margin-bottom: 30px; +`; + +const PendingButton = styled.div<{ selected: boolean }>` + display: flex; + width: 164px; + height: 55px; + align-items: center; + justify-content: center; + border-radius: ${(props) => (props.selected ? "12px" : "12px 0px 0px 12px")}; + border: 1px solid ${(props) => (props.selected ? "rgba(162, 61, 4, 0.80)" : "#EEE")}; + background: ${(props) => (props.selected ? "#B64201" : "#EEE")}; + color: ${(props) => (props.selected ? "#EEE" : "#2E2E2E")}; + font-family: Poppins; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: normal; + z-index: ${(props) => (props.selected ? 1 : 0)}; + cursor: pointer; +`; + +const ListingsButton = styled(PendingButton)` + border-radius: ${(props) => (props.selected ? "12px" : "0px 12px 12px 0px")}; + position: relative; + left: -10px; +`; + const HomePageLayout = styled.div` display: flex; flex-direction: row; @@ -72,12 +111,14 @@ const HomePageLayout = styled.div` height: 100%; `; -const FilterPadding = styled.div` - min-width: 250px; - height: 100%; +const UnitContent = styled.div` + width: 100%; + max-height: calc(100vh - 70px); + overflow-y: scroll; + padding: 70px 60px; `; - export function Home() { + const dataContext = useContext(DataContext); const previousFilters = useLocation().state as FilterParams; const [units, setUnits] = useState([]); const [filters, setFilters] = useState( @@ -87,6 +128,7 @@ export function Home() { }, ); const [viewMode, setViewMode] = useState("card"); + const [pendingSelected, setPendingSelected] = useState(filters.approved === "pending"); const fetchUnits = (filterParams: FilterParams) => { let query: GetUnitsParams = { @@ -130,6 +172,11 @@ export function Home() { .catch(console.error); }; + const refreshUnits = (approved: "pending" | "approved") => { + const newFilters = { ...filters, approved }; + setFilters(newFilters); + }; + useEffect(() => { fetchUnits(filters); }, [filters]); @@ -151,8 +198,7 @@ export function Home() { - -
+ + + {pendingSelected ? ( + Pending Approval + ) : ( + Available Properties + )} + {dataContext.currentUser?.isHousingLocator && ( + + { + setPendingSelected(true); + refreshUnits("pending"); + }} + selected={pendingSelected} + > + Pending Listings + + { + setPendingSelected(false); + refreshUnits("approved"); + }} + selected={!pendingSelected} + > + All Listings + + + )} + {viewMode === "card" ? ( { - const newFilters = { ...filters, approved }; - fetchUnits(newFilters); - setFilters(newFilters); - }} + refreshUnits={refreshUnits} /> ) : ( - { - const newFilters = { ...filters, approved }; - setFilters(newFilters); - }} - /> + )} -
+
From 6f9e8ad06a075d5b9cb63873930809e69e24bd2f Mon Sep 17 00:00:00 2001 From: Philip Zhang Date: Mon, 3 Jun 2024 21:07:21 -0700 Subject: [PATCH 21/22] fix routing --- frontend/src/components/UnitList.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/UnitList.tsx b/frontend/src/components/UnitList.tsx index 0a3a98f..b3a7e51 100644 --- a/frontend/src/components/UnitList.tsx +++ b/frontend/src/components/UnitList.tsx @@ -1,10 +1,11 @@ -import { useState } from "react"; -import { Link } from "react-router-dom"; +import { useContext, useState } from "react"; +import { Link, useLocation } from "react-router-dom"; import styled from "styled-components"; import { Pagination } from "./Pagination"; import { Unit } from "@/api/units"; +import { FiltersContext } from "@/pages/Home"; const ENTRIES_PER_PAGE = 6; @@ -65,9 +66,7 @@ const ListingAddressWrapper = styled.div` flex-direction: column; font-size: 16px; font-family: "Montserrat"; - line-height: 18.4px; width: 200px; - height: 73px; align-items: center; `; @@ -134,6 +133,8 @@ export type UnitListProps = { }; export const UnitList = ({ units }: UnitListProps) => { + const { pathname } = useLocation(); + const { filters } = useContext(FiltersContext); const [pageNumber, setPageNumber] = useState(1); return ( @@ -151,7 +152,12 @@ export const UnitList = ({ units }: UnitListProps) => { units .slice((pageNumber - 1) * ENTRIES_PER_PAGE, pageNumber * ENTRIES_PER_PAGE) .map((unit: Unit, index) => ( - + {unit.listingAddress} ${unit.monthlyRent} From f6decbc0e921619ff2293f0c8d571e556d6dcd14 Mon Sep 17 00:00:00 2001 From: Philip Zhang Date: Mon, 3 Jun 2024 21:12:14 -0700 Subject: [PATCH 22/22] remove pendingSelected state --- frontend/src/pages/Home.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index bf1d8ea..fcaf3bd 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -128,7 +128,6 @@ export function Home() { }, ); const [viewMode, setViewMode] = useState("card"); - const [pendingSelected, setPendingSelected] = useState(filters.approved === "pending"); const fetchUnits = (filterParams: FilterParams) => { let query: GetUnitsParams = { @@ -228,7 +227,7 @@ export function Home() { - {pendingSelected ? ( + {filters.approved === "pending" ? ( Pending Approval ) : ( Available Properties @@ -237,19 +236,17 @@ export function Home() { { - setPendingSelected(true); refreshUnits("pending"); }} - selected={pendingSelected} + selected={filters.approved === "pending"} > Pending Listings { - setPendingSelected(false); refreshUnits("approved"); }} - selected={!pendingSelected} + selected={filters.approved === "approved"} > All Listings