update
This commit is contained in:
53
Frontend/src/utils/constants.ts
Normal file
53
Frontend/src/utils/constants.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Application constants
|
||||
*/
|
||||
|
||||
export const API_TIMEOUT = 30000; // 30 seconds
|
||||
export const MAX_RETRIES = 3;
|
||||
export const RETRY_DELAY = 1000; // 1 second
|
||||
|
||||
export const DATE_FORMATS = {
|
||||
SHORT: 'short',
|
||||
MEDIUM: 'medium',
|
||||
LONG: 'long',
|
||||
FULL: 'full',
|
||||
} as const;
|
||||
|
||||
export const CURRENCY = {
|
||||
VND: 'VND',
|
||||
USD: 'USD',
|
||||
EUR: 'EUR',
|
||||
} as const;
|
||||
|
||||
export const STORAGE_KEYS = {
|
||||
TOKEN: 'token',
|
||||
USER_INFO: 'userInfo',
|
||||
GUEST_FAVORITES: 'guestFavorites',
|
||||
THEME: 'theme',
|
||||
LANGUAGE: 'language',
|
||||
} as const;
|
||||
|
||||
export const ROUTES = {
|
||||
HOME: '/',
|
||||
LOGIN: '/login',
|
||||
REGISTER: '/register',
|
||||
DASHBOARD: '/dashboard',
|
||||
ROOMS: '/rooms',
|
||||
BOOKINGS: '/bookings',
|
||||
FAVORITES: '/favorites',
|
||||
PROFILE: '/profile',
|
||||
ADMIN: '/admin',
|
||||
} as const;
|
||||
|
||||
export const TOAST_DURATION = {
|
||||
SUCCESS: 3000,
|
||||
ERROR: 5000,
|
||||
WARNING: 4000,
|
||||
INFO: 3000,
|
||||
} as const;
|
||||
|
||||
export const PAGINATION = {
|
||||
DEFAULT_PAGE_SIZE: 10,
|
||||
PAGE_SIZE_OPTIONS: [10, 20, 50, 100],
|
||||
} as const;
|
||||
|
||||
162
Frontend/src/utils/format.ts
Normal file
162
Frontend/src/utils/format.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Utility functions for formatting data
|
||||
*/
|
||||
|
||||
/**
|
||||
* Format currency
|
||||
*/
|
||||
export const formatCurrency = (
|
||||
amount: number | string,
|
||||
currency: string = 'VND'
|
||||
): string => {
|
||||
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
|
||||
|
||||
if (isNaN(numAmount)) return '0 ₫';
|
||||
|
||||
if (currency === 'VND') {
|
||||
return new Intl.NumberFormat('vi-VN', {
|
||||
style: 'currency',
|
||||
currency: 'VND',
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
}).format(numAmount);
|
||||
}
|
||||
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currency,
|
||||
}).format(numAmount);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format date
|
||||
*/
|
||||
export const formatDate = (
|
||||
date: string | Date,
|
||||
format: 'short' | 'medium' | 'long' | 'full' = 'medium'
|
||||
): string => {
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
|
||||
if (isNaN(dateObj.getTime())) return 'Invalid Date';
|
||||
|
||||
const formatOptions: Record<string, Intl.DateTimeFormatOptions> = {
|
||||
short: { year: 'numeric', month: 'numeric', day: 'numeric' },
|
||||
medium: { year: 'numeric', month: 'short', day: 'numeric' },
|
||||
long: { year: 'numeric', month: 'long', day: 'numeric' },
|
||||
full: {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'long'
|
||||
},
|
||||
};
|
||||
|
||||
return new Intl.DateTimeFormat('en-US', formatOptions[format]).format(dateObj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format date and time
|
||||
*/
|
||||
export const formatDateTime = (
|
||||
date: string | Date,
|
||||
includeSeconds: boolean = false
|
||||
): string => {
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
|
||||
if (isNaN(dateObj.getTime())) return 'Invalid Date';
|
||||
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
...(includeSeconds && { second: '2-digit' }),
|
||||
}).format(dateObj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format relative time (e.g., "2 hours ago")
|
||||
*/
|
||||
export const formatRelativeTime = (date: string | Date): string => {
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
const now = new Date();
|
||||
const diffInSeconds = Math.floor((now.getTime() - dateObj.getTime()) / 1000);
|
||||
|
||||
if (diffInSeconds < 0) return 'in the future';
|
||||
if (diffInSeconds < 60) return 'just now';
|
||||
if (diffInSeconds < 3600) {
|
||||
const minutes = Math.floor(diffInSeconds / 60);
|
||||
return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
||||
}
|
||||
if (diffInSeconds < 86400) {
|
||||
const hours = Math.floor(diffInSeconds / 3600);
|
||||
return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
||||
}
|
||||
if (diffInSeconds < 604800) {
|
||||
const days = Math.floor(diffInSeconds / 86400);
|
||||
return `${days} day${days > 1 ? 's' : ''} ago`;
|
||||
}
|
||||
|
||||
return formatDate(dateObj, 'short');
|
||||
};
|
||||
|
||||
/**
|
||||
* Format number
|
||||
*/
|
||||
export const formatNumber = (
|
||||
num: number | string,
|
||||
decimals: number = 0
|
||||
): string => {
|
||||
const numValue = typeof num === 'string' ? parseFloat(num) : num;
|
||||
|
||||
if (isNaN(numValue)) return '0';
|
||||
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
minimumFractionDigits: decimals,
|
||||
maximumFractionDigits: decimals,
|
||||
}).format(numValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format phone number
|
||||
*/
|
||||
export const formatPhoneNumber = (phone: string): string => {
|
||||
const cleaned = phone.replace(/\D/g, '');
|
||||
|
||||
if (cleaned.length === 10) {
|
||||
return cleaned.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
|
||||
}
|
||||
|
||||
if (cleaned.length === 11) {
|
||||
return cleaned.replace(/(\d{1})(\d{3})(\d{3})(\d{4})/, '+$1 ($2) $3-$4');
|
||||
}
|
||||
|
||||
return phone;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format file size
|
||||
*/
|
||||
export const formatFileSize = (bytes: number): string => {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Truncate text
|
||||
*/
|
||||
export const truncateText = (
|
||||
text: string,
|
||||
maxLength: number,
|
||||
suffix: string = '...'
|
||||
): string => {
|
||||
if (text.length <= maxLength) return text;
|
||||
return text.slice(0, maxLength - suffix.length) + suffix;
|
||||
};
|
||||
|
||||
4
Frontend/src/utils/index.ts
Normal file
4
Frontend/src/utils/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './format';
|
||||
export * from './constants';
|
||||
export * from './validationSchemas';
|
||||
|
||||
Reference in New Issue
Block a user