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 (
+
+ );
+}
+
+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.charAt(0)}
+
+
+ )}
+
+
+ {user?.firstName}
+
+
+ {user?.lastName}
+
+
Hotel Chain
+
+
+
+ {/* Info + Notes row */}
+
+
+
+ );
+}