refactor: code efficiency improvements (Issues #9-13, #15, #17-20)
Backend: - Extract shared hard-delete authorization utility (#9) - Extract Prisma include constants per entity (#11) - Fix N+1 query pattern in events findAll (#12) - Extract shared date utility functions (#13) - Move vehicle utilization filtering to DB query (#15) - Add ParseBooleanPipe for query params - Add CurrentDriver decorator + ResolveDriverInterceptor (#20) Frontend: - Extract shared form utilities (toDatetimeLocal) and enum labels (#17) - Replace browser confirm() with styled ConfirmModal (#18) - Add centralized query-keys.ts constants (#19) - Clean up unused imports, add useMemo where needed (#19) - Standardize filter button styling across list pages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
41
frontend/src/lib/enum-labels.ts
Normal file
41
frontend/src/lib/enum-labels.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Enum Display Labels
|
||||
* Centralized mapping of enum values to human-readable labels
|
||||
*/
|
||||
|
||||
export const DEPARTMENT_LABELS: Record<string, string> = {
|
||||
OFFICE_OF_DEVELOPMENT: 'Office of Development',
|
||||
ADMIN: 'Admin',
|
||||
OTHER: 'Other',
|
||||
};
|
||||
|
||||
export const ARRIVAL_MODE_LABELS: Record<string, string> = {
|
||||
FLIGHT: 'Flight',
|
||||
SELF_DRIVING: 'Self Driving',
|
||||
};
|
||||
|
||||
export const EVENT_TYPE_LABELS: Record<string, string> = {
|
||||
TRANSPORT: 'Transport',
|
||||
MEETING: 'Meeting',
|
||||
EVENT: 'Event',
|
||||
MEAL: 'Meal',
|
||||
ACCOMMODATION: 'Accommodation',
|
||||
};
|
||||
|
||||
export const EVENT_STATUS_LABELS: Record<string, string> = {
|
||||
SCHEDULED: 'Scheduled',
|
||||
IN_PROGRESS: 'In Progress',
|
||||
COMPLETED: 'Completed',
|
||||
CANCELLED: 'Cancelled',
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to get a label for any enum value
|
||||
* Falls back to the value itself if no mapping is found
|
||||
*/
|
||||
export function getEnumLabel(
|
||||
value: string,
|
||||
labels: Record<string, string>
|
||||
): string {
|
||||
return labels[value] || value;
|
||||
}
|
||||
93
frontend/src/lib/query-keys.ts
Normal file
93
frontend/src/lib/query-keys.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Query key factory constants for TanStack Query
|
||||
*
|
||||
* This file provides typed, centralized query key management for all entities.
|
||||
* Using factory functions ensures consistent keys across the application.
|
||||
*
|
||||
* @see https://tkdodo.eu/blog/effective-react-query-keys
|
||||
*/
|
||||
|
||||
export const queryKeys = {
|
||||
// VIPs
|
||||
vips: {
|
||||
all: ['vips'] as const,
|
||||
detail: (id: string) => ['vip', id] as const,
|
||||
forSchedule: (vipIds: string) => ['vips-for-schedule', vipIds] as const,
|
||||
},
|
||||
|
||||
// Drivers
|
||||
drivers: {
|
||||
all: ['drivers'] as const,
|
||||
myProfile: ['my-driver-profile'] as const,
|
||||
schedule: (driverId: string, date: string) => ['driver-schedule', driverId, date] as const,
|
||||
},
|
||||
|
||||
// Events/Schedule
|
||||
events: {
|
||||
all: ['events'] as const,
|
||||
},
|
||||
|
||||
// Vehicles
|
||||
vehicles: {
|
||||
all: ['vehicles'] as const,
|
||||
},
|
||||
|
||||
// Flights
|
||||
flights: {
|
||||
all: ['flights'] as const,
|
||||
budget: ['flights', 'budget'] as const,
|
||||
},
|
||||
|
||||
// Users
|
||||
users: {
|
||||
all: ['users'] as const,
|
||||
},
|
||||
|
||||
// GPS/Location Tracking
|
||||
gps: {
|
||||
status: ['gps', 'status'] as const,
|
||||
settings: ['gps', 'settings'] as const,
|
||||
devices: ['gps', 'devices'] as const,
|
||||
deviceQr: (driverId: string) => ['gps', 'devices', driverId, 'qr'] as const,
|
||||
locations: {
|
||||
all: ['gps', 'locations'] as const,
|
||||
detail: (driverId: string) => ['gps', 'locations', driverId] as const,
|
||||
},
|
||||
stats: (driverId: string, from?: string, to?: string) =>
|
||||
['gps', 'stats', driverId, from, to] as const,
|
||||
me: {
|
||||
status: ['gps', 'me'] as const,
|
||||
stats: (from?: string, to?: string) => ['gps', 'me', 'stats', from, to] as const,
|
||||
location: ['gps', 'me', 'location'] as const,
|
||||
},
|
||||
traccar: {
|
||||
status: ['gps', 'traccar', 'status'] as const,
|
||||
adminUrl: ['gps', 'traccar', 'admin-url'] as const,
|
||||
},
|
||||
},
|
||||
|
||||
// Settings
|
||||
settings: {
|
||||
pdf: ['settings', 'pdf'] as const,
|
||||
timezone: ['settings', 'timezone'] as const,
|
||||
},
|
||||
|
||||
// Signal Messages
|
||||
signal: {
|
||||
messages: (driverId: string) => ['signal-messages', driverId] as const,
|
||||
unreadCounts: ['signal-unread-counts'] as const,
|
||||
driverResponses: (eventIds: string) => ['signal-driver-responses', eventIds] as const,
|
||||
status: ['signal-status'] as const,
|
||||
messageStats: ['signal-message-stats'] as const,
|
||||
},
|
||||
|
||||
// Admin
|
||||
admin: {
|
||||
stats: ['admin-stats'] as const,
|
||||
},
|
||||
|
||||
// Features
|
||||
features: {
|
||||
all: ['features'] as const,
|
||||
},
|
||||
} as const;
|
||||
@@ -35,3 +35,18 @@ export function formatTime(date: string | Date, timeZone?: string): string {
|
||||
...(timeZone && { timeZone }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert ISO datetime string to datetime-local input format (YYYY-MM-DDTHH:mm)
|
||||
* Used for populating datetime-local inputs in forms
|
||||
*/
|
||||
export function toDatetimeLocal(isoString: string | null | undefined): string {
|
||||
if (!isoString) return '';
|
||||
const date = new Date(isoString);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user