From 7603b7d771041f06bb517880bf8dc5901134c592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20B=C3=B6ckmann?= Date: Thu, 24 Oct 2024 16:33:07 +0300 Subject: [PATCH] feat: added data structure for company dashboard --- src/app/company/dashboard/bookings/page.tsx | 99 +++++++++++++++++ src/app/company/dashboard/layout.tsx | 116 ++++++++++++++++++++ src/app/company/dashboard/members/page.tsx | 99 +++++++++++++++++ src/app/company/dashboard/users/page.tsx | 99 +++++++++++++++++ src/components/dashboard/CompanyContext.tsx | 62 +++++++++++ src/types/index.d.ts | 6 +- 6 files changed, 479 insertions(+), 2 deletions(-) create mode 100644 src/app/company/dashboard/bookings/page.tsx create mode 100644 src/app/company/dashboard/layout.tsx create mode 100644 src/app/company/dashboard/members/page.tsx create mode 100644 src/app/company/dashboard/users/page.tsx create mode 100644 src/components/dashboard/CompanyContext.tsx diff --git a/src/app/company/dashboard/bookings/page.tsx b/src/app/company/dashboard/bookings/page.tsx new file mode 100644 index 0000000..a3363c5 --- /dev/null +++ b/src/app/company/dashboard/bookings/page.tsx @@ -0,0 +1,99 @@ +"use client"; +import { Input } from "@/components/ui/input"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { extractNameInitials } from "@/lib/utils"; +import React from "react"; +import { useRouter } from "next/navigation"; +import { deleteToken } from "@/lib/authActions"; +import Loader from "@/components/layout/Loader"; +import { useCompany } from "@/components/dashboard/CompanyContext"; + +export default function Page() { + const { user, loading, companyLoading, company } = useCompany(); + const router = useRouter(); + + const logout = async () => { + try { + await deleteToken(); + window.location.reload(); + } catch (logoutError) { + console.error("Logout failed", logoutError); + throw logoutError; + } + }; + + if (loading || companyLoading) return ; + + return ( +
+
+

+ {company?.getCompany.name} (ID: {company?.getCompany.id}) +

+
+ + + + + + + +
+

{user?.name}

+

{user?.email}

+
+
+ + { + router.push("/dashboard/settings"); + }}> + Settings + + + + Log out + +
+
+
+
+ +
+
+
+
+ {extractNameInitials(company?.getCompany.name)} +
+
+

{company?.getCompany.name}

+
+
+
+

+ {company?.getCompany.description !== "" ? ( + company?.getCompany.description + ) : ( + This company has not provided a description + )} +

+
+
+ ); +} diff --git a/src/app/company/dashboard/layout.tsx b/src/app/company/dashboard/layout.tsx new file mode 100644 index 0000000..a2f4a51 --- /dev/null +++ b/src/app/company/dashboard/layout.tsx @@ -0,0 +1,116 @@ +"use client"; +import Image from "next/image"; +import { Button } from "@/components/ui/button"; +import { LuBookCopy, LuLayoutDashboard } from "react-icons/lu"; +import React, { Suspense, useEffect, useState } from "react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { cn } from "@/lib/utils"; +import Loader from "@/components/layout/Loader"; +import { CompanyProvider, useCompany } from "@/components/dashboard/CompanyContext"; +import { BriefcaseBusiness, Users } from "lucide-react"; + +function DashboardContent({ children }: { children: React.ReactNode }) { + const { companyLoading, loading } = useCompany(); + const [active, setActive] = useState<"dashboard" | "bookings" | "users" | "members">("dashboard"); + const pathname = usePathname(); + + useEffect(() => { + // Derive the active state based on the current pathname + if (pathname.includes("/company/dashboard/bookings")) { + setActive("bookings"); + } else if (pathname.includes("/company/dashboard/users")) { + setActive("users"); + } else if (pathname.includes("/company/dashboard/members")) { + setActive("members"); + } else { + setActive("dashboard"); + } + }, [pathname]); + + return ( +
+ +
+ {loading || companyLoading ? : }>{children}} +
+

MeetMate

+
+
+
+ ); +} + +// Wrap the dashboard with the UserProvider +export default function DashboardLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/src/app/company/dashboard/members/page.tsx b/src/app/company/dashboard/members/page.tsx new file mode 100644 index 0000000..a3363c5 --- /dev/null +++ b/src/app/company/dashboard/members/page.tsx @@ -0,0 +1,99 @@ +"use client"; +import { Input } from "@/components/ui/input"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { extractNameInitials } from "@/lib/utils"; +import React from "react"; +import { useRouter } from "next/navigation"; +import { deleteToken } from "@/lib/authActions"; +import Loader from "@/components/layout/Loader"; +import { useCompany } from "@/components/dashboard/CompanyContext"; + +export default function Page() { + const { user, loading, companyLoading, company } = useCompany(); + const router = useRouter(); + + const logout = async () => { + try { + await deleteToken(); + window.location.reload(); + } catch (logoutError) { + console.error("Logout failed", logoutError); + throw logoutError; + } + }; + + if (loading || companyLoading) return ; + + return ( +
+
+

