diff --git a/client/src/components/composite/BookingForm/BookingForm.tsx b/client/src/components/composite/Bookings/BookingForm.tsx similarity index 100% rename from client/src/components/composite/BookingForm/BookingForm.tsx rename to client/src/components/composite/Bookings/BookingForm.tsx diff --git a/client/src/components/composite/ProfileBookingHistory/ProfileBookingHistory.tsx b/client/src/components/composite/Bookings/ProfileBookingHistory/ProfileBookingHistory.tsx similarity index 100% rename from client/src/components/composite/ProfileBookingHistory/ProfileBookingHistory.tsx rename to client/src/components/composite/Bookings/ProfileBookingHistory/ProfileBookingHistory.tsx diff --git a/client/src/components/composite/ProfileCurrentBookings/ProfileCurrentBookings.tsx b/client/src/components/composite/Bookings/ProfileCurrentBookings/ProfileCurrentBookings.tsx similarity index 100% rename from client/src/components/composite/ProfileCurrentBookings/ProfileCurrentBookings.tsx rename to client/src/components/composite/Bookings/ProfileCurrentBookings/ProfileCurrentBookings.tsx diff --git a/client/src/components/composite/Profile/ProfileCalendarCard/ProfileCalendarCard.tsx b/client/src/components/composite/Profile/ProfileCalendarCard/ProfileCalendarCard.tsx new file mode 100644 index 000000000..340161d23 --- /dev/null +++ b/client/src/components/composite/Profile/ProfileCalendarCard/ProfileCalendarCard.tsx @@ -0,0 +1,31 @@ +import { Card, Typography, CardContent, Stack, Box } from "@mui/material" + +function ProfileCalendarCard() { + return ( +
+ + + + + Calendar + + + + + +
+ ) +} + +export default ProfileCalendarCard diff --git a/client/src/components/composite/Profile/UserInformation/UserInformationDisplay.tsx b/client/src/components/composite/Profile/UserInformation/UserInformationDisplay.tsx new file mode 100644 index 000000000..18aba86dc --- /dev/null +++ b/client/src/components/composite/Profile/UserInformation/UserInformationDisplay.tsx @@ -0,0 +1,40 @@ +import Button from "components/generic/FigmaButtons/FigmaButton" +import { UserAdditionalInfo } from "models/User" + +interface IUserInformationDisplay { + userData?: UserAdditionalInfo + editHandler?: () => void +} + +const UserInformationDisplay = ({ + userData, + editHandler +}: IUserInformationDisplay) => { + if (userData) { + const { date_of_birth, ...displayableFields } = userData + return ( + <> + +
+ {/* TODO: Properly style and name fields */} + {Object.keys(displayableFields).map((key) => { + const _key = key as keyof typeof displayableFields + return ( + <> +
+ {_key}: {displayableFields[_key]} +
+ + ) + })} +
{new Date(date_of_birth.seconds * 1000).toDateString()}
+
+ + ) + } + return <>No Data found +} + +export default UserInformationDisplay diff --git a/client/src/components/composite/Profile/UserInformation/UserInformationEdit.tsx b/client/src/components/composite/Profile/UserInformation/UserInformationEdit.tsx new file mode 100644 index 000000000..1e7f0ac9d --- /dev/null +++ b/client/src/components/composite/Profile/UserInformation/UserInformationEdit.tsx @@ -0,0 +1,40 @@ +import Button from "components/generic/FigmaButtons/FigmaButton" +import TextInput from "components/generic/TextInputComponent/TextInput" +import { UserAdditionalInfo } from "models/User" + +interface IUserInformationEdit { + userData?: UserAdditionalInfo + saveHandler?: () => void +} + +const UserInformationEdit = ({ + userData, + saveHandler +}: IUserInformationEdit) => { + if (userData) { + const { date_of_birth, ...displayableFields } = userData + return ( + <> + +
+ {/* TODO: Properly style and name fields */} + {Object.keys(displayableFields).map((key) => { + const _key = key as keyof typeof displayableFields + return ( + <> +
{_key}
+ + + ) + })} +
{new Date(date_of_birth.seconds * 1000).toDateString()}
+
+ + ) + } + return <>No Data found +} + +export default UserInformationEdit diff --git a/client/src/components/composite/Profile/UserInformation/UserInformationSection.tsx b/client/src/components/composite/Profile/UserInformation/UserInformationSection.tsx new file mode 100644 index 000000000..68dcbee7a --- /dev/null +++ b/client/src/components/composite/Profile/UserInformation/UserInformationSection.tsx @@ -0,0 +1,32 @@ +import { useState } from "react" +import { useSearchParams } from "react-router-dom" +import UserInformationEdit from "./UserInformationEdit" +import UserInformationDisplay from "./UserInformationDisplay" +import { UserAdditionalInfo } from "models/User" + +interface IUserInformationSection { + userData?: UserAdditionalInfo +} + +const UserInformationSection = ({ userData }: IUserInformationSection) => { + const [searchParams] = useSearchParams() + const [isEdit, setIsEdit] = useState(!!searchParams.get("edit")) + + return ( + <> + {isEdit ? ( + setIsEdit(false)} + userData={userData} + /> + ) : ( + setIsEdit(true)} + userData={userData} + /> + )} + + ) +} + +export default UserInformationSection diff --git a/client/src/components/composite/ProfileCalendarCard/ProfileCalendarCard.tsx b/client/src/components/composite/ProfileCalendarCard/ProfileCalendarCard.tsx deleted file mode 100644 index 00a29ae6b..000000000 --- a/client/src/components/composite/ProfileCalendarCard/ProfileCalendarCard.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { Card, Typography, CardContent, Stack, Box } from "@mui/material" -import React from "react" -import { DateCalendar, PickersDay } from "@mui/x-date-pickers" -import { styled } from "@mui/material/styles" - -const StyledCalendarDay = styled(PickersDay, { - shouldForwardProp: (prop) => prop !== "isBookedDate" -})( - /** - * @param {{isBookedDate: boolean}} - */ - ({ theme, isBookedDate }: any) => ({ - ...(isBookedDate && { - backgroundColor: theme.palette.primary[theme.palette.mode], - "&:hover, &:focus": { - backgroundColor: theme.palette.primary[theme.palette.mode] - }, - borderTopLeftRadius: "50%", - borderBottomLeftRadius: "50%", - borderTopRightRadius: "50%", - borderBottomRightRadius: "50%" - }) - }) -) - -function CalendarDay(props: any) { - const { bookings, ...others } = props - - const isBookedDate = bookings.some((booking: any) => { - const start = booking.data().check_in.toDate() - const end = booking.data().check_out.toDate() - - const calendarDate = others.day.toDate() - - return start <= calendarDate && end >= calendarDate - }) - - return -} - -/** - * @param {{bookings: Array}} - */ -function ProfileCalendarCard({ bookings }: any) { - // construct all the dates - - return ( -
- - - - - Calendar - - - ({ - bookings - }) - }} - /> - - - - -
- ) -} - -export default ProfileCalendarCard diff --git a/client/src/components/composite/ProfileCard/ProfileCard.tsx b/client/src/components/composite/ProfileCard/ProfileCard.tsx deleted file mode 100644 index a487d63b9..000000000 --- a/client/src/components/composite/ProfileCard/ProfileCard.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { - Card, - Typography, - Avatar, - CardContent, - Stack, - Button -} from "@mui/material" -import React, { useEffect, useState } from "react" - -import { useAuthenticatedUser } from "hooks/useAuthenticatedUser" - -const textType = "body1" - -function ProfileCard() { - const [user, userMetadata] = useAuthenticatedUser() - const [userData, setUserData] = useState(undefined) - const [expanded, setExpanded] = useState(false) - - // Expanding the more details on the user page - const expandDetails = () => { - setExpanded(!expanded) - } - - useEffect(() => { - if (userMetadata) { - // @ts-ignore - setUserData(userMetadata) - } - }, [user, userMetadata]) - - return ( -
- - - - - - - - {userData - ? userData.firstName + " " + userData.lastName - : "John Doe"} - - - - - {/* @ts-ignore */} - - - - Phone - - - {userData ? userData.phoneNumber : "N/A"} - - - - - Email - - - {userData ? userData.email : "N/A"} - - - - - Membership - - - {userMetadata - ? // @ts-ignore - userMetadata.membership === undefined - ? "Member" - : // @ts-ignore - userMetadata.membership - : "N/A"} - - - - - {expanded ? ( - // @ts-ignore - - - - Emergency Contact Name - - - Jane Doe - - - - - Emergency Contact Number - - - +64 2400 420 422 - - - - - Emergency Contact Relation - - - Mother - - - - ) : ( -
- )} - - {expanded ? "Hide Details" : "More Details"} - -
-
-
-
-
- ) -} - -export default ProfileCard diff --git a/client/src/components/generic/ProfileInformationPanel/ProfileInformationPanel.tsx b/client/src/components/generic/ProfileInformationPanel/ProfileInformationPanel.tsx index 61fc7d4b2..717813fac 100644 --- a/client/src/components/generic/ProfileInformationPanel/ProfileInformationPanel.tsx +++ b/client/src/components/generic/ProfileInformationPanel/ProfileInformationPanel.tsx @@ -12,7 +12,7 @@ const ProfileInformationPanel = ({ onEdit }: IProfileInformationPanel) => { return ( -
+

{title}

{onEdit && ( diff --git a/client/src/components/generic/ResponsiveBackgroundImage/ResponsiveBackground.story.tsx b/client/src/components/generic/ResponsiveBackgroundImage/ResponsiveBackground.story.tsx new file mode 100644 index 000000000..b4c25d428 --- /dev/null +++ b/client/src/components/generic/ResponsiveBackgroundImage/ResponsiveBackground.story.tsx @@ -0,0 +1,14 @@ +import type { Meta, StoryObj } from "@storybook/react" + +import ResponsiveBackgroundImage from "./ResponsiveBackground" + +const meta: Meta = { + component: ResponsiveBackgroundImage +} + +export default meta +type Story = StoryObj + +export const DefaultExample: Story = { + args: {} +} diff --git a/client/src/components/generic/ResponsiveBackgroundImage/ResponsiveBackground.tsx b/client/src/components/generic/ResponsiveBackgroundImage/ResponsiveBackground.tsx new file mode 100644 index 000000000..21d868ae8 --- /dev/null +++ b/client/src/components/generic/ResponsiveBackgroundImage/ResponsiveBackground.tsx @@ -0,0 +1,19 @@ +interface IResponsiveBackgroundImage { + children: React.ReactNode +} +/** + * to find/change the bg you need to check the + * `backgroundImage` property in `tailwind.config.ts` + */ +const ResponsiveBackgroundImage = ({ + children +}: IResponsiveBackgroundImage) => ( +
+
+
+ {children} +
+
+) + +export default ResponsiveBackgroundImage diff --git a/client/src/pages/Booking.tsx b/client/src/pages/Booking.tsx index 3cf96f488..e23596fe0 100644 --- a/client/src/pages/Booking.tsx +++ b/client/src/pages/Booking.tsx @@ -1,12 +1,28 @@ -import { Route, Routes } from "react-router-dom" -import BookingSuccess from "pages/BookingSuccess" +import BookingForm from "components/composite/Bookings/BookingForm" +import { Typography, Stack } from "@mui/material" const Booking = () => { return ( -
- - } /> - +
+ + + Bookings + + +
) } diff --git a/client/src/pages/Profile.tsx b/client/src/pages/Profile.tsx deleted file mode 100644 index 919e1ed84..000000000 --- a/client/src/pages/Profile.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { db } from "../firebase" -import React, { useEffect, useState } from "react" -import { - getDocs, - where, - query, - collection, - doc, - QueryDocumentSnapshot, - DocumentData -} from "firebase/firestore" -import { Stack, Typography } from "@mui/material" -import ProfileCard from "components/composite/ProfileCard/ProfileCard" -import ProfileCalendarCard from "components/composite/ProfileCalendarCard/ProfileCalendarCard" -import ProfileCurrentBookings from "components/composite/ProfileCurrentBookings/ProfileCurrentBookings" -import ProfileBookingHistory from "components/composite/ProfileBookingHistory/ProfileBookingHistory" -import { useAuthenticatedUser } from "../hooks/useAuthenticatedUser" - -/** - * Reference to a booking from the Firestore. - * @typedef {{ data(): { user_id: string, check_in: require("firebase/firestore").Timestamp, check_out: require("firebase/firestore").Timestamp } }} Booking - */ - -export default function Profile() { - const [user, userMetadata] = useAuthenticatedUser() - /** - * @type {[Array, React.Dispatch>]} - */ - const [bookings, setBookings] = useState< - QueryDocumentSnapshot[] | undefined - >(undefined) - - /** - * Retrieves the current user bookings from firestore. - * @param {string} userId - */ - const getBookings = (userId: any) => { - getDocs( - query( - collection(db, "bookings"), - where("user_id", "==", doc(db, "users", userId)) - ) - ) - .then((storeBookings) => { - setBookings(storeBookings.docs) - }) - .catch((err) => - console.error( - `Failed to retrieve bookings for authenticated user: ${err}` - ) - ) - } - - useEffect(() => { - if (user) { - getBookings(user.uid) - } - }, [user, userMetadata]) - - return ( -
- - - Profile - - - - - - My Bookings - - - - - - - -
- ) -} diff --git a/client/src/pages/Profile/Profile.story.tsx b/client/src/pages/Profile/Profile.story.tsx new file mode 100644 index 000000000..c527de032 --- /dev/null +++ b/client/src/pages/Profile/Profile.story.tsx @@ -0,0 +1,21 @@ +import type { Meta, StoryObj } from "@storybook/react" + +import Profile from "./Profile" +import { MemoryRouter } from "react-router-dom" + +const meta: Meta = { + component: Profile +} + +export default meta +type Story = StoryObj + +export const DefaultProfilePage: Story = { + decorators: [ + (Story) => ( + + + + ) + ] +} diff --git a/client/src/pages/Profile/Profile.tsx b/client/src/pages/Profile/Profile.tsx new file mode 100644 index 000000000..826d45ab8 --- /dev/null +++ b/client/src/pages/Profile/Profile.tsx @@ -0,0 +1,145 @@ +import { useAppData } from "store/Store" +import { useNavigate } from "react-router-dom" + +import ProfileInformationPanel from "components/generic/ProfileInformationPanel/ProfileInformationPanel" +import { Footer } from "components/generic/Footer/Footer" +import ResponsiveBackgroundImage from "components/generic/ResponsiveBackgroundImage/ResponsiveBackground" + +const SignOutButton = () => { + const navigate = useNavigate() + const handleOnclick = () => { + navigate("/login") + } + return ( +
+ +
+ ) +} + +const Field = ({ + subtitle, + description +}: { + subtitle: string + description?: string +}) => { + return ( + <> +
+

+ {subtitle} +

+

+ {description} +

+
+ + ) +} +export default function Profile() { + const [{ currentUserData }] = useAppData() + const [{ currentUser }] = useAppData() + + function toDateTime(secs?: number) { + const t = new Date() // Epoch + t.setSeconds(secs!) + const f = t.toDateString() + return f + } + + return ( +
+ +
+
+
+

{`${currentUserData?.first_name} ${currentUserData?.last_name}`}

+
+ +
+
+ +
+ {}} + > +
+
+ + + + +
+
+ + + +
+
+
+
+ {}}> + + + + {}} + > + + + + +
+ Calender component waiting to be implemented +
+
+
+
+
+
+
+
+
+
+ ) +} diff --git a/client/src/routes/routes.tsx b/client/src/routes/routes.tsx index 82d154728..b1b3f2317 100644 --- a/client/src/routes/routes.tsx +++ b/client/src/routes/routes.tsx @@ -7,7 +7,7 @@ import Contact from "pages/Contact" import Events from "pages/Events" import Home from "pages/Home/Home" import Login from "pages/Login/Login" -import Profile from "pages/Profile" +import Profile from "pages/Profile/Profile" import Register from "pages/Register/Register" import Thanks from "pages/Thanks" import { Route, Routes } from "react-router-dom"