diff --git a/client/src/components/Budget/costsCategories.test.ts b/client/src/components/Budget/costsCategories.test.ts new file mode 100644 index 000000000..d316d6430 --- /dev/null +++ b/client/src/components/Budget/costsCategories.test.ts @@ -0,0 +1,34 @@ +import { describe, it, expect } from 'vitest' +import { COST_CATEGORIES } from '@trek/shared' +import { COST_CAT_META, COST_CATEGORY_LIST, catMeta } from './costsCategories' + +describe('COST_CAT_META', () => { + it('has metadata for every fixed category, including fuel and parkings', () => { + for (const key of COST_CATEGORIES) { + expect(COST_CAT_META[key]).toBeDefined() + expect(COST_CAT_META[key].key).toBe(key) + } + }) + + it('derives COST_CATEGORY_LIST in the same order as COST_CATEGORIES', () => { + expect(COST_CATEGORY_LIST.map(m => m.key)).toEqual([...COST_CATEGORIES]) + }) +}) + +describe('catMeta', () => { + it('resolves fuel and parkings directly', () => { + expect(catMeta('fuel').key).toBe('fuel') + expect(catMeta('parkings').key).toBe('parkings') + }) + + it('maps legacy/free-text aliases to the new categories', () => { + expect(catMeta('gas').key).toBe('fuel') + expect(catMeta('petrol').key).toBe('fuel') + expect(catMeta('parking').key).toBe('parkings') + }) + + it('falls back to other for unknown categories', () => { + expect(catMeta('made-up-category').key).toBe('other') + expect(catMeta(null).key).toBe('other') + }) +}) diff --git a/client/src/components/Budget/costsCategories.tsx b/client/src/components/Budget/costsCategories.tsx index be649f854..be1502e0b 100644 --- a/client/src/components/Budget/costsCategories.tsx +++ b/client/src/components/Budget/costsCategories.tsx @@ -1,4 +1,4 @@ -import { Hotel, Utensils, ShoppingCart, Bus, Plane, Ticket, Camera, ShoppingBag, FileText, HeartPulse, Coins, MoreHorizontal } from 'lucide-react' +import { Hotel, Utensils, ShoppingCart, Bus, Plane, Ticket, Camera, ShoppingBag, FileText, HeartPulse, Coins, MoreHorizontal, Fuel, ParkingCircle } from 'lucide-react' import type { LucideIcon } from 'lucide-react' import { COST_CATEGORIES, type CostCategory } from '@trek/shared' @@ -27,6 +27,8 @@ export const COST_CAT_META: Record = { fees: { key: 'fees', labelKey: 'costs.cat.fees', Icon: FileText, color: '#475569' }, health: { key: 'health', labelKey: 'costs.cat.health', Icon: HeartPulse, color: '#dc2626' }, tips: { key: 'tips', labelKey: 'costs.cat.tips', Icon: Coins, color: '#d97706' }, + fuel: { key: 'fuel', labelKey: 'costs.cat.fuel', Icon: Fuel, color: '#f59e0b' }, + parkings: { key: 'parkings', labelKey: 'costs.cat.parkings', Icon: ParkingCircle, color: '#3b82f6' }, other: { key: 'other', labelKey: 'costs.cat.other', Icon: MoreHorizontal, color: '#6b7280' }, } @@ -51,6 +53,8 @@ const LEGACY_CATEGORY_MAP: Record = { fee: 'fees', fees: 'fees', health: 'health', medical: 'health', tip: 'tips', tips: 'tips', + gas: 'fuel', fuel: 'fuel', petrol: 'fuel', + parking: 'parkings', parkings: 'parkings', other: 'other', misc: 'other', } diff --git a/shared/src/budget/budget.schema.spec.ts b/shared/src/budget/budget.schema.spec.ts index 81230d49c..aa2b9efc8 100644 --- a/shared/src/budget/budget.schema.spec.ts +++ b/shared/src/budget/budget.schema.spec.ts @@ -3,6 +3,7 @@ import { budgetUpdateMembersRequestSchema, budgetToggleMemberPaidRequestSchema, budgetReorderItemsRequestSchema, + COST_CATEGORIES, } from './budget.schema'; import { describe, it, expect } from 'vitest'; @@ -41,3 +42,10 @@ describe('budgetReorderItemsRequestSchema', () => { expect(budgetReorderItemsRequestSchema.safeParse({ orderedIds: ['a'] }).success).toBe(false); }); }); + +describe('COST_CATEGORIES', () => { + it('includes fuel and parkings alongside the existing fixed categories', () => { + expect(COST_CATEGORIES).toContain('fuel'); + expect(COST_CATEGORIES).toContain('parkings'); + }); +}); diff --git a/shared/src/budget/budget.schema.ts b/shared/src/budget/budget.schema.ts index e2355a3d7..d3f0490fe 100644 --- a/shared/src/budget/budget.schema.ts +++ b/shared/src/budget/budget.schema.ts @@ -46,6 +46,8 @@ export const COST_CATEGORIES = [ 'fees', 'health', 'tips', + 'fuel', + 'parkings', 'other', ] as const; export type CostCategory = (typeof COST_CATEGORIES)[number]; diff --git a/shared/src/i18n/ar/budget.ts b/shared/src/i18n/ar/budget.ts index 11dab6473..b0756d897 100644 --- a/shared/src/i18n/ar/budget.ts +++ b/shared/src/i18n/ar/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'الرسوم والتذاكر', 'costs.cat.health': 'الصحة', 'costs.cat.tips': 'البقشيش', + 'costs.cat.fuel': 'وقود', + 'costs.cat.parkings': 'موقف سيارات', 'costs.cat.other': 'أخرى', 'costs.daysCount': '{count} أيام', 'costs.travelers': '{count} مسافرين', diff --git a/shared/src/i18n/br/budget.ts b/shared/src/i18n/br/budget.ts index 1a7975211..f8b0b94e6 100644 --- a/shared/src/i18n/br/budget.ts +++ b/shared/src/i18n/br/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Taxas e ingressos', 'costs.cat.health': 'Saúde', 'costs.cat.tips': 'Gorjetas', + 'costs.cat.fuel': 'Combustível', + 'costs.cat.parkings': 'Estacionamento', 'costs.cat.other': 'Outros', 'costs.daysCount': '{count} dias', 'costs.travelers': '{count} viajantes', diff --git a/shared/src/i18n/ca/budget.ts b/shared/src/i18n/ca/budget.ts index 7f9bd4d5c..53cd10bc0 100644 --- a/shared/src/i18n/ca/budget.ts +++ b/shared/src/i18n/ca/budget.ts @@ -116,6 +116,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Taxes i entrades', 'costs.cat.health': 'Salut', 'costs.cat.tips': 'Propines', + 'costs.cat.fuel': 'Combustible', + 'costs.cat.parkings': 'Aparcament', 'costs.cat.other': 'Altres', 'costs.daysCount': '{count} dies', 'costs.travelers': '{count} viatgers', diff --git a/shared/src/i18n/cs/budget.ts b/shared/src/i18n/cs/budget.ts index b19827bf9..8e994a267 100644 --- a/shared/src/i18n/cs/budget.ts +++ b/shared/src/i18n/cs/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Poplatky a vstupenky', 'costs.cat.health': 'Zdraví', 'costs.cat.tips': 'Spropitné', + 'costs.cat.fuel': 'Palivo', + 'costs.cat.parkings': 'Parkování', 'costs.cat.other': 'Ostatní', 'costs.daysCount': '{count} dní', 'costs.travelers': '{count} cestovatelů', diff --git a/shared/src/i18n/de/budget.ts b/shared/src/i18n/de/budget.ts index 19f8750df..70ceb7fee 100644 --- a/shared/src/i18n/de/budget.ts +++ b/shared/src/i18n/de/budget.ts @@ -120,6 +120,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Gebühren & Tickets', 'costs.cat.health': 'Gesundheit', 'costs.cat.tips': 'Trinkgeld', + 'costs.cat.fuel': 'Kraftstoff', + 'costs.cat.parkings': 'Parken', 'costs.cat.other': 'Sonstiges', 'costs.daysCount': '{count} Tage', 'costs.travelers': '{count} Reisende', diff --git a/shared/src/i18n/en/budget.ts b/shared/src/i18n/en/budget.ts index 4b2d6cd0b..6e3d6103a 100644 --- a/shared/src/i18n/en/budget.ts +++ b/shared/src/i18n/en/budget.ts @@ -124,6 +124,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Fees & tickets', 'costs.cat.health': 'Health', 'costs.cat.tips': 'Tips', + 'costs.cat.fuel': 'Fuel', + 'costs.cat.parkings': 'Parking', 'costs.cat.other': 'Other', 'costs.daysCount': '{count} days', 'costs.travelers': '{count} travelers', diff --git a/shared/src/i18n/es/budget.ts b/shared/src/i18n/es/budget.ts index b5c2daa48..fd536e435 100644 --- a/shared/src/i18n/es/budget.ts +++ b/shared/src/i18n/es/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Tasas y entradas', 'costs.cat.health': 'Salud', 'costs.cat.tips': 'Propinas', + 'costs.cat.fuel': 'Combustible', + 'costs.cat.parkings': 'Aparcamiento', 'costs.cat.other': 'Otros', 'costs.daysCount': '{count} días', 'costs.travelers': '{count} viajeros', diff --git a/shared/src/i18n/fr/budget.ts b/shared/src/i18n/fr/budget.ts index 6826df2dc..83b6b6183 100644 --- a/shared/src/i18n/fr/budget.ts +++ b/shared/src/i18n/fr/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Frais et billets', 'costs.cat.health': 'Santé', 'costs.cat.tips': 'Pourboires', + 'costs.cat.fuel': 'Carburant', + 'costs.cat.parkings': 'Parking', 'costs.cat.other': 'Autre', 'costs.daysCount': '{count} jours', 'costs.travelers': '{count} voyageurs', diff --git a/shared/src/i18n/gr/budget.ts b/shared/src/i18n/gr/budget.ts index b90ba5ed0..7615d3c77 100644 --- a/shared/src/i18n/gr/budget.ts +++ b/shared/src/i18n/gr/budget.ts @@ -118,6 +118,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Τέλη & εισιτήρια', 'costs.cat.health': 'Υγεία', 'costs.cat.tips': 'Φιλοδωρήματα', + 'costs.cat.fuel': 'Καύσιμα', + 'costs.cat.parkings': 'Στάθμευση', 'costs.cat.other': 'Άλλα', 'costs.daysCount': '{count} ημέρες', 'costs.travelers': '{count} ταξιδιώτες', diff --git a/shared/src/i18n/hu/budget.ts b/shared/src/i18n/hu/budget.ts index dcb5bb926..269526ca1 100644 --- a/shared/src/i18n/hu/budget.ts +++ b/shared/src/i18n/hu/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Díjak és jegyek', 'costs.cat.health': 'Egészség', 'costs.cat.tips': 'Borravaló', + 'costs.cat.fuel': 'Üzemanyag', + 'costs.cat.parkings': 'Parkolás', 'costs.cat.other': 'Egyéb', 'costs.daysCount': '{count} nap', 'costs.travelers': '{count} utazó', diff --git a/shared/src/i18n/id/budget.ts b/shared/src/i18n/id/budget.ts index 7c3d9f192..02d540616 100644 --- a/shared/src/i18n/id/budget.ts +++ b/shared/src/i18n/id/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Biaya & tiket', 'costs.cat.health': 'Kesehatan', 'costs.cat.tips': 'Tip', + 'costs.cat.fuel': 'Bahan Bakar', + 'costs.cat.parkings': 'Parkir', 'costs.cat.other': 'Lainnya', 'costs.daysCount': '{count} hari', 'costs.travelers': '{count} pelancong', diff --git a/shared/src/i18n/it/budget.ts b/shared/src/i18n/it/budget.ts index 5b462d930..0a9b18866 100644 --- a/shared/src/i18n/it/budget.ts +++ b/shared/src/i18n/it/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Tariffe e biglietti', 'costs.cat.health': 'Salute', 'costs.cat.tips': 'Mance', + 'costs.cat.fuel': 'Carburante', + 'costs.cat.parkings': 'Parcheggio', 'costs.cat.other': 'Altro', 'costs.daysCount': '{count} giorni', 'costs.travelers': '{count} viaggiatori', diff --git a/shared/src/i18n/ja/budget.ts b/shared/src/i18n/ja/budget.ts index 7c9214f9a..9f8ccc68d 100644 --- a/shared/src/i18n/ja/budget.ts +++ b/shared/src/i18n/ja/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': '手数料・チケット', 'costs.cat.health': '健康', 'costs.cat.tips': 'チップ', + 'costs.cat.fuel': '燃料', + 'costs.cat.parkings': '駐車場', 'costs.cat.other': 'その他', 'costs.daysCount': '{count}日間', 'costs.travelers': '{count}人の旅行者', diff --git a/shared/src/i18n/ko/budget.ts b/shared/src/i18n/ko/budget.ts index 73ba8be60..5d27a63f6 100644 --- a/shared/src/i18n/ko/budget.ts +++ b/shared/src/i18n/ko/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': '요금 및 입장권', 'costs.cat.health': '건강', 'costs.cat.tips': '팁', + 'costs.cat.fuel': '연료', + 'costs.cat.parkings': '주차', 'costs.cat.other': '기타', 'costs.daysCount': '{count}일', 'costs.travelers': '여행자 {count}명', diff --git a/shared/src/i18n/nl/budget.ts b/shared/src/i18n/nl/budget.ts index c2f000048..3ab904121 100644 --- a/shared/src/i18n/nl/budget.ts +++ b/shared/src/i18n/nl/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Kosten & tickets', 'costs.cat.health': 'Gezondheid', 'costs.cat.tips': 'Fooien', + 'costs.cat.fuel': 'Brandstof', + 'costs.cat.parkings': 'Parkeren', 'costs.cat.other': 'Overig', 'costs.daysCount': '{count} dagen', 'costs.travelers': '{count} reizigers', diff --git a/shared/src/i18n/pl/budget.ts b/shared/src/i18n/pl/budget.ts index f4b326f1d..0f128bc21 100644 --- a/shared/src/i18n/pl/budget.ts +++ b/shared/src/i18n/pl/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Opłaty i bilety', 'costs.cat.health': 'Zdrowie', 'costs.cat.tips': 'Napiwki', + 'costs.cat.fuel': 'Paliwo', + 'costs.cat.parkings': 'Parking', 'costs.cat.other': 'Inne', 'costs.daysCount': 'Dni: {count}', 'costs.travelers': 'Podróżnych: {count}', diff --git a/shared/src/i18n/ru/budget.ts b/shared/src/i18n/ru/budget.ts index 623b0eb3e..8136ab245 100644 --- a/shared/src/i18n/ru/budget.ts +++ b/shared/src/i18n/ru/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Сборы и билеты', 'costs.cat.health': 'Здоровье', 'costs.cat.tips': 'Чаевые', + 'costs.cat.fuel': 'Топливо', + 'costs.cat.parkings': 'Парковка', 'costs.cat.other': 'Прочее', 'costs.daysCount': '{count} дней', 'costs.travelers': '{count} путешественников', diff --git a/shared/src/i18n/sv/budget.ts b/shared/src/i18n/sv/budget.ts index 2deecf14e..a0378a774 100644 --- a/shared/src/i18n/sv/budget.ts +++ b/shared/src/i18n/sv/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Avgifter och biljetter', 'costs.cat.health': 'Hälsa', 'costs.cat.tips': 'Dricks', + 'costs.cat.fuel': 'Bränsle', + 'costs.cat.parkings': 'Parkering', 'costs.cat.other': 'Annat', 'costs.daysCount': '{count} dagar', 'costs.travelers': '{count} resenärer', diff --git a/shared/src/i18n/tr/budget.ts b/shared/src/i18n/tr/budget.ts index 8e6848b6e..93a67006c 100644 --- a/shared/src/i18n/tr/budget.ts +++ b/shared/src/i18n/tr/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Ücretler & biletler', 'costs.cat.health': 'Sağlık', 'costs.cat.tips': 'Bahşişler', + 'costs.cat.fuel': 'Yakıt', + 'costs.cat.parkings': 'Otopark', 'costs.cat.other': 'Diğer', 'costs.daysCount': '{count} gün', 'costs.travelers': '{count} yolcu', diff --git a/shared/src/i18n/uk/budget.ts b/shared/src/i18n/uk/budget.ts index 239d1886f..cd583f96c 100644 --- a/shared/src/i18n/uk/budget.ts +++ b/shared/src/i18n/uk/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Збори та квитки', 'costs.cat.health': 'Здоров’я', 'costs.cat.tips': 'Чайові', + 'costs.cat.fuel': 'Пальне', + 'costs.cat.parkings': 'Паркування', 'costs.cat.other': 'Інше', 'costs.daysCount': '{count} днів', 'costs.travelers': '{count} мандрівників', diff --git a/shared/src/i18n/vi/budget.ts b/shared/src/i18n/vi/budget.ts index c6d0089b8..1e4c57a88 100644 --- a/shared/src/i18n/vi/budget.ts +++ b/shared/src/i18n/vi/budget.ts @@ -117,6 +117,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': 'Phí & vé', 'costs.cat.health': 'Sức khỏe', 'costs.cat.tips': 'Mẹo', + 'costs.cat.fuel': 'Nhiên liệu', + 'costs.cat.parkings': 'Bãi đỗ xe', 'costs.cat.other': 'Khác', 'costs.daysCount': '{count} ngày', 'costs.travelers': '{count} người', diff --git a/shared/src/i18n/zh-TW/budget.ts b/shared/src/i18n/zh-TW/budget.ts index 62331c1c4..a6af98f62 100644 --- a/shared/src/i18n/zh-TW/budget.ts +++ b/shared/src/i18n/zh-TW/budget.ts @@ -116,6 +116,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': '費用與票券', 'costs.cat.health': '健康', 'costs.cat.tips': '小費', + 'costs.cat.fuel': '燃油', + 'costs.cat.parkings': '停車', 'costs.cat.other': '其他', 'costs.daysCount': '{count} 天', 'costs.travelers': '{count} 位旅伴', diff --git a/shared/src/i18n/zh/budget.ts b/shared/src/i18n/zh/budget.ts index e996255f4..96ad4ff55 100644 --- a/shared/src/i18n/zh/budget.ts +++ b/shared/src/i18n/zh/budget.ts @@ -116,6 +116,8 @@ const budget: TranslationStrings = { 'costs.cat.fees': '费用与门票', 'costs.cat.health': '健康', 'costs.cat.tips': '小费', + 'costs.cat.fuel': '燃油', + 'costs.cat.parkings': '停车', 'costs.cat.other': '其他', 'costs.daysCount': '{count} 天', 'costs.travelers': '{count} 位同行者',