+ {company?.getCompany.name} (ID: {company?.getCompany.id}) +

+
+ + + + + + + +
+

{user?.name}

+

{user?.email}

+
+
+ + { + router.push("/dashboard/settings"); + }}> + Settings + + + + Log out + +
+
+
+
+ +
+
+
+
+ {extractNameInitials(company?.getCompany.name)} +
+
+

{company?.getCompany.name}

+
+
+
+

+ {company?.getCompany.description !== "" ? ( + company?.getCompany.description + ) : ( + This company has not provided a description + )} +

+
+
+ ); +} diff --git a/src/app/company/dashboard/users/page.tsx b/src/app/company/dashboard/users/page.tsx new file mode 100644 index 0000000..a3363c5 --- /dev/null +++ b/src/app/company/dashboard/users/page.tsx @@ -0,0 +1,99 @@ +"use client"; +import { Input } from "@/components/ui/input"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { extractNameInitials } from "@/lib/utils"; +import React from "react"; +import { useRouter } from "next/navigation"; +import { deleteToken } from "@/lib/authActions"; +import Loader from "@/components/layout/Loader"; +import { useCompany } from "@/components/dashboard/CompanyContext"; + +export default function Page() { + const { user, loading, companyLoading, company } = useCompany(); + const router = useRouter(); + + const logout = async () => { + try { + await deleteToken(); + window.location.reload(); + } catch (logoutError) { + console.error("Logout failed", logoutError); + throw logoutError; + } + }; + + if (loading || companyLoading) return ; + + return ( +
+
+

+ {company?.getCompany.name} (ID: {company?.getCompany.id}) +

+
+ + + + + + + +
+

{user?.name}

+

{user?.email}

+
+
+ + { + router.push("/dashboard/settings"); + }}> + Settings + + + + Log out + +
+
+
+
+ +
+
+
+
+ {extractNameInitials(company?.getCompany.name)} +
+
+

{company?.getCompany.name}

+
+
+
+

+ {company?.getCompany.description !== "" ? ( + company?.getCompany.description + ) : ( + This company has not provided a description + )} +

+
+
+ ); +} diff --git a/src/components/dashboard/CompanyContext.tsx b/src/components/dashboard/CompanyContext.tsx new file mode 100644 index 0000000..c8f463f --- /dev/null +++ b/src/components/dashboard/CompanyContext.tsx @@ -0,0 +1,62 @@ +import React, { createContext, useContext, useEffect, useState } from "react"; +import { type Company, type CompanyUser } from "@/types"; +import { getAccessToken, getUser } from "@/lib/authActions"; +import { type ApolloQueryResult, useQuery } from "@apollo/client"; +import { getCompany } from "@/lib/graphql/queries"; + +type CompanyContextType = { + user: CompanyUser | undefined; + loading: boolean; + refreshUser: () => Promise; + + company: { getCompany: Company } | undefined; + companyLoading: boolean; + refreshCompany: () => Promise>; +}; + +const CompanyContext = createContext(undefined); + +export function CompanyProvider({ children }: { children: React.ReactNode }) { + const [user, setUser] = useState(); + const { + loading: companyLoading, + data: company, + refetch: refreshCompany + } = useQuery(getCompany, { variables: { id: user?.associatedCompany }, pollInterval: 300000 }); + const [loading, setLoading] = useState(true); + + const fetchUser = async () => { + setLoading(true); + try { + const accessToken = await getAccessToken(); + const userData = await getUser(accessToken); + setUser(userData as CompanyUser); + } catch (error) { + console.error("Failed to fetch user", error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + void fetchUser(); + }, []); + + const refreshUser = async () => { + await fetchUser(); + }; + + return ( + + {children} + + ); +} + +export function useCompany() { + const context = useContext(CompanyContext); + if (context === undefined) { + throw new Error("useCompany must be used within a CompanyProvider"); + } + return context; +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts index e229db1..1fec22c 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -53,18 +53,20 @@ type ClientUser = BaseUser & { type CompanyMemberUser = BaseUser & { role: Role.COMPANY_MEMBER; - companyID: string; + associatedCompany: string; }; type CompanyAdminUser = BaseUser & { role: Role.COMPANY_ADMIN; - companyID: string; + associatedCompany: string; }; type AdminUser = BaseUser & { role: Role.ADMIN; }; +type CompanyUser = BaseUser & (CompanyMemberUser | CompanyAdminUser); + // Union type for all possible user types export type User = ClientUser | CompanyMemberUser | CompanyAdminUser | AdminUser;