feat: navigation and UX improvements complete

This commit is contained in:
Eric Gullickson
2025-12-26 09:25:42 -06:00
parent 50baec390f
commit 8c13dc0a55
23 changed files with 327 additions and 126 deletions

View File

@@ -11,8 +11,8 @@ export class UserPreferencesRepository {
async findByUserId(userId: string): Promise<UserPreferences | null> {
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<UserPreferences> {
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,
};

View File

@@ -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.';

View File

@@ -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;
}

View File

@@ -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,
});