diff --git a/backend/src/core/user-preferences/data/user-preferences.repository.ts b/backend/src/core/user-preferences/data/user-preferences.repository.ts index 03642b1..1a24eb8 100644 --- a/backend/src/core/user-preferences/data/user-preferences.repository.ts +++ b/backend/src/core/user-preferences/data/user-preferences.repository.ts @@ -11,8 +11,8 @@ export class UserPreferencesRepository { async findByUserId(userId: string): Promise { const query = ` - SELECT id, user_id, unit_system, currency_code, time_zone, created_at, updated_at - FROM user_preferences + SELECT id, user_id, unit_system, currency_code, time_zone, dark_mode, created_at, updated_at + FROM user_preferences WHERE user_id = $1 `; @@ -22,8 +22,8 @@ export class UserPreferencesRepository { async create(data: CreateUserPreferencesRequest): Promise { const query = ` - INSERT INTO user_preferences (user_id, unit_system, currency_code, time_zone) - VALUES ($1, $2, $3, $4) + INSERT INTO user_preferences (user_id, unit_system, currency_code, time_zone, dark_mode) + VALUES ($1, $2, $3, $4, $5) RETURNING * `; @@ -31,7 +31,8 @@ export class UserPreferencesRepository { data.userId, data.unitSystem || 'imperial', data.currencyCode || 'USD', - data.timeZone || 'UTC' + data.timeZone || 'UTC', + data.darkMode ?? null ]; const result = await this.db.query(query, values); @@ -55,6 +56,10 @@ export class UserPreferencesRepository { fields.push(`time_zone = $${paramCount++}`); values.push(data.timeZone); } + if (data.darkMode !== undefined) { + fields.push(`dark_mode = $${paramCount++}`); + values.push(data.darkMode); + } if (fields.length === 0) { return this.findByUserId(userId); @@ -90,6 +95,7 @@ export class UserPreferencesRepository { unitSystem: row.unit_system, currencyCode: row.currency_code || 'USD', timeZone: row.time_zone || 'UTC', + darkMode: row.dark_mode, createdAt: row.created_at, updatedAt: row.updated_at, }; diff --git a/backend/src/core/user-preferences/migrations/003_add_dark_mode.sql b/backend/src/core/user-preferences/migrations/003_add_dark_mode.sql new file mode 100644 index 0000000..2c89c01 --- /dev/null +++ b/backend/src/core/user-preferences/migrations/003_add_dark_mode.sql @@ -0,0 +1,8 @@ +-- Migration: Add dark_mode column to user_preferences +-- NULL = use system preference, TRUE = dark mode, FALSE = light mode + +ALTER TABLE user_preferences + ADD COLUMN IF NOT EXISTS dark_mode BOOLEAN DEFAULT NULL; + +-- Add comment for documentation +COMMENT ON COLUMN user_preferences.dark_mode IS 'User dark mode preference. NULL means use system preference.'; diff --git a/backend/src/core/user-preferences/user-preferences.types.ts b/backend/src/core/user-preferences/user-preferences.types.ts index c35544c..2cd572c 100644 --- a/backend/src/core/user-preferences/user-preferences.types.ts +++ b/backend/src/core/user-preferences/user-preferences.types.ts @@ -11,6 +11,7 @@ export interface UserPreferences { unitSystem: UnitSystem; currencyCode: string; timeZone: string; + darkMode: boolean | null; createdAt: Date; updatedAt: Date; } @@ -20,12 +21,14 @@ export interface CreateUserPreferencesRequest { unitSystem?: UnitSystem; currencyCode?: string; timeZone?: string; + darkMode?: boolean | null; } export interface UpdateUserPreferencesRequest { unitSystem?: UnitSystem; currencyCode?: string; timeZone?: string; + darkMode?: boolean | null; } export interface UserPreferencesResponse { @@ -34,6 +37,7 @@ export interface UserPreferencesResponse { unitSystem: UnitSystem; currencyCode: string; timeZone: string; + darkMode: boolean | null; createdAt: string; updatedAt: string; } diff --git a/backend/src/features/user-preferences/api/user-preferences.controller.ts b/backend/src/features/user-preferences/api/user-preferences.controller.ts index 870370f..7003b2e 100644 --- a/backend/src/features/user-preferences/api/user-preferences.controller.ts +++ b/backend/src/features/user-preferences/api/user-preferences.controller.ts @@ -37,6 +37,7 @@ export class UserPreferencesController { unitSystem: preferences.unitSystem, currencyCode: preferences.currencyCode, timeZone: preferences.timeZone, + darkMode: preferences.darkMode, createdAt: preferences.createdAt, updatedAt: preferences.updatedAt, }); @@ -55,7 +56,7 @@ export class UserPreferencesController { ) { try { const userId = (request as any).user.sub; - const { unitSystem, currencyCode, timeZone } = request.body; + const { unitSystem, currencyCode, timeZone, darkMode } = request.body; // Validate unitSystem if provided if (unitSystem && !['imperial', 'metric'].includes(unitSystem)) { @@ -73,6 +74,14 @@ export class UserPreferencesController { }); } + // Validate darkMode if provided (must be boolean or null) + if (darkMode !== undefined && darkMode !== null && typeof darkMode !== 'boolean') { + return reply.code(400).send({ + error: 'Bad Request', + message: 'darkMode must be a boolean or null', + }); + } + // Check if preferences exist, create if not let preferences = await this.repository.findByUserId(userId); if (!preferences) { @@ -81,12 +90,14 @@ export class UserPreferencesController { unitSystem: unitSystem || 'imperial', currencyCode: currencyCode || 'USD', timeZone: timeZone || 'UTC', + darkMode: darkMode, }); } else { const updated = await this.repository.update(userId, { unitSystem, currencyCode, timeZone, + darkMode, }); if (updated) { preferences = updated; @@ -99,6 +110,7 @@ export class UserPreferencesController { unitSystem: preferences.unitSystem, currencyCode: preferences.currencyCode, timeZone: preferences.timeZone, + darkMode: preferences.darkMode, createdAt: preferences.createdAt, updatedAt: preferences.updatedAt, }); diff --git a/docs/PROMPTS.md b/docs/PROMPTS.md index b488bd4..a089151 100644 --- a/docs/PROMPTS.md +++ b/docs/PROMPTS.md @@ -22,18 +22,15 @@ You are a senior software engineer specializsing in NodeJS, Typescript, front en - Make no assumptions. - Ask clarifying questions. - Ultrathink -- You will be extending the "Documents" feature to include manuals. +- You will be auditing the Dark vs Light theme implementation *** CONTEXT *** - This is a modern web app for managing a vehicle fleet. It has both a desktop and mobile versions of the site that both need to maintain feature parity. It's currently deployed via docker compose but in the future will be deployed via k8s. - Read README.md CLAUDE.md and AI-INDEX.md and follow relevant instructions to understand this code repository in the context of this change. -- You need to extend the Documents feature to include a third "Document Type" -- Right now the document has two types. Insurance and Registration -- The third type will be called "Manual" -- This document will just have the uploaded file and a notes field and Title field -- When implementing this we need to play for the future feature of scanning the document for maintenance schedules -- Add a toggle for this scanning. Label it "Scan for Maintenance Schedule" -- Do not implement this feature at this time but put the toggle in the interface and the backend changes to facility this workflow. +- You need to audit the Dark vs Light theme. +- The colors were not all changed so some of the dark theme +- The dark versus light theme does not save between logins. +- Think hard about the color choices and if any better colors are available form the MVP-COLOR-SCHEME.md *** CHANGES TO IMPLEMENT *** - Research this code base and ask iterative questions to compile a complete plan. @@ -58,30 +55,7 @@ You are a senior software engineer specializsing in NodeJS, Typescript, front en - Debug what could be causing this issue. No changes were made to the Gitlab server besides adding the RESEND variable so there shouldn't be anything on the server causing this issue. $ chmod +x scripts/inject-secrets.sh $ ./scripts/inject-secrets.sh -Injecting secrets... - Deploy path: /opt/gitlab-runner/builds/motovaultpro - Secrets dir: /opt/gitlab-runner/builds/motovaultpro/secrets/app -Cleaning up any corrupted secret paths... - ERROR: Variable POSTGRES_PASSWORD is not set - Ensure it exists in GitLab CI/CD Variables - ERROR: Variable AUTH0_CLIENT_SECRET is not set - Ensure it exists in GitLab CI/CD Variables - ERROR: Variable AUTH0_MANAGEMENT_CLIENT_ID is not set - Ensure it exists in GitLab CI/CD Variables - ERROR: Variable AUTH0_MANAGEMENT_CLIENT_SECRET is not set - Ensure it exists in GitLab CI/CD Variables - ERROR: Variable GOOGLE_MAPS_API_KEY is not set - Ensure it exists in GitLab CI/CD Variables - ERROR: Variable GOOGLE_MAPS_MAP_ID is not set - Ensure it exists in GitLab CI/CD Variables - ERROR: Variable CF_DNS_API_TOKEN is not set - Ensure it exists in GitLab CI/CD Variables - ERROR: Variable RESEND_API_KEY is not set - Ensure it exists in GitLab CI/CD Variables -ERROR: One or more secrets failed to inject -Ensure all required CI/CD variables are configured as File type in GitLab -Running after_script -00:00 + Running after script... @@ -89,6 +63,35 @@ Running after script... +*** ROLE *** +- You are a senior DBA with expert knowledge in Postgres SQL. + +*** ACTION *** +- Make no assumptions. +- Ask clarifying questions. +- Ultrathink +- You will be implementing an ETL process that takes a export of the NHTSA vPIC database in Postgres and transforming it for use in this application. + +*** CONTEXT *** +- This is a modern web app for managing a vehicle fleet. It has both a desktop and mobile versions of the site that both need to maintain feature parity. It's currently deployed via docker compose but in the future will be deployed via k8s. +- Read README.md CLAUDE.md and AI-INDEX.md and follow relevant instructions to understand this code repository in the context of this change. +- There is an existing database import process in this directory. This process works and should not be changed. +- The source database from the NHTSA vPIC dataset is located in the @vpic-source directory +- Deep research needs to be conducted on how to execute this ETL process. +- The source database is designed for VIN decoding only. +- Example VIN: 2025 Honda Civic Hybrid - 2HGFE4F88SH315466 +- Example VIN: 2023 GMC Sierra 1500 AT4x - 3GTUUFEL6PG140748 +- Example VIN: 2017 Chevrolet Corvette Z06 - 1G1YU3D64H5602799 + +*** CHANGES TO IMPLEMENT *** +- Research this code base and ask iterative questions to compile a complete plan. +- generate a project plan +- break into bite-sized tasks and milestones + + + + + *** ROLE *** - You are a senior DBA with expert knowledge in Postgres SQL. diff --git a/DARK-THEME-PLAN.md b/docs/changes/DARK-THEME-PLAN.md similarity index 100% rename from DARK-THEME-PLAN.md rename to docs/changes/DARK-THEME-PLAN.md diff --git a/ONBOARDING-FIX.md b/docs/changes/ONBOARDING-FIX.md similarity index 100% rename from ONBOARDING-FIX.md rename to docs/changes/ONBOARDING-FIX.md diff --git a/docs/admin-implementation-plan.md b/docs/changes/admin-implementation-plan.md similarity index 100% rename from docs/admin-implementation-plan.md rename to docs/changes/admin-implementation-plan.md diff --git a/docs/admin-settings-frontend-plan.md b/docs/changes/admin-settings-frontend-plan.md similarity index 100% rename from docs/admin-settings-frontend-plan.md rename to docs/changes/admin-settings-frontend-plan.md diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 472809f..3e443de 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -17,6 +17,7 @@ import CloseIcon from '@mui/icons-material/Close'; import { useAppStore } from '../core/store'; import { Button } from '../shared-minimal/components/Button'; import { NotificationBell } from '../features/notifications'; +import { useThemeSync } from '../shared-minimal/theme/useThemeSync'; interface LayoutProps { children: React.ReactNode; @@ -28,6 +29,9 @@ export const Layout: React.FC = ({ children, mobileMode = false }) const { sidebarOpen, toggleSidebar, setSidebarOpen } = useAppStore(); const location = useLocation(); + // Sync theme preference with backend + useThemeSync(); + // Ensure desktop has a visible navigation by default (only on mount) React.useEffect(() => { if (!mobileMode && !sidebarOpen) { @@ -72,7 +76,7 @@ export const Layout: React.FC = ({ children, mobileMode = false }) />
-
v1.0
+
v1.0
@@ -157,7 +161,7 @@ export const Layout: React.FC = ({ children, mobileMode = false }) ? 'primary.main' : 'transparent', color: isActive - ? '#FFFFFF' + ? 'primary.contrastText' : 'text.primary', '&:hover': { backgroundColor: isActive diff --git a/frontend/src/features/auth/components/SignupForm.tsx b/frontend/src/features/auth/components/SignupForm.tsx index 86b118d..2eb1696 100644 --- a/frontend/src/features/auth/components/SignupForm.tsx +++ b/frontend/src/features/auth/components/SignupForm.tsx @@ -49,38 +49,38 @@ export const SignupForm: React.FC = ({ onSubmit, loading }) => return (
-
-
-
diff --git a/frontend/src/features/auth/pages/SignupPage.tsx b/frontend/src/features/auth/pages/SignupPage.tsx index 14ab4c9..6812dfa 100644 --- a/frontend/src/features/auth/pages/SignupPage.tsx +++ b/frontend/src/features/auth/pages/SignupPage.tsx @@ -25,24 +25,28 @@ export const SignupPage: React.FC = () => { }; return ( -
+
-
+
-

MotoVaultPro

-

Create Your Account

-

+ MotoVaultPro - Precision Vehicle Management +

Create Your Account

+

Start tracking your vehicle maintenance and fuel logs

-
+
Already have an account?{' '} diff --git a/frontend/src/features/documents/components/DocumentForm.tsx b/frontend/src/features/documents/components/DocumentForm.tsx index 252f8aa..6351bb4 100644 --- a/frontend/src/features/documents/components/DocumentForm.tsx +++ b/frontend/src/features/documents/components/DocumentForm.tsx @@ -158,7 +158,7 @@ export const DocumentForm: React.FC = ({ onSuccess, onCancel
- + = ({ onSuccess, onCancel
- + = ({ onSuccess, onCancel {documentType === 'insurance' && ( <>
- + = ({ onSuccess, onCancel />
- + = ({ onSuccess, onCancel
- + = ({ onSuccess, onCancel />
- + = ({ onSuccess, onCancel
- + = ({ onSuccess, onCancel />
- + = ({ onSuccess, onCancel {documentType === 'registration' && ( <>
- + = ({ onSuccess, onCancel />
- + = ({ onSuccess, onCancel onChange={(e) => setScanForMaintenance(e.target.checked)} className="w-5 h-5 rounded border-slate-300 text-primary-600 focus:ring-primary-500 dark:border-silverstone dark:focus:ring-abudhabi" /> - + Scan for Maintenance Schedule - (Coming soon) + (Coming soon)
)}
- +