Skip to content

Commit

Permalink
feat: company browse page added; waiting for backend to add companies…
Browse files Browse the repository at this point in the history
… array in user object
  • Loading branch information
bencodes07 committed Oct 13, 2024
1 parent e2e3665 commit 6544905
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 86 deletions.
27 changes: 2 additions & 25 deletions src/app/dashboard/bookings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { Progress } from "@/components/ui/progress";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Calendar } from "@/components/ui/calendar";
import { useDashboardData } from "@/components/dashboard/DashboardContext";
import { type Appointment, type Company } from "@/types";
import { type Appointment } from "@/types";
import { useSelector } from "react-redux";
import { type RootState } from "@/store/store";

Expand All @@ -49,30 +49,7 @@ function Bookings() {
const [searchQuery, setSearchQuery] = useState("");
const appointments = useSelector((state: RootState) => state.collection.appointments);

const [companies, setCompanies] = useState<Company[]>([
{
id: "1",
name: "Github",
createdAt: "2024-01-01",
description: "Version control platform",
owner: user!,
members: [],
settings: {
appointmentDuration: 60,
appointmentBuffer: 15,
appointmentTypes: ["Code Review", "Project Planning"],
appointmentLocations: ["Online", "Office"],
openingHours: {
from: "09:00",
to: "17:00"
}
}
}
]);

useEffect(() => {
if (false) setCompanies([]);
}, []);
const companies = useSelector((state: RootState) => state.collection.companies);

const [filteredAppointments, setFilteredAppointments] = useState<Appointment[]>(appointments);
const router = useRouter();
Expand Down
92 changes: 92 additions & 0 deletions src/app/dashboard/browse/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"use client";
import React from "react";
import { useDashboardData } from "@/components/dashboard/DashboardContext";
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 { useRouter } from "next/navigation";
import { deleteToken } from "@/lib/authActions";
import { useSelector } from "react-redux";
import type { RootState } from "@/store/store";

function CompanyBrowse() {
const { user } = useDashboardData();
const router = useRouter();

const { companies } = useSelector((state: RootState) => state.collection);

const logout = async () => {
try {
await deleteToken();
window.location.reload();
} catch (error) {
console.error("Logout failed", error);
throw error;
}
};

return (
<div className="flex h-[calc(100%-32px)] flex-col items-start justify-start p-8 px-6">
<header className="flex w-full flex-row items-center justify-between">
<h1 className="m-4 font-medium text-foreground md:text-2xl">Browse Companies</h1>
<div className="flex items-center gap-x-6">
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild className={"mr-4"}>
<Button variant="ghost" className="relative size-8 rounded-full">
<Avatar className="size-10">
<AvatarFallback className={"bg-primary"}>{extractNameInitials(user?.name)}</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align={"end"} className={"w-56 border-border"}>
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">{user?.name}</p>
<p className="text-xs leading-none text-muted-foreground">{user?.email}</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => {
router.push("/dashboard/settings");
}}>
Settings
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={logout} className={"text-red-500"}>
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>

<div
className={
"mt-8 grid w-full gap-6 max-lg:grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-3 min-[1900px]:grid-cols-4"
}>
{companies.map((company) => (
<div
key={company.id}
className={
"flex h-[200px] w-full flex-col items-start justify-start gap-y-2 rounded-lg border border-border p-6"
}>
<h2 className={"text-2xl font-medium text-foreground"}>{company.name}</h2>
<p className={"text-base font-normal text-muted-foreground"}>{company.description}</p>
{/* <Button>{user.companies.includes(company.id) ? "Following" : "Follow"}</Button> */}
</div>
))}
</div>
</div>
);
}

