diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 0b773456..8db8bc6a 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -66,6 +66,7 @@ definitions: type: integer type: array limit: + minimum: 0 type: integer type: object GenerateRequestInput: diff --git a/clients/mobile/components/tasks/active-filter-chips.tsx b/clients/mobile/components/tasks/active-filter-chips.tsx index bac98fb8..c6b8ee59 100644 --- a/clients/mobile/components/tasks/active-filter-chips.tsx +++ b/clients/mobile/components/tasks/active-filter-chips.tsx @@ -1,11 +1,6 @@ import Feather from "@expo/vector-icons/Feather"; import { Pressable, Text, View } from "react-native"; -interface FilterItem { - label: string; - value: string; -} - interface ActiveFilterChipsProps { filters: { label: string; value: string }[]; onRemoveFilter: (value: string) => void; diff --git a/clients/mobile/eslint.config.js b/clients/mobile/eslint.config.js index ece97001..67de47bd 100644 --- a/clients/mobile/eslint.config.js +++ b/clients/mobile/eslint.config.js @@ -7,4 +7,13 @@ module.exports = defineConfig([ { ignores: ["dist/*"], }, + { + settings: { + "import/resolver": { + typescript: { + project: "./tsconfig.json", + }, + }, + }, + }, ]); diff --git a/clients/web/src/components/Sidebar.tsx b/clients/web/src/components/Sidebar.tsx index d6f270e2..9d487e05 100644 --- a/clients/web/src/components/Sidebar.tsx +++ b/clients/web/src/components/Sidebar.tsx @@ -19,7 +19,7 @@ function NavLink({ to={to} className={`flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors ${ isActive - ? "bg-primary/10 font-semibold text-gray-900" + ? "bg-bg-selected font-semibold text-subtle" : "text-gray-500 hover:bg-gray-100 hover:text-gray-700" }`} > @@ -29,6 +29,33 @@ function NavLink({ ); } +function ProfileLink({ displayName }: { displayName: string | undefined }) { + const pathname = useRouterState({ select: (s) => s.location.pathname }); + const isActive = pathname === "/profile"; + return ( + + +
+

+ {displayName || "User"} +

+

Hotel Chain

+
+ + ); +} + export function Sidebar() { const { user } = useUser(); @@ -69,21 +96,7 @@ export function Sidebar() { Settings -
- -
-

- {displayName || "User"} -

-

Hotel Chain

-
-
+ ); diff --git a/clients/web/src/routeTree.gen.ts b/clients/web/src/routeTree.gen.ts index ed265607..38533111 100644 --- a/clients/web/src/routeTree.gen.ts +++ b/clients/web/src/routeTree.gen.ts @@ -16,6 +16,7 @@ import { Route as IndexRouteImport } from './routes/index' import { Route as ProtectedTestApiRouteImport } from './routes/_protected/test-api' import { Route as ProtectedSettingsRouteImport } from './routes/_protected/settings' import { Route as ProtectedRoomsRouteImport } from './routes/_protected/rooms' +import { Route as ProtectedProfileRouteImport } from './routes/_protected/profile' import { Route as ProtectedHomeRouteImport } from './routes/_protected/home' import { Route as ProtectedRoomsIndexRouteImport } from './routes/_protected/rooms.index' import { Route as ProtectedGuestsIndexRouteImport } from './routes/_protected/guests.index' @@ -55,6 +56,11 @@ const ProtectedRoomsRoute = ProtectedRoomsRouteImport.update({ path: '/rooms', getParentRoute: () => ProtectedRoute, } as any) +const ProtectedProfileRoute = ProtectedProfileRouteImport.update({ + id: '/profile', + path: '/profile', + getParentRoute: () => ProtectedRoute, +} as any) const ProtectedHomeRoute = ProtectedHomeRouteImport.update({ id: '/home', path: '/home', @@ -81,6 +87,7 @@ export interface FileRoutesByFullPath { '/sign-in': typeof SignInRoute '/sign-up': typeof SignUpRoute '/home': typeof ProtectedHomeRoute + '/profile': typeof ProtectedProfileRoute '/rooms': typeof ProtectedRoomsRouteWithChildren '/settings': typeof ProtectedSettingsRoute '/test-api': typeof ProtectedTestApiRoute @@ -93,6 +100,7 @@ export interface FileRoutesByTo { '/sign-in': typeof SignInRoute '/sign-up': typeof SignUpRoute '/home': typeof ProtectedHomeRoute + '/profile': typeof ProtectedProfileRoute '/settings': typeof ProtectedSettingsRoute '/test-api': typeof ProtectedTestApiRoute '/guests/$guestId': typeof ProtectedGuestsGuestIdRoute @@ -106,6 +114,7 @@ export interface FileRoutesById { '/sign-in': typeof SignInRoute '/sign-up': typeof SignUpRoute '/_protected/home': typeof ProtectedHomeRoute + '/_protected/profile': typeof ProtectedProfileRoute '/_protected/rooms': typeof ProtectedRoomsRouteWithChildren '/_protected/settings': typeof ProtectedSettingsRoute '/_protected/test-api': typeof ProtectedTestApiRoute @@ -120,6 +129,7 @@ export interface FileRouteTypes { | '/sign-in' | '/sign-up' | '/home' + | '/profile' | '/rooms' | '/settings' | '/test-api' @@ -132,6 +142,7 @@ export interface FileRouteTypes { | '/sign-in' | '/sign-up' | '/home' + | '/profile' | '/settings' | '/test-api' | '/guests/$guestId' @@ -144,6 +155,7 @@ export interface FileRouteTypes { | '/sign-in' | '/sign-up' | '/_protected/home' + | '/_protected/profile' | '/_protected/rooms' | '/_protected/settings' | '/_protected/test-api' @@ -210,6 +222,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProtectedRoomsRouteImport parentRoute: typeof ProtectedRoute } + '/_protected/profile': { + id: '/_protected/profile' + path: '/profile' + fullPath: '/profile' + preLoaderRoute: typeof ProtectedProfileRouteImport + parentRoute: typeof ProtectedRoute + } '/_protected/home': { id: '/_protected/home' path: '/home' @@ -255,6 +274,7 @@ const ProtectedRoomsRouteWithChildren = ProtectedRoomsRoute._addFileChildren( interface ProtectedRouteChildren { ProtectedHomeRoute: typeof ProtectedHomeRoute + ProtectedProfileRoute: typeof ProtectedProfileRoute ProtectedRoomsRoute: typeof ProtectedRoomsRouteWithChildren ProtectedSettingsRoute: typeof ProtectedSettingsRoute ProtectedTestApiRoute: typeof ProtectedTestApiRoute @@ -264,6 +284,7 @@ interface ProtectedRouteChildren { const ProtectedRouteChildren: ProtectedRouteChildren = { ProtectedHomeRoute: ProtectedHomeRoute, + ProtectedProfileRoute: ProtectedProfileRoute, ProtectedRoomsRoute: ProtectedRoomsRouteWithChildren, ProtectedSettingsRoute: ProtectedSettingsRoute, ProtectedTestApiRoute: ProtectedTestApiRoute, diff --git a/clients/web/src/routes/_protected/profile.tsx b/clients/web/src/routes/_protected/profile.tsx new file mode 100644 index 00000000..4a5a966f --- /dev/null +++ b/clients/web/src/routes/_protected/profile.tsx @@ -0,0 +1,113 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { useUser } from "@clerk/clerk-react"; + +export const Route = createFileRoute("/_protected/profile")({ + component: ProfilePage, +}); + +function DetailRow({ label, value }: { label: string; value: string }) { + return ( +
+

{label}

+

{value}

+
+ ); +} + +function ProfileInfoCard({ + displayName, + email, + phone, +}: { + displayName: string; + email: string; + phone: string; +}) { + return ( +
+
+ + + + +
+
+ ); +} + +function NotesFromManagerCard() { + return ( +
+

+ Notes from Manager +

+
+ ); +} + +function ProfilePage() { + const { user } = useUser(); + + const displayName = + user?.fullName || + [user?.firstName, user?.lastName].filter(Boolean).join(" ") || + "User"; + + const email = user?.primaryEmailAddress?.emailAddress ?? ""; + const phone = user?.primaryPhoneNumber?.phoneNumber ?? ""; + + return ( +
+ {/* Page header */} +
+

Profile

+

+ Overview of profile +

+
+ + {/* Scrollable content */} +
+ {/* User hero */} +
+ {user?.imageUrl ? ( + {displayName} + ) : ( +
+ + {displayName.charAt(0)} + +
+ )} +
+

+ {user?.firstName} +

+

+ {user?.lastName} +

+

Hotel Chain

+
+
+ + {/* Info + Notes row */} +
+
+ +
+
+ +
+
+
+
+ ); +}