car_mms/app/lib/settings-management.server.ts
2025-09-11 14:22:27 +03:00

209 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { prisma as db } from './db.server';
export interface AppSettings {
dateFormat: 'ar-SA' | 'en-US';
currency: string;
numberFormat: 'ar-SA' | 'en-US';
currencySymbol: string;
dateDisplayFormat: string;
}
export interface SettingItem {
id: number;
key: string;
value: string;
description?: string;
createdDate: Date;
updateDate: Date;
}
// Default settings
const DEFAULT_SETTINGS: AppSettings = {
dateFormat: 'ar-SA',
currency: 'JOD',
numberFormat: 'ar-SA',
currencySymbol: 'د.أ',
dateDisplayFormat: 'dd/MM/yyyy'
};
// Get all settings as a typed object
export async function getAppSettings(): Promise<AppSettings> {
try {
const settings = await db.settings.findMany();
const settingsMap = settings.reduce((acc, setting) => {
acc[setting.key] = setting.value;
return acc;
}, {} as Record<string, string>);
return {
dateFormat: (settingsMap.dateFormat as 'ar-SA' | 'en-US') || DEFAULT_SETTINGS.dateFormat,
currency: settingsMap.currency || DEFAULT_SETTINGS.currency,
numberFormat: (settingsMap.numberFormat as 'ar-SA' | 'en-US') || DEFAULT_SETTINGS.numberFormat,
currencySymbol: settingsMap.currencySymbol || DEFAULT_SETTINGS.currencySymbol,
dateDisplayFormat: settingsMap.dateDisplayFormat || DEFAULT_SETTINGS.dateDisplayFormat
};
} catch (error) {
console.error('Error fetching settings:', error);
return DEFAULT_SETTINGS;
}
}
// Get a specific setting by key
export async function getSetting(key: string): Promise<string | null> {
try {
const setting = await db.settings.findUnique({
where: { key }
});
return setting?.value || null;
} catch (error) {
console.error(`Error fetching setting ${key}:`, error);
return null;
}
}
// Update or create a setting
export async function updateSetting(key: string, value: string, description?: string): Promise<SettingItem> {
try {
return await db.settings.upsert({
where: { key },
update: {
value,
description: description || undefined,
updateDate: new Date()
},
create: {
key,
value,
description: description || undefined
}
});
} catch (error) {
console.error(`Error updating setting ${key}:`, error);
throw new Error(`Failed to update setting: ${key}`);
}
}
// Update multiple settings at once
export async function updateSettings(settings: Partial<AppSettings>): Promise<void> {
try {
const updates = Object.entries(settings).map(([key, value]) =>
updateSetting(key, value as string)
);
await Promise.all(updates);
} catch (error) {
console.error('Error updating settings:', error);
throw new Error('Failed to update settings');
}
}
// Get all settings for admin management
export async function getAllSettings(): Promise<SettingItem[]> {
try {
return await db.settings.findMany({
orderBy: { key: 'asc' }
});
} catch (error) {
console.error('Error fetching all settings:', error);
throw new Error('Failed to fetch settings');
}
}
// Initialize default settings if they don't exist
export async function initializeDefaultSettings(): Promise<void> {
try {
const existingSettings = await db.settings.findMany();
const existingKeys = new Set(existingSettings.map(s => s.key));
const defaultEntries = [
{ key: 'dateFormat', value: 'ar-SA', description: 'Date format locale (ar-SA or en-US)' },
{ key: 'currency', value: 'JOD', description: 'Currency code (JOD, USD, EUR, etc.)' },
{ key: 'numberFormat', value: 'ar-SA', description: 'Number format locale (ar-SA or en-US)' },
{ key: 'currencySymbol', value: 'د.أ', description: 'Currency symbol display' },
{ key: 'dateDisplayFormat', value: 'dd/MM/yyyy', description: 'Date display format pattern' }
];
const newSettings = defaultEntries.filter(entry => !existingKeys.has(entry.key));
if (newSettings.length > 0) {
await db.settings.createMany({
data: newSettings
});
}
} catch (error) {
console.error('Error initializing default settings:', error);
// Don't throw error, just log it and continue with defaults
}
}
// Formatting utilities using settings
export class SettingsFormatter {
constructor(private settings: AppSettings) {}
// Format number according to settings
formatNumber(value: number): string {
return value.toLocaleString(this.settings.numberFormat);
}
// Format currency according to settings
formatCurrency(value: number): string {
const formatted = value.toLocaleString(this.settings.numberFormat, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
return `${formatted} ${this.settings.currencySymbol}`;
}
// Format date according to settings
formatDate(date: Date | string): string {
const dateObj = typeof date === 'string' ? new Date(date) : date;
return this.formatDateWithPattern(dateObj, this.settings.dateDisplayFormat);
}
// Format datetime according to settings
formatDateTime(date: Date | string): string {
const dateObj = typeof date === 'string' ? new Date(date) : date;
const datePart = this.formatDateWithPattern(dateObj, this.settings.dateDisplayFormat);
const timePart = dateObj.toLocaleTimeString(this.settings.dateFormat, {
hour: '2-digit',
minute: '2-digit'
});
return `${datePart} ${timePart}`;
}
// Helper method to format date with custom pattern
private formatDateWithPattern(date: Date, pattern: string): string {
const day = date.getDate();
const month = date.getMonth() + 1;
const year = date.getFullYear();
// Format numbers according to locale
const formatNumber = (num: number, padLength: number = 2): string => {
const padded = num.toString().padStart(padLength, '0');
return this.settings.numberFormat === 'ar-SA'
? this.convertToArabicNumerals(padded)
: padded;
};
return pattern
.replace(/yyyy/g, formatNumber(year, 4))
.replace(/yy/g, formatNumber(year % 100, 2))
.replace(/MM/g, formatNumber(month, 2))
.replace(/M/g, formatNumber(month, 1))
.replace(/dd/g, formatNumber(day, 2))
.replace(/d/g, formatNumber(day, 1));
}
// Helper method to convert Western numerals to Arabic numerals
private convertToArabicNumerals(str: string): string {
const arabicNumerals = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
return str.replace(/[0-9]/g, (digit) => arabicNumerals[parseInt(digit)]);
}
}
// Create formatter instance with current settings
export async function createFormatter(): Promise<SettingsFormatter> {
const settings = await getAppSettings();
return new SettingsFormatter(settings);
}