export default CompanyBrowse;
71 changes: 44 additions & 27 deletions src/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import Image from "next/image";
import { Button } from "@/components/ui/button";
import { LuBookCopy, LuBuilding, LuGauge, LuLayoutDashboard, LuSettings, LuUser2, LuHome } from "react-icons/lu";
import { LuBookCopy, LuBuilding, LuGauge, LuHome, LuLayoutDashboard, LuSettings, LuUser2 } from "react-icons/lu";
import React, { Suspense, useEffect, useState } from "react";
import type { User } from "@/types";
import { getAccessToken, getUser } from "@/lib/authActions";
Expand All @@ -10,27 +10,27 @@ import { usePathname } from "next/navigation";
import { cn } from "@/lib/utils";
import Loader from "@/components/layout/Loader";
import { DashboardProvider } from "@/components/dashboard/DashboardContext";
import { FaPlus } from "react-icons/fa6";
import { Role } from "@/types/role";
import { useSelector } from "react-redux";
import type { RootState } from "@/store/store";

export default function DashboardLayout({ children }: { children: React.ReactNode }) {
const [loading, setLoading] = useState(true);
const [user, setUser] = useState<User | null>();
const [isAdmin, setIsAdmin] = useState(user?.role === "ADMIN");
const [isAdmin, setIsAdmin] = useState(user?.role === Role.ADMIN);
const [active, setActive] = useState<"dashboard" | "bookings" | "settings">("dashboard");
const [companyIndicatorTop, setCompanyIndicatorTop] = useState(0);

const companies = [
{ id: "29103", name: "Company 1" },
{ id: "61241", name: "Company 2" },
{ id: "1241", name: "Company 3" }
];
const companies = useSelector((state: RootState) => state.collection.companies);

useEffect(() => {
const fetchUser = async () => {
setLoading(true);
try {
const accessToken = await getAccessToken();
setUser(await getUser(accessToken));
setIsAdmin(user?.role === "ADMIN");
setIsAdmin(user?.role === Role.ADMIN);
} catch (error) {
setIsAdmin(false);
console.error("Failed to fetch user", error);
Expand All @@ -53,9 +53,13 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
setActive("dashboard");
}

// Derive the top position of the company indicator
const companyIndex = companies.findIndex((company) => pathname.includes(company.id));
setCompanyIndicatorTop(companyIndex === -1 ? 40 : 144 + 72 * companyIndex);
if (pathname.includes("/dashboard/browse")) {
setCompanyIndicatorTop(companies.length * 72 + 144);
} else {
// Derive the top position of the company indicator
const companyIndex = companies.findIndex((company) => pathname.includes(company.id));
setCompanyIndicatorTop(companyIndex === -1 ? 40 : 144 + 72 * companyIndex);
}
}, [pathname]);

return (
Expand All @@ -76,9 +80,19 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
<div className="mt-14 flex flex-col gap-y-6">
{companies.map((company) => (
<Link key={company.id} className={"size-12"} href={`/dashboard/company/${company.id}`}>
<div key={company.id} className="size-12 rounded-md bg-secondary"></div>
<div
title={company.name}
key={company.id}
className="flex size-12 items-center justify-center rounded-lg bg-secondary">
{company.name[0]}
</div>
</Link>
))}
<Link href={"/dashboard/browse"} className={"size-12"}>
<div className={`flex size-12 items-center justify-center rounded-lg border-2 border-secondary`}>
<FaPlus />
</div>
</Link>
</div>
</div>
<div className="flex h-full w-[230px] flex-col items-center justify-start rounded-[20px] border-2 border-primary">
Expand All @@ -92,27 +106,30 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
active === "dashboard" ? "text-foreground" : "text-muted-foreground"
)}
variant={active === "dashboard" ? "default" : "ghost"}>
{!pathname.includes("/dashboard/company") ? (
{!pathname.includes("/dashboard/company") && !pathname.includes("/dashboard/browse") ? (
<LuLayoutDashboard className="mx-2" size={18} />
) : (
<LuHome className={"mx-2"} />
)}
{pathname.includes("/dashboard/company") ? "Home" : "Dashboard"}
{pathname.includes("/dashboard/company") || pathname.includes("/dashboard/browse")
? "Home"
: "Dashboard"}
</Button>
</Link>
{!companies.some((company) => pathname.includes(company.id)) && (
<Link href={"/dashboard/bookings"}>
<Button
className={cn(
"w-[168px] justify-start",
active === "bookings" ? "text-foreground" : "text-muted-foreground"
)}
variant={active === "bookings" ? "default" : "ghost"}>
<LuBookCopy className="mx-2" size={18} />
Bookings
</Button>
</Link>
)}
{!companies.some((company) => pathname.includes(company.id)) &&
!pathname.includes("/dashboard/browse") && (
<Link href={"/dashboard/bookings"}>
<Button
className={cn(
"w-[168px] justify-start",
active === "bookings" ? "text-foreground" : "text-muted-foreground"
)}
variant={active === "bookings" ? "default" : "ghost"}>
<LuBookCopy className="mx-2" size={18} />
Bookings
</Button>
</Link>
)}
<Link href={"/dashboard/settings"}>
<Button
className={cn(
Expand Down
2 changes: 1 addition & 1 deletion src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function Dashboard() {
<div className="mt-8 flex h-[200px] w-full flex-col items-start justify-center gap-2 rounded-[20px] bg-primary pl-12">
<h2 className="text-3xl font-semibold">MeetMate Dashboard</h2>
<p className="text-sm">Create your appointments in minutes</p>
<Link href={"dashboard/bookings"}>
<Link href={"/dashboard/bookings"}>
<Button className="mt-2" variant={"secondary"}>
Book now
</Button>
Expand Down
4 changes: 2 additions & 2 deletions src/components/dashboard/DashboardContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import React, { createContext, useContext } from "react";
import type { User } from "@/types";

type DashboardContextProps = {
user: User | null;
user: User;
};

const DashboardContext = createContext<DashboardContextProps | undefined>(undefined);

export const DashboardProvider: React.FC<{ user: User | null; children: React.ReactNode }> = ({ user, children }) => {
export const DashboardProvider: React.FC<{ user: User; children: React.ReactNode }> = ({ user, children }) => {
return <DashboardContext.Provider value={{ user }}>{children}</DashboardContext.Provider>;
};

Expand Down
79 changes: 77 additions & 2 deletions src/store/features/collectionSlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type Appointment, type Company } from "@/types";
import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
import { type Appointment, type Company } from "@/types";
import { Role } from "@/types/role";

type CollectionState = {
appointments: Appointment[];
Expand Down Expand Up @@ -42,7 +43,81 @@ const initialState: CollectionState = {
status: "BOOKED"
}
],
companies: []
companies: [
{
id: "1",
name: "GitHub",
createdAt: new Date(2024, 8, 23).toDateString(),
description: "GitHub is a web-based platform for version control and collaboration.",
owner: {
id: 124,
name: "John Doe",
email: "[email protected]",
role: Role.COMPANY_ADMIN,
companyID: "1"
},
members: [],
settings: {
appointmentDuration: 60,
appointmentBuffer: 15,
appointmentTypes: [],
appointmentLocations: [],
openingHours: {
from: "08:00",
to: "18:00"
}
}
},
{
id: "2",
name: "Vercel",
createdAt: new Date(2024, 8, 23).toDateString(),
description: "Vercel is a cloud platform for static sites and Serverless Functions.",
owner: {
id: 123,
name: "John Doe",
email: "[email protected]",
role: Role.COMPANY_ADMIN,
companyID: "2"
},
members: [],
settings: {
appointmentDuration: 60,
appointmentBuffer: 15,
appointmentTypes: [],
appointmentLocations: [],
openingHours: {
from: "08:00",
to: "18:00"
}
}
},
{
id: "3",
name: "Google",
createdAt: new Date(2024, 8, 23).toDateString(),
description:
"Google is an American multinational technology company that specializes in Internet-related services and products.",
owner: {
id: 125,
name: "John Doe",
email: "[email protected]",
role: Role.COMPANY_ADMIN,
companyID: "3"
},
members: [],
settings: {
appointmentDuration: 60,
appointmentBuffer: 15,
appointmentTypes: [],
appointmentLocations: [],
openingHours: {
from: "08:00",
to: "18:00"
}
}
}
]
};

export const collectionSlice = createSlice({
Expand Down
Loading

0 comments on commit 6544905

Please sign in to comment.