- Implemented FormStep component for user input (name, email, address). - Created OtpStep component for OTP verification. - Developed SuccessStep component to display success messages based on account creation. - Introduced eligibility-check.store for managing state throughout the eligibility check process. - Added commitlint configuration for standardized commit messages. - Configured knip for workspace management and project structure.
98 lines
2.6 KiB
TypeScript
98 lines
2.6 KiB
TypeScript
/**
|
|
* Toolkit - Currency Formatting
|
|
*
|
|
* Our product currently operates in Japanese Yen only, but we keep a single
|
|
* helper so the portal and BFF share the same formatting behaviour.
|
|
*
|
|
* The function still accepts legacy signatures (`formatCurrency(amount, "JPY", "¥")`)
|
|
* so existing call sites remain compatible.
|
|
*/
|
|
|
|
export type SupportedCurrency = "JPY";
|
|
|
|
type LegacyOptions = {
|
|
/**
|
|
* Optional locale override. Defaults to "ja-JP".
|
|
*/
|
|
locale?: string | undefined;
|
|
/**
|
|
* Set to false if you ever want to hide the symbol. Defaults to true.
|
|
*/
|
|
showSymbol?: boolean | undefined;
|
|
/**
|
|
* Optional custom symbol. Defaults to "¥".
|
|
*/
|
|
currencySymbol?: string | undefined;
|
|
};
|
|
|
|
const DEFAULT_CURRENCY: SupportedCurrency = "JPY";
|
|
const DEFAULT_SYMBOL = "¥";
|
|
const DEFAULT_LOCALE = "ja-JP";
|
|
|
|
export const getCurrencyLocale = (_currency?: string): string => DEFAULT_LOCALE;
|
|
|
|
const normalizeOptions = (
|
|
currencyOrOptions?: string | LegacyOptions,
|
|
symbolOrOptions?: string | LegacyOptions
|
|
) => {
|
|
const result: {
|
|
currency: string;
|
|
symbol: string;
|
|
locale: string;
|
|
showSymbol: boolean;
|
|
} = {
|
|
currency: DEFAULT_CURRENCY,
|
|
symbol: DEFAULT_SYMBOL,
|
|
locale: DEFAULT_LOCALE,
|
|
showSymbol: true,
|
|
};
|
|
|
|
const applyOptions = (opts?: LegacyOptions) => {
|
|
if (!opts) return;
|
|
if (opts.locale) result.locale = opts.locale;
|
|
if (typeof opts.showSymbol === "boolean") result.showSymbol = opts.showSymbol;
|
|
if (opts.currencySymbol) result.symbol = opts.currencySymbol;
|
|
};
|
|
|
|
if (typeof currencyOrOptions === "string") {
|
|
result.currency = currencyOrOptions;
|
|
} else {
|
|
applyOptions(currencyOrOptions);
|
|
}
|
|
|
|
if (typeof symbolOrOptions === "string") {
|
|
result.symbol = symbolOrOptions;
|
|
} else {
|
|
applyOptions(symbolOrOptions);
|
|
}
|
|
|
|
// Even if a different currency code is provided, we treat it like JPY for now.
|
|
const fractionDigits = result.currency.toUpperCase() === "JPY" ? 0 : 2;
|
|
|
|
return { ...result, fractionDigits };
|
|
};
|
|
|
|
export function formatCurrency(
|
|
amount: number,
|
|
currencyOrOptions?: string | LegacyOptions,
|
|
symbolOrOptions?: string | LegacyOptions
|
|
): string {
|
|
const { locale, symbol, showSymbol, fractionDigits } = normalizeOptions(
|
|
currencyOrOptions,
|
|
symbolOrOptions
|
|
);
|
|
|
|
const formatted = amount.toLocaleString(locale, {
|
|
minimumFractionDigits: fractionDigits,
|
|
maximumFractionDigits: fractionDigits,
|
|
});
|
|
|
|
return showSymbol ? `${symbol}${formatted}` : formatted;
|
|
}
|
|
|
|
export function parseCurrency(value: string): number | null {
|
|
const cleaned = value.replace(/[¥$€,\s]/g, "");
|
|
const parsed = Number.parseFloat(cleaned);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|