This commit is contained in:
2026-01-23 20:35:40 +03:00
parent cf3b0e48ec
commit 66c151653e
137 changed files with 41495 additions and 0 deletions

View 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);
}