From 7ea0976bdeb1e44f9af4938e8d30ca21362a59cb Mon Sep 17 00:00:00 2001 From: Sam Piper Date: Wed, 17 Apr 2024 18:30:06 +0100 Subject: [PATCH] changes: new themes feat: - changed back to using HSL (easier compatibility with shadcn tooling) - new light and dark themes - removal of navbar specific color (uses card variable now) - new subcomponents in active location selector - detailed count in status endpoint reponse fix: - readability - some syntax --- apps/anvil/src/sign-in/sign-in.service.ts | 11 +- .../navbar/appNav/appLinkDropdown.tsx | 12 +- .../src/components/navbar/appNav/index.tsx | 2 +- .../components/navbar/appSwitcher/index.tsx | 2 +- apps/forge/src/components/navbar/index.tsx | 2 +- .../src/components/routing/GenericError.tsx | 6 +- .../ActiveLocationSelector/QueueStatus.tsx | 24 ++++ .../ActiveLocationSelector/StatusBadge.tsx | 24 ++++ .../ActiveLocationSelector/UserCount.tsx | 49 +++++++ .../ActiveLocationSelector/index.tsx | 101 +++++++++----- .../signin/actions/SignInManager/index.tsx | 11 +- .../actions/SignInReasonInput/index.tsx | 22 ++- .../dashboard/components/SignInDrawer.tsx | 6 +- .../subcomponents/SignInReasonDisplay.tsx | 20 ++- .../src/components/signin/dashboard/index.tsx | 2 +- apps/forge/src/index.css | 127 ++++++++++-------- .../_reponly/signin/actions/enqueue.tsx | 50 +++---- .../_reponly/signin/actions/in.tsx | 50 +++---- .../_reponly/signin/actions/out.tsx | 50 +++---- .../_reponly/signin/actions/register.tsx | 50 +++---- .../_authenticated/signin/home/index.tsx | 2 +- .../routes/_authenticated/signin/index.tsx | 2 +- packages/types/sign_in.ts | 4 +- packages/ui/components/ui/toast.tsx | 83 +++++------- packages/ui/tailwind.config.js | 42 +++--- 25 files changed, 451 insertions(+), 303 deletions(-) create mode 100644 apps/forge/src/components/signin/ActiveLocationSelector/QueueStatus.tsx create mode 100644 apps/forge/src/components/signin/ActiveLocationSelector/StatusBadge.tsx create mode 100644 apps/forge/src/components/signin/ActiveLocationSelector/UserCount.tsx rename apps/forge/src/components/signin/{actions => }/ActiveLocationSelector/index.tsx (61%) diff --git a/apps/anvil/src/sign-in/sign-in.service.ts b/apps/anvil/src/sign-in/sign-in.service.ts index af86e16..cd75e73 100644 --- a/apps/anvil/src/sign-in/sign-in.service.ts +++ b/apps/anvil/src/sign-in/sign-in.service.ts @@ -58,8 +58,9 @@ export class SignInService implements OnModuleInit { } async getStatusForLocation(location: Location): Promise { - const [on_shift_count, count, max, can_sign_in, count_in_queue] = await Promise.all([ + const [on_shift_rep_count, off_shift_rep_count, total_count, max, can_sign_in, count_in_queue] = await Promise.all([ this.onShiftReps(location), + this.offShiftReps(location), this.totalCount(location), this.maxCount(location), this.canSignIn(location), @@ -67,10 +68,14 @@ export class SignInService implements OnModuleInit { this.outOfHours(), ]); + const user_count = total_count - off_shift_rep_count - on_shift_rep_count; + return { locationName: location, - open: on_shift_count > 0, - count, + open: on_shift_rep_count > 0, + on_shift_rep_count, + off_shift_rep_count, + user_count, max, out_of_hours: this.outOfHours(), needs_queue: !can_sign_in, diff --git a/apps/forge/src/components/navbar/appNav/appLinkDropdown.tsx b/apps/forge/src/components/navbar/appNav/appLinkDropdown.tsx index deb87a1..0b5c3bf 100644 --- a/apps/forge/src/components/navbar/appNav/appLinkDropdown.tsx +++ b/apps/forge/src/components/navbar/appNav/appLinkDropdown.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { Link, useNavigate } from "@tanstack/react-router"; import { AppLink } from "@/components/navbar/appNav/appLinks.ts"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; +import { Button } from "@ui/components/ui/button.tsx"; type DropdownLinkProps = { link: AppLink; @@ -15,7 +16,7 @@ const DropdownLink: React.FC = ({ link, onClick, activeId }) const isActive = linkItem.id === activeId; // Link styles - const linkClasses = `block px-4 py-2 text-sm rounded-md text-navbar-foreground hover:bg-accent ${ + const linkClasses = `block px-4 py-2 text-sm rounded-md text-card-foreground hover:bg-accent ${ isActive ? "border-2 border-accent" : "" }`; @@ -45,7 +46,7 @@ const DropdownLink: React.FC = ({ link, onClick, activeId }) {/* Children links */} {linkItem.children!.map((child) => renderLink(child, level + 1))} @@ -58,12 +59,9 @@ const DropdownLink: React.FC = ({ link, onClick, activeId }) return ( - + - + {linkItem.children!.map((child) => renderLink(child, level + 1))} diff --git a/apps/forge/src/components/navbar/appNav/index.tsx b/apps/forge/src/components/navbar/appNav/index.tsx index f214fc3..8f7e8d0 100644 --- a/apps/forge/src/components/navbar/appNav/index.tsx +++ b/apps/forge/src/components/navbar/appNav/index.tsx @@ -18,7 +18,7 @@ export default function AppNav() { {link.displayName} diff --git a/apps/forge/src/components/navbar/appSwitcher/index.tsx b/apps/forge/src/components/navbar/appSwitcher/index.tsx index 13dfd1b..3ccb401 100644 --- a/apps/forge/src/components/navbar/appSwitcher/index.tsx +++ b/apps/forge/src/components/navbar/appSwitcher/index.tsx @@ -21,7 +21,7 @@ export default function AppSwitcher() { - iForge | {currentapp} + iForge | {currentapp}
    diff --git a/apps/forge/src/components/navbar/index.tsx b/apps/forge/src/components/navbar/index.tsx index 7cb6a3b..19c2d5c 100644 --- a/apps/forge/src/components/navbar/index.tsx +++ b/apps/forge/src/components/navbar/index.tsx @@ -5,7 +5,7 @@ import AppNav from "@/components/navbar/appNav"; export default function NavBar() { return ( -
    +
    diff --git a/apps/forge/src/components/routing/GenericError.tsx b/apps/forge/src/components/routing/GenericError.tsx index 2debeff..2eb8848 100644 --- a/apps/forge/src/components/routing/GenericError.tsx +++ b/apps/forge/src/components/routing/GenericError.tsx @@ -6,6 +6,7 @@ import React from "react"; export const GenericError = () => { const navigate = useNavigate(); const goToHome = () => navigate({ to: "/" }); + const reload = () => navigate({ search: {} }); return ( @@ -27,7 +28,10 @@ export const GenericError = () => {
    +
    diff --git a/apps/forge/src/components/signin/ActiveLocationSelector/QueueStatus.tsx b/apps/forge/src/components/signin/ActiveLocationSelector/QueueStatus.tsx new file mode 100644 index 0000000..e6e7f67 --- /dev/null +++ b/apps/forge/src/components/signin/ActiveLocationSelector/QueueStatus.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { Ban } from "lucide-react"; + +interface QueueStatusProps { + queue_needed: boolean; + count_in_queue: number; +} + +export const QueueStatus: React.FC = ({ queue_needed, count_in_queue }) => { + if (!queue_needed) { + return ( +
    + + QUEUE DISABLED +
    + ); + } + return ( +
    + {count_in_queue} + in Queue +
    + ); +}; diff --git a/apps/forge/src/components/signin/ActiveLocationSelector/StatusBadge.tsx b/apps/forge/src/components/signin/ActiveLocationSelector/StatusBadge.tsx new file mode 100644 index 0000000..cdd1fde --- /dev/null +++ b/apps/forge/src/components/signin/ActiveLocationSelector/StatusBadge.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { Separator } from "@ui/components/ui/separator.tsx"; +import { Moon } from "lucide-react"; + +interface StatusBadgeProps { + is_open: boolean; + is_out_of_hours: boolean; +} + +export const StatusBadge: React.FC = ({ is_open, is_out_of_hours }) => { + return ( +
    + Status + {/* Open Status */} + {is_open ? OPEN : CLOSED} + {is_out_of_hours ? ( + <> + + Out of Hours + + ) : undefined} +
    + ); +}; diff --git a/apps/forge/src/components/signin/ActiveLocationSelector/UserCount.tsx b/apps/forge/src/components/signin/ActiveLocationSelector/UserCount.tsx new file mode 100644 index 0000000..0191d8e --- /dev/null +++ b/apps/forge/src/components/signin/ActiveLocationSelector/UserCount.tsx @@ -0,0 +1,49 @@ +import React, { useState } from "react"; + +interface UserCountProps { + rep_count: number; + off_shift_rep_count: number; + user_count: number; + max_count: number; +} + +export const UserCount: React.FC = ({ rep_count, off_shift_rep_count, max_count, user_count }) => { + const [showDetails, setShowDetails] = useState(false); + const total_count = rep_count + off_shift_rep_count + user_count; + + const handleClick = () => { + setShowDetails(!showDetails); + }; + + return ( +
    { + // Handle the 'Enter' key to mimic mouse click interaction + if (event.key === "Enter") { + handleClick(); + } + }} + > + {showDetails ? ( + <> + {rep_count} + rep(s) + {off_shift_rep_count} + off-shift rep(s) + {user_count} + user(s) + + ) : ( + <> + user count + + {total_count}|{max_count} + + max + + )} +
    + ); +}; diff --git a/apps/forge/src/components/signin/actions/ActiveLocationSelector/index.tsx b/apps/forge/src/components/signin/ActiveLocationSelector/index.tsx similarity index 61% rename from apps/forge/src/components/signin/actions/ActiveLocationSelector/index.tsx rename to apps/forge/src/components/signin/ActiveLocationSelector/index.tsx index 7768c39..c46d3ff 100644 --- a/apps/forge/src/components/signin/actions/ActiveLocationSelector/index.tsx +++ b/apps/forge/src/components/signin/ActiveLocationSelector/index.tsx @@ -12,7 +12,12 @@ import { useQuery } from "@tanstack/react-query"; import { Separator } from "@ui/components/ui/separator.tsx"; import { PulseLoader } from "react-spinners"; import { Location } from "@ignis/types/sign_in.ts"; -import { Moon } from "lucide-react"; +import { UserCount } from "@/components/signin/ActiveLocationSelector/UserCount.tsx"; +import { StatusBadge } from "@/components/signin/ActiveLocationSelector/StatusBadge.tsx"; +import { QueueStatus } from "@/components/signin/ActiveLocationSelector/QueueStatus.tsx"; +import { Skeleton } from "@ui/components/ui/skeleton.tsx"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@ui/components/ui/tooltip.tsx"; +import { Link } from "@tanstack/react-router"; const ActiveLocationSelector = () => { const [open, setOpen] = useState(false); @@ -60,9 +65,9 @@ const ActiveLocationSelector = () => { return ( <> -
    +
    - Select Location + Select Location
    - {isLoading && } - {activeLocationStatus && !isLoading && ( + {isLoading && (
    - Status - {/* Open Status */} - {activeLocationStatus.open ? ( - OPEN - ) : ( - CLOSED - )} - {activeLocationStatus.out_of_hours ? ( - <> - - Out of Hours - - ) : undefined} - - {/* Count and Max Count Status */} - - Current Users - - {" "} - {activeLocationStatus.count}/{activeLocationStatus.max} - - Max Users - {/* Queue Status */} - - {activeLocationStatus.needs_queue ? ( - Queue Needed - ) : ( - No Queue Needed - )} + - {activeLocationStatus.count_in_queue} - in Queue + + +
    + )} + {activeLocationStatus && !isLoading && ( +
    + + + + + + +

    The space is marked as OPEN when there is at least one rep signed in.

    +

    It is closed otherwise

    +

    Current opening hours are: 12:00 - 20:00

    +
    +
    + + + + + + +

    Click to view a more detailed breakdown of the count

    +
    +
    + + + + + +

    The Queue is only enabled when capacity is reached.

    +

    + The view detailed queue status visit the{" "} + + dashboard + + . +

    +
    +
    +
    )}
    diff --git a/apps/forge/src/components/signin/actions/SignInManager/index.tsx b/apps/forge/src/components/signin/actions/SignInManager/index.tsx index 34ef808..bc7e58d 100644 --- a/apps/forge/src/components/signin/actions/SignInManager/index.tsx +++ b/apps/forge/src/components/signin/actions/SignInManager/index.tsx @@ -169,14 +169,13 @@ const SignInActionsManager: React.FC = ({ initialFlow }) => if (StepComponent) { // This is to stop the case where a rep is backtracking and then step 3 auto navigates them forwards again - if (currentStep == SignInSteps.Step3) { + if (currentStep === SignInSteps.Step3) { dispatch(signinActions.updateSignInSessionField("navigation_is_backtracking", true)); } return StepComponent ? : null; - } else { - // Handle the end of the flow or an invalid step - return
    Flow completed or invalid step
    ; } + // Handle the end of the flow or an invalid step + return
    Flow completed or invalid step
    ; }; const getTotalSteps = (flow: FlowType): number => { @@ -190,7 +189,7 @@ const SignInActionsManager: React.FC = ({ initialFlow }) =>

    Sign In Actions

    {currentFlow && ( -
    +
    Current Flow: {flowTypeToPrintTable(currentFlow)} @@ -218,7 +217,7 @@ const SignInActionsManager: React.FC = ({ initialFlow }) => -
    -
    +
    {entries.length === 0 && ( diff --git a/apps/forge/src/components/signin/dashboard/components/SignedInUserCard/subcomponents/SignInReasonDisplay.tsx b/apps/forge/src/components/signin/dashboard/components/SignedInUserCard/subcomponents/SignInReasonDisplay.tsx index c610eb4..1df28ad 100644 --- a/apps/forge/src/components/signin/dashboard/components/SignedInUserCard/subcomponents/SignInReasonDisplay.tsx +++ b/apps/forge/src/components/signin/dashboard/components/SignedInUserCard/subcomponents/SignInReasonDisplay.tsx @@ -9,16 +9,24 @@ interface SignInReasonDisplayProps { export const SignInReasonDisplay: React.FC = ({ tools, reason }) => { return ( <> -
    +
    -
    Sign In Reason
    -
    {reason.name}
    +
    + Sign In Reason +
    +
    + + {reason.name} + +
    -
    Tools Used
    -
    +
    + Tools Used +
    +
    {tools.map((tool) => ( - + {tool} ))} diff --git a/apps/forge/src/components/signin/dashboard/index.tsx b/apps/forge/src/components/signin/dashboard/index.tsx index 1b66e39..278d103 100644 --- a/apps/forge/src/components/signin/dashboard/index.tsx +++ b/apps/forge/src/components/signin/dashboard/index.tsx @@ -1,4 +1,4 @@ -import ActiveLocationSelector from "@/components/signin/actions/ActiveLocationSelector"; +import ActiveLocationSelector from "@/components/signin/ActiveLocationSelector"; import { SignInDrawer } from "@/components/signin/dashboard/components/SignInDrawer.tsx"; import { SignedInUserCard } from "@/components/signin/dashboard/components/SignedInUserCard"; import Title from "@/components/title"; diff --git a/apps/forge/src/index.css b/apps/forge/src/index.css index 201ac5e..1daf1be 100644 --- a/apps/forge/src/index.css +++ b/apps/forge/src/index.css @@ -5,105 +5,116 @@ @layer base { :root { + /* Custom Colors */ --heartspace: #3badbf; --mainspace: #ce5a4b; --george-porter: #2e3bad; - --background: #fefbfb; - --foreground: #0d0402; - --muted: #eef1f2; - --muted-foreground: #666666; - --popover: #fdf8f7; - --popover-foreground: #090202; - --card: #fdf8f7; - --card-foreground: #090202; - --border: #e9e7e7; - --input: #e9e7e7; - --primary: #ce5a4b; - --primary-foreground: #ffffff; - --secondary: #b1b1b1; - --secondary-foreground: #000000; - --accent: #f5f5f4; - --accent-foreground: #555351; - --destructive: #d63600; - --destructive-foreground: #ffffff; - --navbar: #fef9fa; - --navbar-foreground: #09090b; - --ring: #ce5a4b; + /* shadcn Colors (Generated from https://ui.jln.dev */ + + --background: 0 0% 99%; + --foreground: 60 2% 30%; + --muted: 0 12% 90%; + --muted-foreground: 0 12% 30%; + --popover: 0 0% 96%; + --popover-foreground: 60 2% 20%; + --card: 0 0% 97%; + --card-foreground: 60 2% 25%; + --border: 0 0% 94%; + --input: 0 0% 91%; + --primary: 7 82% 36%; + --primary-foreground: 7 82% 96%; + --secondary: 7 30% 75%; + --secondary-foreground: 7 30% 15%; + --accent: 0 0% 84%; + --accent-foreground: 0 0% 24%; + --destructive: 1 83% 35%; + --destructive-foreground: 1 83% 95%; + --ring: 7 82% 36%; --radius: 0.5rem; --font-sans: Roboto, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* SignInsChart.tsx specific stuff */ /* The chart's background */ - #sign-in-chart>div>div>svg>rect { - fill: var(--background) !important; + + #sign-in-chart > div > div > svg > rect { + fill: hsl(var(--background)) !important; } /* Any text in the chart*/ - #sign-in-chart>text { - fill: var(--foreground) !important; - font-family: var(--font-sans) !important; + + #sign-in-chart > text { + fill: hsl(var(--foreground)) !important; + font-family: var(--font-sans), sans-serif !important; } /* The individual rectangles in the chart */ + #sign-in-chart g rect[x][y][width][height][style] { rx: var(--radius); - stroke: var(--background) !important; + stroke: hsl(var(--background)) !important; } /* The lines that bind months together */ - #sign-in-chart>div>div>svg>g>path { - stroke: var(--background) !important; + + #sign-in-chart > div > div > svg > g > path { + stroke: hsl(var(--background)) !important; stroke-width: 0px !important; } /* The legend text */ - #sign-in-chart>div>div>svg>g>text { - fill: var(--foreground) !important; - font-family: var(--font-sans) !important; + + #sign-in-chart > div > div > svg > g > text { + fill: hsl(var(--foreground)) !important; + font-family: var(--font-sans), sans-serif !important; } /* The on-hover pop-up */ - #sign-in-chart>div>div>div>div { - background: var(--background) !important; + + #sign-in-chart > div > div > div > div { + background: hsl(var(--background)) !important; color: var(--foreground) !important; } /* The text for the pop-up */ - #sign-in-chart>div>div>div>div>div>span { - color: var(--foreground) !important; - font-family: var(--font-sans) !important; + + #sign-in-chart > div > div > div > div > div > span { + color: hsl(var(--foreground)) !important; + font-family: var(--font-sans), sans-serif !important; } /* Otherwise this doesn't show as clickable */ + #training-content a { - color: var(--primary) + color: hsl(var(--primary)) } } .dark { - --background: #080302; - --foreground: #faf9f9; - --muted: #0d1111; - --muted-foreground: #a6b3b5; - --popover: #0c0403; - --popover-foreground: #fdfcfc; - --card: #0c0403; - --card-foreground: #fdfcfc; - --border: #1e1b1a; - --input: #1e1b1a; - --primary: #ce5a4b; - --primary-foreground: #ffffff; - --secondary: #b1b1b1; - --secondary-foreground: #000000; - --accent: #292524; - --accent-foreground: #cfcfcf; - --destructive: #e63900; - --destructive-foreground: #ffffff; - --navbar: #171717; - --navbar-foreground: #f2f2f2; + /* shadcn Colors dark mode (Generated from https://ui.jln.dev */ + + --background: 220 11% 11%; + --foreground: 60 30% 96%; + --muted: 220 12% 15%; + --muted-foreground: 220 12% 65%; + --popover: 220 11% 8%; + --popover-foreground: 0 0% 100%; + --card: 220 11% 9%; + --card-foreground: 0 0% 100%; + --border: 220 1% 16%; + --input: 220 1% 19%; + --primary: 353 57% 45%; + --primary-foreground: 0 0% 100%; + --secondary: 353 30% 75%; + --secondary-foreground: 353 30% 15%; + --accent: 220 11% 26%; + --accent-foreground: 220 11% 86%; + --destructive: 9 97% 46%; + --destructive-foreground: 0 0% 100%; + --ring: 353 57% 45%; + } } diff --git a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/enqueue.tsx b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/enqueue.tsx index ca9557c..8859e2e 100644 --- a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/enqueue.tsx +++ b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/enqueue.tsx @@ -1,31 +1,31 @@ -import {createFileRoute, redirect} from "@tanstack/react-router"; -import ActiveLocationSelector from "@/components/signin/actions/ActiveLocationSelector"; +import { createFileRoute, redirect } from "@tanstack/react-router"; +import ActiveLocationSelector from "@/components/signin/ActiveLocationSelector"; import SignInActionsManager from "@/components/signin/actions/SignInManager"; -import {FlowType} from "@/components/signin/actions/SignInManager/types.ts"; +import { FlowType } from "@/components/signin/actions/SignInManager/types.ts"; import Title from "@/components/title"; const EnqueueComponent = () => { - return ( - <> -
    - - <ActiveLocationSelector/> - <SignInActionsManager initialFlow={FlowType.Enqueue}/> - </div> - </> - ) + return ( + <> + <div className="p-4 mt-1"> + <Title prompt="Enqueue User" /> + <ActiveLocationSelector /> + <SignInActionsManager initialFlow={FlowType.Enqueue} /> + </div> + </> + ); }; -export const Route = createFileRoute('/_authenticated/_reponly/signin/actions/enqueue')({ - beforeLoad: ({ context, location }) => { - if (!context.auth.user?.roles.find(role => role.name === "Rep")) { - throw redirect({ - to: '/auth/login', - search: { - redirect: location.href, - }, - }) - } - }, - component: EnqueueComponent -}); \ No newline at end of file +export const Route = createFileRoute("/_authenticated/_reponly/signin/actions/enqueue")({ + beforeLoad: ({ context, location }) => { + if (!context.auth.user?.roles.find((role) => role.name === "Rep")) { + throw redirect({ + to: "/auth/login", + search: { + redirect: location.href, + }, + }); + } + }, + component: EnqueueComponent, +}); diff --git a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/in.tsx b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/in.tsx index 95ec040..b8bddf0 100644 --- a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/in.tsx +++ b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/in.tsx @@ -1,31 +1,31 @@ -import {createFileRoute, redirect} from "@tanstack/react-router"; -import ActiveLocationSelector from "@/components/signin/actions/ActiveLocationSelector"; +import { createFileRoute, redirect } from "@tanstack/react-router"; +import ActiveLocationSelector from "@/components/signin/ActiveLocationSelector"; import SignInActionsManager from "@/components/signin/actions/SignInManager"; -import {FlowType} from "@/components/signin/actions/SignInManager/types.ts"; +import { FlowType } from "@/components/signin/actions/SignInManager/types.ts"; import Title from "@/components/title"; const InComponent = () => { - return ( - <> - <div className="p-4 mt-1"> - <Title prompt="Signin User"/> - <ActiveLocationSelector/> - <SignInActionsManager initialFlow={FlowType.SignIn}/> - </div> - </> - ) + return ( + <> + <div className="p-4 mt-1"> + <Title prompt="Signin User" /> + <ActiveLocationSelector /> + <SignInActionsManager initialFlow={FlowType.SignIn} /> + </div> + </> + ); }; -export const Route = createFileRoute('/_authenticated/_reponly/signin/actions/in')({ - beforeLoad: ({ context, location }) => { - if (!context.auth.user?.roles.find(role => role.name === "Rep")) { - throw redirect({ - to: '/auth/login', - search: { - redirect: location.href, - }, - }) - } - }, - component: InComponent -}); \ No newline at end of file +export const Route = createFileRoute("/_authenticated/_reponly/signin/actions/in")({ + beforeLoad: ({ context, location }) => { + if (!context.auth.user?.roles.find((role) => role.name === "Rep")) { + throw redirect({ + to: "/auth/login", + search: { + redirect: location.href, + }, + }); + } + }, + component: InComponent, +}); diff --git a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/out.tsx b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/out.tsx index 81ee30e..9dc6883 100644 --- a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/out.tsx +++ b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/out.tsx @@ -1,31 +1,31 @@ -import {createFileRoute, redirect} from "@tanstack/react-router"; -import ActiveLocationSelector from "@/components/signin/actions/ActiveLocationSelector"; +import { createFileRoute, redirect } from "@tanstack/react-router"; +import ActiveLocationSelector from "@/components/signin/ActiveLocationSelector"; import SignInActionsManager from "@/components/signin/actions/SignInManager"; -import {FlowType} from "@/components/signin/actions/SignInManager/types.ts"; +import { FlowType } from "@/components/signin/actions/SignInManager/types.ts"; import Title from "@/components/title"; const OutComponent = () => { - return ( - <> - <div className="p-4 mt-1"> - <Title prompt="Signout User"/> - <ActiveLocationSelector/> - <SignInActionsManager initialFlow={FlowType.SignOut}/> - </div> - </> - ) + return ( + <> + <div className="p-4 mt-1"> + <Title prompt="Signout User" /> + <ActiveLocationSelector /> + <SignInActionsManager initialFlow={FlowType.SignOut} /> + </div> + </> + ); }; -export const Route = createFileRoute('/_authenticated/_reponly/signin/actions/out')({ - beforeLoad: ({ context, location }) => { - if (!context.auth.user?.roles.find(role => role.name === "Rep")) { - throw redirect({ - to: '/auth/login', - search: { - redirect: location.href, - }, - }) - } - }, - component: OutComponent -}); \ No newline at end of file +export const Route = createFileRoute("/_authenticated/_reponly/signin/actions/out")({ + beforeLoad: ({ context, location }) => { + if (!context.auth.user?.roles.find((role) => role.name === "Rep")) { + throw redirect({ + to: "/auth/login", + search: { + redirect: location.href, + }, + }); + } + }, + component: OutComponent, +}); diff --git a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/register.tsx b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/register.tsx index 048f3db..3cd7f4a 100644 --- a/apps/forge/src/routes/_authenticated/_reponly/signin/actions/register.tsx +++ b/apps/forge/src/routes/_authenticated/_reponly/signin/actions/register.tsx @@ -1,31 +1,31 @@ -import {createFileRoute, redirect} from "@tanstack/react-router"; -import ActiveLocationSelector from "@/components/signin/actions/ActiveLocationSelector"; +import { createFileRoute, redirect } from "@tanstack/react-router"; +import ActiveLocationSelector from "@/components/signin/ActiveLocationSelector"; import SignInActionsManager from "@/components/signin/actions/SignInManager"; -import {FlowType} from "@/components/signin/actions/SignInManager/types.ts"; +import { FlowType } from "@/components/signin/actions/SignInManager/types.ts"; import Title from "@/components/title"; const RegisterComponent = () => { - return ( - <> - <div className="p-4 mt-1"> - <Title prompt="Register User"/> - <ActiveLocationSelector/> - <SignInActionsManager initialFlow={FlowType.Register}/> - </div> - </> - ) + return ( + <> + <div className="p-4 mt-1"> + <Title prompt="Register User" /> + <ActiveLocationSelector /> + <SignInActionsManager initialFlow={FlowType.Register} /> + </div> + </> + ); }; -export const Route = createFileRoute('/_authenticated/_reponly/signin/actions/register')({ - beforeLoad: ({ context, location }) => { - if (!context.auth.user?.roles.find(role => role.name === "Rep")) { - throw redirect({ - to: '/auth/login', - search: { - redirect: location.href, - }, - }) - } - }, - component: RegisterComponent -}); \ No newline at end of file +export const Route = createFileRoute("/_authenticated/_reponly/signin/actions/register")({ + beforeLoad: ({ context, location }) => { + if (!context.auth.user?.roles.find((role) => role.name === "Rep")) { + throw redirect({ + to: "/auth/login", + search: { + redirect: location.href, + }, + }); + } + }, + component: RegisterComponent, +}); diff --git a/apps/forge/src/routes/_authenticated/signin/home/index.tsx b/apps/forge/src/routes/_authenticated/signin/home/index.tsx index 0072969..dfe9d33 100644 --- a/apps/forge/src/routes/_authenticated/signin/home/index.tsx +++ b/apps/forge/src/routes/_authenticated/signin/home/index.tsx @@ -1,4 +1,4 @@ -import ActiveLocationSelector from "@/components/signin/actions/ActiveLocationSelector"; +import ActiveLocationSelector from "@/components/signin/ActiveLocationSelector"; import Title from "@/components/title"; import { useUser } from "@/lib/utils.ts"; import { createFileRoute } from "@tanstack/react-router"; diff --git a/apps/forge/src/routes/_authenticated/signin/index.tsx b/apps/forge/src/routes/_authenticated/signin/index.tsx index dbb8c60..1d4acdc 100644 --- a/apps/forge/src/routes/_authenticated/signin/index.tsx +++ b/apps/forge/src/routes/_authenticated/signin/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute, redirect } from "@tanstack/react-router"; -import ActiveLocationSelector from "@/components/signin/actions/ActiveLocationSelector"; +import ActiveLocationSelector from "@/components/signin/ActiveLocationSelector"; import SignInActionsManager from "@/components/signin/actions/SignInManager"; import Title from "@/components/title"; diff --git a/packages/types/sign_in.ts b/packages/types/sign_in.ts index fd684e5..b419260 100644 --- a/packages/types/sign_in.ts +++ b/packages/types/sign_in.ts @@ -44,7 +44,9 @@ export type List = { /** The status of a space */ export type LocationStatus = { open: boolean; - count: number; + on_shift_rep_count: number; + off_shift_rep_count: number; + user_count: number; max: number; needs_queue: boolean; out_of_hours: boolean; diff --git a/packages/ui/components/ui/toast.tsx b/packages/ui/components/ui/toast.tsx index b5b63c0..574d62a 100644 --- a/packages/ui/components/ui/toast.tsx +++ b/packages/ui/components/ui/toast.tsx @@ -1,11 +1,11 @@ -import * as React from "react" -import * as ToastPrimitives from "@radix-ui/react-toast" -import { cva, type VariantProps } from "class-variance-authority" -import { X } from "lucide-react" +import * as React from "react"; +import * as ToastPrimitives from "@radix-ui/react-toast"; +import { cva, type VariantProps } from "class-variance-authority"; +import { X } from "lucide-react"; -import { cn } from "@ui/lib/utils" +import { cn } from "@ui/lib/utils"; -const ToastProvider = ToastPrimitives.Provider +const ToastProvider = ToastPrimitives.Provider; const ToastViewport = React.forwardRef< React.ElementRef<typeof ToastPrimitives.Viewport>, @@ -15,12 +15,12 @@ const ToastViewport = React.forwardRef< ref={ref} className={cn( "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", - className + className, )} {...props} /> -)) -ToastViewport.displayName = ToastPrimitives.Viewport.displayName +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; const toastVariants = cva( "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", @@ -28,30 +28,23 @@ const toastVariants = cva( variants: { variant: { default: "border bg-background text-foreground", - destructive: - "destructive group border-destructive bg-destructive text-destructive-foreground", + destructive: "destructive group border-destructive bg-destructive text-destructive-foreground", + informative: "informative group border-informative bg-informative text-informative-foreground", }, }, defaultVariants: { variant: "default", }, - } -) + }, +); const Toast = React.forwardRef< React.ElementRef<typeof ToastPrimitives.Root>, - React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & - VariantProps<typeof toastVariants> + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants> >(({ className, variant, ...props }, ref) => { - return ( - <ToastPrimitives.Root - ref={ref} - className={cn(toastVariants({ variant }), className)} - {...props} - /> - ) -}) -Toast.displayName = ToastPrimitives.Root.displayName + return <ToastPrimitives.Root ref={ref} className={cn(toastVariants({ variant }), className)} {...props} />; +}); +Toast.displayName = ToastPrimitives.Root.displayName; const ToastAction = React.forwardRef< React.ElementRef<typeof ToastPrimitives.Action>, @@ -60,13 +53,13 @@ const ToastAction = React.forwardRef< <ToastPrimitives.Action ref={ref} className={cn( - "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", - className + "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.informative]:border-muted/40 group-[.destructive]:hover:border-destructive-foreground/70 group-[.informative]:hover:border-informative-foreground/70 group-[.destructive]:hover:bg-destructive group-[.informative]:hover:bg-informative group-[.destructive]:hover:text-destructive-foreground group-[.informative]:hover:text-informative-foreground group-[.destructive]:focus:ring-destructive group-[.informative]:focus:ring-informative", + className, )} {...props} /> -)) -ToastAction.displayName = ToastPrimitives.Action.displayName +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; const ToastClose = React.forwardRef< React.ElementRef<typeof ToastPrimitives.Close>, @@ -75,44 +68,36 @@ const ToastClose = React.forwardRef< <ToastPrimitives.Close ref={ref} className={cn( - "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", - className + "group-[.informative]:text-informative-300 group-[.informative]:hover:text-informative-50 group-[.informative]:focus:ring-informative-400 group-[.informative]:focus:ring-offset-informative-600 absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", + className, )} toast-close="" {...props} > <X className="h-4 w-4" /> </ToastPrimitives.Close> -)) -ToastClose.displayName = ToastPrimitives.Close.displayName +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; const ToastTitle = React.forwardRef< React.ElementRef<typeof ToastPrimitives.Title>, React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title> >(({ className, ...props }, ref) => ( - <ToastPrimitives.Title - ref={ref} - className={cn("text-sm font-semibold", className)} - {...props} - /> -)) -ToastTitle.displayName = ToastPrimitives.Title.displayName + <ToastPrimitives.Title ref={ref} className={cn("text-sm font-semibold", className)} {...props} /> +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; const ToastDescription = React.forwardRef< React.ElementRef<typeof ToastPrimitives.Description>, React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description> >(({ className, ...props }, ref) => ( - <ToastPrimitives.Description - ref={ref} - className={cn("text-sm opacity-90", className)} - {...props} - /> -)) -ToastDescription.displayName = ToastPrimitives.Description.displayName + <ToastPrimitives.Description ref={ref} className={cn("text-sm opacity-90", className)} {...props} /> +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; -type ToastProps = React.ComponentPropsWithoutRef<typeof Toast> +type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>; -type ToastActionElement = React.ReactElement<typeof ToastAction> +type ToastActionElement = React.ReactElement<typeof ToastAction>; export { type ToastProps, @@ -124,4 +109,4 @@ export { ToastDescription, ToastClose, ToastAction, -} +}; diff --git a/packages/ui/tailwind.config.js b/packages/ui/tailwind.config.js index aa0de32..02e78a8 100644 --- a/packages/ui/tailwind.config.js +++ b/packages/ui/tailwind.config.js @@ -25,42 +25,42 @@ module.exports = { mainspace: "var(--mainspace)", george_porter: "var(--george-porter)", - border: "var(--border)", - input: "var(--input)", - ring: "var(--ring)", - background: "var(--background)", - foreground: "var(--foreground)", + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", primary: { - DEFAULT: "var(--primary)", - foreground: "var(--primary-foreground)", + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", }, secondary: { - DEFAULT: "var(--secondary)", - foreground: "var(--secondary-foreground)", + DEFAULT: "hsl(var(--secondary)", + foreground: "hsl(var(--secondary-foreground))", }, destructive: { - DEFAULT: "var(--destructive)", - foreground: "var(--destructive-foreground)", + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", }, muted: { - DEFAULT: "var(--muted)", - foreground: "var(--muted-foreground)", + DEFAULT: "hsl(var(--muted))", + foreground: "var(--muted-foreground))", }, accent: { - DEFAULT: "var(--accent)", - foreground: "var(--accent-foreground)", + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", }, popover: { - DEFAULT: "var(--popover)", - foreground: "var(--popover-foreground)", + DEFAULT: "hsl(var(--popover))", + foreground: "var(--popover-foreground))", }, card: { - DEFAULT: "var(--card)", - foreground: "var(--card-foreground)", + DEFAULT: "hsl(var(--card))", + foreground: "var(--card-foreground))", }, navbar: { - DEFAULT: "var(--navbar)", - foreground: "var(--navbar-foreground)", + DEFAULT: "hsl(var(--navbar))", + foreground: "hsl(var(--navbar-foreground))", }, }, },