Add Swiss franc (CHF) and improve Euro currency support

- Add CHF to currency detection patterns in priceParser
- Add getCurrencySymbol helper in notifications service
- Update all frontend price formatting to support CHF
- Swiss francs display as "CHF 29.99" format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
clucraft 2026-01-22 14:16:01 -05:00
parent c2cec6d4f1
commit 4928d6b9d3
5 changed files with 25 additions and 9 deletions

View file

@ -1,5 +1,17 @@
import axios from 'axios';
// Helper to get currency symbol for display
function getCurrencySymbol(currency?: string): string {
switch (currency) {
case 'EUR': return '€';
case 'GBP': return '£';
case 'CHF': return 'CHF ';
case 'JPY': return '¥';
case 'INR': return '₹';
default: return '$';
}
}
export interface NotificationPayload {
productName: string;
productUrl: string;
@ -12,7 +24,7 @@ export interface NotificationPayload {
}
function formatMessage(payload: NotificationPayload): string {
const currencySymbol = payload.currency === 'EUR' ? '€' : payload.currency === 'GBP' ? '£' : '$';
const currencySymbol = getCurrencySymbol(payload.currency);
if (payload.type === 'price_drop') {
const oldPriceStr = payload.oldPrice ? `${currencySymbol}${payload.oldPrice.toFixed(2)}` : 'N/A';
@ -78,7 +90,7 @@ export async function sendDiscordNotification(
payload: NotificationPayload
): Promise<boolean> {
try {
const currencySymbol = payload.currency === 'EUR' ? '€' : payload.currency === 'GBP' ? '£' : '$';
const currencySymbol = getCurrencySymbol(payload.currency);
let embed;
if (payload.type === 'price_drop') {
@ -145,7 +157,7 @@ export async function sendPushoverNotification(
payload: NotificationPayload
): Promise<boolean> {
try {
const currencySymbol = payload.currency === 'EUR' ? '€' : payload.currency === 'GBP' ? '£' : '$';
const currencySymbol = getCurrencySymbol(payload.currency);
let title: string;
let message: string;

View file

@ -10,6 +10,8 @@ const currencyMap: Record<string, string> = {
'£': 'GBP',
'¥': 'JPY',
'₹': 'INR',
'Fr.': 'CHF',
'CHF': 'CHF',
'CAD': 'CAD',
'AUD': 'AUD',
'USD': 'USD',
@ -21,8 +23,10 @@ const currencyMap: Record<string, string> = {
const pricePatterns = [
// $29.99 or $29,99 or $ 29.99
/(?<currency>[$€£¥₹])\s*(?<price>[\d,]+\.?\d*)/,
// 29.99 USD or 29,99 EUR
/(?<price>[\d,]+\.?\d*)\s*(?<currency>USD|EUR|GBP|CAD|AUD|JPY|INR)/i,
// CHF 29.99 or Fr. 29.99 (Swiss franc prefix)
/(?<currency>CHF|Fr\.)\s*(?<price>[\d,]+\.?\d*)/i,
// 29.99 USD or 29,99 EUR or 29.99 CHF
/(?<price>[\d,]+\.?\d*)\s*(?<currency>USD|EUR|GBP|CAD|AUD|JPY|INR|CHF)/i,
// Plain number with optional decimal (fallback)
/(?<price>\d{1,3}(?:[,.\s]?\d{3})*(?:[.,]\d{2})?)/,
];
@ -89,7 +93,7 @@ export function extractPricesFromText(html: string): ParsedPrice[] {
// Match all price-like patterns in the HTML
const allMatches = html.matchAll(
/(?:[$€£¥₹])\s*[\d,]+\.?\d*|[\d,]+\.?\d*\s*(?:USD|EUR|GBP|CAD|AUD)/gi
/(?:[$€£¥₹])\s*[\d,]+\.?\d*|(?:CHF|Fr\.)\s*[\d,]+\.?\d*|[\d,]+\.?\d*\s*(?:USD|EUR|GBP|CAD|AUD|CHF)/gi
);
for (const match of allMatches) {

View file

@ -59,7 +59,7 @@ export default function PriceChart({
};
const currencySymbol =
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$';
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : currency === 'CHF' ? 'CHF ' : '$';
const chartData = prices.map((p) => ({
date: new Date(p.recorded_at).getTime(),

View file

@ -81,7 +81,7 @@ export default function ProductCard({ product, onDelete, onRefresh, isSelected,
const numPrice = typeof price === 'string' ? parseFloat(price) : price;
if (isNaN(numPrice)) return 'N/A';
const currencySymbol =
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$';
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : currency === 'CHF' ? 'CHF ' : '$';
return `${currencySymbol}${numPrice.toFixed(2)}`;
};

View file

@ -157,7 +157,7 @@ export default function ProductDetail() {
const numPrice = typeof price === 'string' ? parseFloat(price) : price;
if (isNaN(numPrice)) return 'N/A';
const currencySymbol =
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : '$';
currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : currency === 'CHF' ? 'CHF ' : '$';
return `${currencySymbol}${numPrice.toFixed(2)}`;
};