Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
19df7a4
Added basic embedded map in newly created components folder under Emb…
MasonMines2006 Oct 21, 2025
86fb542
Implemented basic embed map
MasonMines2006 Oct 21, 2025
d35c431
created react map
MasonMines2006 Oct 28, 2025
c7d3a4e
Created changed to
MasonMines2006 Oct 28, 2025
d11eb73
Merge branch 'main' into embedded-map/#23
MasonMines2006 Oct 28, 2025
5e1be29
Fixed Embedded Map to include the react integration library ( vis.gl/…
MasonMines2006 Oct 28, 2025
ba97c83
Merge branch 'main' into embedded-map/#23
naasanov Nov 3, 2025
df68222
Created Party Demo test page
MasonMines2006 Nov 3, 2025
119936a
Merge remote-tracking branch 'origin/main' into embedded-map/#23
MasonMines2006 Nov 3, 2025
9128996
First mockup of party list props, and page created to be displayed in…
MasonMines2006 Nov 4, 2025
49a441b
Added popup text box and color to map
MasonMines2006 Nov 4, 2025
e527ab9
Removed package and package lock in root directory, updated .env.temp…
MasonMines2006 Nov 4, 2025
e09d155
Created functioning dropdown for party list
MasonMines2006 Nov 4, 2025
adcf2e0
Modified styling to be accurate with the Lo-Fi wireframe
MasonMines2006 Nov 11, 2025
2b5c3af
Changed API keys and added basic sidebar functionality
MasonMines2006 Nov 11, 2025
b9f17ce
Working global sidebar
MasonMines2006 Nov 11, 2025
f7c714f
Created generic implementation of info chips
MasonMines2006 Nov 11, 2025
0a33dbd
Finished implementation of generic info chip details, added specific …
MasonMines2006 Nov 11, 2025
0f40423
Merge branch 'main' into mason-info-chip
MasonMines2006 Nov 11, 2025
6e95b02
Merge branch 'main' of https://github.com/cssgunc/party-registration …
naasanov Nov 14, 2025
8dfdd64
Minor changes to PartyList before merge with main
MasonMines2006 Nov 18, 2025
acdee3e
Merging with Main
MasonMines2006 Nov 18, 2025
af89dcf
fixed type erros
MasonMines2006 Nov 18, 2025
90a68ca
Resolved corrections suggested in PR review
MasonMines2006 Nov 18, 2025
a3bb5f3
Merge remote-tracking branch 'origin/mason-info-chip' into mason-info…
MasonMines2006 Nov 18, 2025
03693b0
Changes to sidebar functionality and chip information
MasonMines2006 Nov 20, 2025
6ff4e3d
Reverted to old non-shadCN sidebar implementation
MasonMines2006 Nov 20, 2025
1a490b4
Merge branch 'main' of https://github.com/cssgunc/party-registration …
naasanov Nov 20, 2025
e1319e9
chore: update package.json
naasanov Nov 20, 2025
3f6ec84
chore: reverted whitespace changes to prevent merge conflicts
naasanov Nov 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,431 changes: 3,357 additions & 74 deletions frontend/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"lint": "eslint"
},
"dependencies": {
"@vis.gl/react-google-maps": "^1.6.1",
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dropdown-menu": "^2.1.16",
Expand All @@ -21,6 +20,7 @@
"@radix-ui/react-tabs": "^1.1.13",
"@tanstack/react-query": "^5.90.7",
"@tanstack/react-table": "^8.21.3",
"@vis.gl/react-google-maps": "^1.6.1",
"axios": "^1.12.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand All @@ -33,6 +33,8 @@
"react-day-picker": "^9.11.1",
"react-dom": "19.1.0",
"react-hook-form": "^7.65.0",
"react-icons": "^5.5.0",
"shadcn": "^3.5.0",
"tailwind-merge": "^3.3.1",
"validator": "^13.15.23",
"zod": "^4.1.12"
Expand Down
90 changes: 90 additions & 0 deletions frontend/src/app/InfoChipDemo/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"use client";

import { GenericInfoChip } from "@/components/GenericInfoChip";
import LocationInfoChipDetails from "@/components/LocationInfoChipDetails";
import { useSidebar } from "@/components/SidebarContext";
import StudentInfoChipDetails from "@/components/StudentInfoChipDetails";
import { Location } from "@/types/api/location";
import { Student } from "@/types/api/student";

const Page = () => {
const { openSidebar } = useSidebar();
const { student: defaultStudent, location: defaultLocation } =
getTestChipData();

return (
<div className="font-sans min-h-screen flex flex-col items-center justify-center p-8 sm:p-20 max-w-2xl mx-auto">
<h1 className="text-3xl font-bold mb-4">Info Chip Demo Page</h1>

<h2 className="text-2xl font-bold mb-4">Test Sidebar</h2>
<button
className="bg-gray-200 px-3 py-1 rounded mb-6"
onClick={() =>
openSidebar(
"test-sidebar",
"Example",
"Example Description",
<div>Hello from Sidebar!</div>
)
}
>
Open Sidebar
</button>

<h2 className="text-2xl font-bold mb-4">Student Info Chip</h2>
<GenericInfoChip
chipKey="student-1"
shortName={`${defaultStudent.firstName} ${defaultStudent.lastName}`}
title="Student Information"
description="Detailed information about the selected student"
sidebarContent={<StudentInfoChipDetails data={defaultStudent} />}
/>

<h2 className="text-2xl font-bold mt-6 mb-4">Location Info Chip</h2>
<GenericInfoChip
chipKey="location-1"
title="Location Information"
description="Detailed information about the selected location"
shortName={defaultLocation.formattedAddress}
sidebarContent={<LocationInfoChipDetails data={defaultLocation} />}
/>
</div>
);
};

export function getTestChipData() {
const student: Student = {
id: 1,
firstName: "Mr",
lastName: "Beast",
contactPreference: "text",
email: "[email protected]",
pid: "123456789",
lastRegistered: new Date("2023-08-15"),
phoneNumber: "555-1234",
};

const location: Location = {
id: 1,
warningCount: 2,
citationCount: 1,
hasActiveHold: true,
holdExpirationDate: new Date("2025-12-31"),
googlePlaceId: "abcd1234",
formattedAddress: "123 Main St, Chapel Hill, NC",
latitude: 35.9132,
longitude: -79.0558,
streetNumber: "123",
streetName: "Main St",
unit: "Apt 4",
city: "Chapel Hill",
county: "Orange",
state: "NC",
country: "USA",
zipCode: "27514",
};

return { student, location };
}

export default Page;
9 changes: 8 additions & 1 deletion frontend/src/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use client";

import GenericSidebar from "@/components/GenericSidebar";
import { SidebarProvider } from "@/components/SidebarContext";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { SessionProvider } from "next-auth/react";

Expand All @@ -8,7 +10,12 @@ const queryClient = new QueryClient();
export default function Providers({ children }: { children: React.ReactNode }) {
return (
<SessionProvider>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
<SidebarProvider>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
<GenericSidebar />
</SidebarProvider>
</SessionProvider>
);
}
25 changes: 25 additions & 0 deletions frontend/src/components/GenericChipDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";

import { ReactNode } from "react";

interface GenericChipDetailsProps<T> {
data: T;
title: string;
description: string;
renderView: (value: T) => ReactNode;
}

export function GenericChipDetails<T>({
data,
title,
description,
renderView,
}: GenericChipDetailsProps<T>) {
return (
<div className="space-y-3">
<h1 className="text-lg font-bold">{title}</h1>
<h2 className="text-lg">{description}</h2>
{renderView(data)}
</div>
);
}
44 changes: 44 additions & 0 deletions frontend/src/components/GenericInfoChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { useSidebar } from "@/components/SidebarContext";
import { Badge } from "@/components/ui/badge";
import { ReactNode } from "react";

interface GenericInfoChipProps {
chipKey: string;
shortName: string;
title: string;
description: string;
sidebarContent: ReactNode;
}

export function GenericInfoChip({
chipKey,
shortName,
title,
description,
sidebarContent,
}: GenericInfoChipProps) {
const { openSidebar, selectedKey, closeSidebar } = useSidebar();
const isSelected = selectedKey === chipKey;

const handleOpen = () => {
if (isSelected) {
closeSidebar();
return;
} else {
openSidebar(chipKey, title, description, sidebarContent);
}
};

return (
<Badge
onClick={handleOpen}
className={`cursor-pointer px-3 py-1 ${
isSelected ? "bg-blue-500 text-white" : "bg-gray-200 text-black"
}`}
>
{shortName}
</Badge>
);
}
22 changes: 22 additions & 0 deletions frontend/src/components/GenericSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";
import { useSidebar } from "@/components/SidebarContext";

function GenericSidebar() {
const { isOpen, closeSidebar, content } = useSidebar();

if (!isOpen) return null;

return (
<div className="fixed top-0 right-0 w-96 h-full bg-white shadow-lg p-4 overflow-auto z-50">
<button
className="mb-4 px-3 py-1 bg-gray-200 rounded"
onClick={closeSidebar}
>
Close
</button>
{content}
</div>
);
}

export default GenericSidebar;
44 changes: 44 additions & 0 deletions frontend/src/components/LocationInfoChipDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { Location } from "@/types/api/location";
import { GenericChipDetails } from "./GenericChipDetails";

interface LocationInfoChipDetailsProps {
data: Location;
}

function LocationInfoChipDetails({ data }: LocationInfoChipDetailsProps) {
return (
<GenericChipDetails<Location>
data={data}
title={"Info about the Location"}
description={"View information on the Location you just clicked on"}
renderView={(d) => (
<div className="space-y-3">
<div>
<label className="block text-sm font-medium">Address</label>
<p className="p-2 border rounded">{d.formattedAddress}</p>
</div>
<div>
<label className="block text-sm font-medium">Warning Count</label>
<p className="p-2 border rounded">{d.warningCount}</p>
</div>
<div>
<label className="block text-sm font-medium">Citation Count</label>
<p className="p-2 border rounded">{d.citationCount}</p>
</div>
<div>
<label className="block text-sm font-medium">Active Hold</label>
<p className="p-2 border rounded">
{d.hasActiveHold
? "Active: Expires " + d.holdExpirationDate?.toDateString()
: "No"}
</p>
</div>
</div>
)}
/>
);
}

export default LocationInfoChipDetails;
40 changes: 40 additions & 0 deletions frontend/src/components/PartyInfoChipDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use client";

import { Party } from "@/types/api/party";
import { GenericChipDetails } from "./GenericChipDetails";

interface PartyInfoChipDetailsProps {
data: Party;
}

export function PartyInfoChipDetails({ data }: PartyInfoChipDetailsProps) {
return (
<GenericChipDetails<Party>
data={data}
title={"Info about the Party"}
description={"View information on the Party you just clicked on"}
renderView={(d) => (
<div className="space-y-3">
<div>
<label className="block text-sm font-medium">Address</label>
<p className="p-2 border rounded">{d.location.formattedAddress}</p>
</div>
<div>
<label className="block text-sm font-medium">Date</label>
<p className="p-2 border rounded">{d.datetime.toDateString()}</p>
</div>
<div>
<label className="block text-sm font-medium">First name</label>
<p className="p-2 border rounded">{d.contactOne.firstName}</p>
</div>
<div>
<label className="block text-sm font-medium">Last Name</label>
<p className="p-2 border rounded">{d.contactOne.lastName}</p>
</div>
</div>
)}
/>
);
}

export default PartyInfoChipDetails;
74 changes: 74 additions & 0 deletions frontend/src/components/SidebarContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"use client";
import { createContext, ReactNode, useContext, useState } from "react";

type SidebarContextType = {
isOpen: boolean;
title: string | null;
description: string | null;
content: ReactNode | null;
selectedKey: string | null;
openSidebar: (
key: string,
title: string,
description: string,
content: ReactNode
) => void;
closeSidebar: () => void;
};
interface SidebarProviderProps {
children: ReactNode;
}

const SidebarContext = createContext<SidebarContextType | undefined>(undefined);

export function SidebarProvider({ children }: SidebarProviderProps) {
const [isOpen, setIsOpen] = useState(false);
const [title, setTitle] = useState<string | null>(null);
const [description, setDescription] = useState<string | null>(null);
const [content, setContent] = useState<ReactNode | null>(null);
const [selectedKey, setSelectedKey] = useState<string | null>(null);

const openSidebar = (
key: string,
title: string,
description: string,
content: ReactNode
) => {
setContent(content);
setTitle(title);
setDescription(description);
setSelectedKey(key);
setIsOpen(true);
};

const closeSidebar = () => {
setIsOpen(false);
setContent(null);
setSelectedKey(null);
setTitle(null);
setDescription(null);
};

return (
<SidebarContext.Provider
value={{
isOpen,
title,
description,
content,
selectedKey,
openSidebar,
closeSidebar,
}}
>
{children}
</SidebarContext.Provider>
);
}

export const useSidebar = () => {
const context = useContext(SidebarContext);
if (!context)
throw new Error("useSidebar must be used within SidebarProvider");
return context;
};
Loading