uup
This commit is contained in:
209
app/lib/settings-management.server.ts
Normal file
209
app/lib/settings-management.server.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user