mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(budget): name the category parking and file parking bookings in it
The key was 'parkings', which is not a word and does not match the 'parking' the reservation type uses everywhere else. The shared trip page prints the stored category verbatim, so a share link would have shown "parkings" in every language. The category also had no relationship to the booking type of the same name: RESERVATION_TYPE_TO_COST_CATEGORY still sent a parking booking's auto-expense to Transport, so the new bucket sat empty next to the costs that belong in it while a manually typed "parking" resolved into it. Both paths agree now.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { COST_CATEGORIES } from '@trek/shared'
|
||||
import { COST_CATEGORIES, typeToCostCategory } 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', () => {
|
||||
it('has metadata for every fixed category, including fuel and parking', () => {
|
||||
for (const key of COST_CATEGORIES) {
|
||||
expect(COST_CAT_META[key]).toBeDefined()
|
||||
expect(COST_CAT_META[key].key).toBe(key)
|
||||
@@ -16,15 +16,21 @@ describe('COST_CAT_META', () => {
|
||||
})
|
||||
|
||||
describe('catMeta', () => {
|
||||
it('resolves fuel and parkings directly', () => {
|
||||
it('resolves fuel and parking directly', () => {
|
||||
expect(catMeta('fuel').key).toBe('fuel')
|
||||
expect(catMeta('parkings').key).toBe('parkings')
|
||||
expect(catMeta('parking').key).toBe('parking')
|
||||
})
|
||||
|
||||
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')
|
||||
expect(catMeta('Parking').key).toBe('parking')
|
||||
expect(catMeta('car park').key).toBe('parking')
|
||||
})
|
||||
|
||||
it('resolves a parking booking the same way whichever path created the expense', () => {
|
||||
expect(catMeta(typeToCostCategory('parking')).key).toBe('parking')
|
||||
expect(catMeta('parking').key).toBe(typeToCostCategory('parking'))
|
||||
})
|
||||
|
||||
it('falls back to other for unknown categories', () => {
|
||||
|
||||
@@ -28,7 +28,7 @@ export const COST_CAT_META: Record<CostCategory, CostCategoryMeta> = {
|
||||
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' },
|
||||
parking: { key: 'parking', labelKey: 'costs.cat.parking', Icon: ParkingCircle, color: '#3b82f6' },
|
||||
other: { key: 'other', labelKey: 'costs.cat.other', Icon: MoreHorizontal, color: '#6b7280' },
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ const LEGACY_CATEGORY_MAP: Record<string, CostCategory> = {
|
||||
health: 'health', medical: 'health',
|
||||
tip: 'tips', tips: 'tips',
|
||||
gas: 'fuel', fuel: 'fuel', petrol: 'fuel',
|
||||
parking: 'parkings', parkings: 'parkings',
|
||||
parking: 'parking', parkings: 'parking', 'car park': 'parking',
|
||||
other: 'other', misc: 'other',
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
budgetToggleMemberPaidRequestSchema,
|
||||
budgetReorderItemsRequestSchema,
|
||||
COST_CATEGORIES,
|
||||
typeToCostCategory,
|
||||
} from './budget.schema';
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
@@ -44,8 +45,19 @@ describe('budgetReorderItemsRequestSchema', () => {
|
||||
});
|
||||
|
||||
describe('COST_CATEGORIES', () => {
|
||||
it('includes fuel and parkings alongside the existing fixed categories', () => {
|
||||
it('includes fuel and parking alongside the existing fixed categories', () => {
|
||||
expect(COST_CATEGORIES).toContain('fuel');
|
||||
expect(COST_CATEGORIES).toContain('parkings');
|
||||
expect(COST_CATEGORIES).toContain('parking');
|
||||
});
|
||||
});
|
||||
|
||||
describe('typeToCostCategory', () => {
|
||||
it('files a parking booking under parking, not transport', () => {
|
||||
expect(typeToCostCategory('parking')).toBe('parking');
|
||||
});
|
||||
|
||||
it('leaves the other vehicle types on transport', () => {
|
||||
expect(typeToCostCategory('car-rental')).toBe('transport');
|
||||
expect(typeToCostCategory('taxi')).toBe('transport');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ export const COST_CATEGORIES = [
|
||||
'health',
|
||||
'tips',
|
||||
'fuel',
|
||||
'parkings',
|
||||
'parking',
|
||||
'other',
|
||||
] as const;
|
||||
export type CostCategory = (typeof COST_CATEGORIES)[number];
|
||||
@@ -72,7 +72,7 @@ const RESERVATION_TYPE_TO_COST_CATEGORY: Record<string, CostCategory> = {
|
||||
hotel: 'accommodation',
|
||||
accommodation: 'accommodation',
|
||||
lodging: 'accommodation',
|
||||
parking: 'transport',
|
||||
parking: 'parking',
|
||||
restaurant: 'food',
|
||||
activity: 'activities',
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'الصحة',
|
||||
'costs.cat.tips': 'البقشيش',
|
||||
'costs.cat.fuel': 'وقود',
|
||||
'costs.cat.parkings': 'موقف سيارات',
|
||||
'costs.cat.parking': 'موقف سيارات',
|
||||
'costs.cat.other': 'أخرى',
|
||||
'costs.daysCount': '{count} أيام',
|
||||
'costs.travelers': '{count} مسافرين',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Saúde',
|
||||
'costs.cat.tips': 'Gorjetas',
|
||||
'costs.cat.fuel': 'Combustível',
|
||||
'costs.cat.parkings': 'Estacionamento',
|
||||
'costs.cat.parking': 'Estacionamento',
|
||||
'costs.cat.other': 'Outros',
|
||||
'costs.daysCount': '{count} dias',
|
||||
'costs.travelers': '{count} viajantes',
|
||||
|
||||
@@ -117,7 +117,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Salut',
|
||||
'costs.cat.tips': 'Propines',
|
||||
'costs.cat.fuel': 'Combustible',
|
||||
'costs.cat.parkings': 'Aparcament',
|
||||
'costs.cat.parking': 'Aparcament',
|
||||
'costs.cat.other': 'Altres',
|
||||
'costs.daysCount': '{count} dies',
|
||||
'costs.travelers': '{count} viatgers',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Zdraví',
|
||||
'costs.cat.tips': 'Spropitné',
|
||||
'costs.cat.fuel': 'Palivo',
|
||||
'costs.cat.parkings': 'Parkování',
|
||||
'costs.cat.parking': 'Parkování',
|
||||
'costs.cat.other': 'Ostatní',
|
||||
'costs.daysCount': '{count} dní',
|
||||
'costs.travelers': '{count} cestovatelů',
|
||||
|
||||
@@ -121,7 +121,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Gesundheit',
|
||||
'costs.cat.tips': 'Trinkgeld',
|
||||
'costs.cat.fuel': 'Kraftstoff',
|
||||
'costs.cat.parkings': 'Parken',
|
||||
'costs.cat.parking': 'Parken',
|
||||
'costs.cat.other': 'Sonstiges',
|
||||
'costs.daysCount': '{count} Tage',
|
||||
'costs.travelers': '{count} Reisende',
|
||||
|
||||
@@ -125,7 +125,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Health',
|
||||
'costs.cat.tips': 'Tips',
|
||||
'costs.cat.fuel': 'Fuel',
|
||||
'costs.cat.parkings': 'Parking',
|
||||
'costs.cat.parking': 'Parking',
|
||||
'costs.cat.other': 'Other',
|
||||
'costs.daysCount': '{count} days',
|
||||
'costs.travelers': '{count} travelers',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Salud',
|
||||
'costs.cat.tips': 'Propinas',
|
||||
'costs.cat.fuel': 'Combustible',
|
||||
'costs.cat.parkings': 'Aparcamiento',
|
||||
'costs.cat.parking': 'Aparcamiento',
|
||||
'costs.cat.other': 'Otros',
|
||||
'costs.daysCount': '{count} días',
|
||||
'costs.travelers': '{count} viajeros',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Santé',
|
||||
'costs.cat.tips': 'Pourboires',
|
||||
'costs.cat.fuel': 'Carburant',
|
||||
'costs.cat.parkings': 'Parking',
|
||||
'costs.cat.parking': 'Parking',
|
||||
'costs.cat.other': 'Autre',
|
||||
'costs.daysCount': '{count} jours',
|
||||
'costs.travelers': '{count} voyageurs',
|
||||
|
||||
@@ -119,7 +119,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Υγεία',
|
||||
'costs.cat.tips': 'Φιλοδωρήματα',
|
||||
'costs.cat.fuel': 'Καύσιμα',
|
||||
'costs.cat.parkings': 'Στάθμευση',
|
||||
'costs.cat.parking': 'Στάθμευση',
|
||||
'costs.cat.other': 'Άλλα',
|
||||
'costs.daysCount': '{count} ημέρες',
|
||||
'costs.travelers': '{count} ταξιδιώτες',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Egészség',
|
||||
'costs.cat.tips': 'Borravaló',
|
||||
'costs.cat.fuel': 'Üzemanyag',
|
||||
'costs.cat.parkings': 'Parkolás',
|
||||
'costs.cat.parking': 'Parkolás',
|
||||
'costs.cat.other': 'Egyéb',
|
||||
'costs.daysCount': '{count} nap',
|
||||
'costs.travelers': '{count} utazó',
|
||||
|
||||
@@ -117,8 +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.fuel': 'Bahan bakar',
|
||||
'costs.cat.parking': 'Parkir',
|
||||
'costs.cat.other': 'Lainnya',
|
||||
'costs.daysCount': '{count} hari',
|
||||
'costs.travelers': '{count} pelancong',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Salute',
|
||||
'costs.cat.tips': 'Mance',
|
||||
'costs.cat.fuel': 'Carburante',
|
||||
'costs.cat.parkings': 'Parcheggio',
|
||||
'costs.cat.parking': 'Parcheggio',
|
||||
'costs.cat.other': 'Altro',
|
||||
'costs.daysCount': '{count} giorni',
|
||||
'costs.travelers': '{count} viaggiatori',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': '健康',
|
||||
'costs.cat.tips': 'チップ',
|
||||
'costs.cat.fuel': '燃料',
|
||||
'costs.cat.parkings': '駐車場',
|
||||
'costs.cat.parking': '駐車場',
|
||||
'costs.cat.other': 'その他',
|
||||
'costs.daysCount': '{count}日間',
|
||||
'costs.travelers': '{count}人の旅行者',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': '건강',
|
||||
'costs.cat.tips': '팁',
|
||||
'costs.cat.fuel': '연료',
|
||||
'costs.cat.parkings': '주차',
|
||||
'costs.cat.parking': '주차',
|
||||
'costs.cat.other': '기타',
|
||||
'costs.daysCount': '{count}일',
|
||||
'costs.travelers': '여행자 {count}명',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Gezondheid',
|
||||
'costs.cat.tips': 'Fooien',
|
||||
'costs.cat.fuel': 'Brandstof',
|
||||
'costs.cat.parkings': 'Parkeren',
|
||||
'costs.cat.parking': 'Parkeren',
|
||||
'costs.cat.other': 'Overig',
|
||||
'costs.daysCount': '{count} dagen',
|
||||
'costs.travelers': '{count} reizigers',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Zdrowie',
|
||||
'costs.cat.tips': 'Napiwki',
|
||||
'costs.cat.fuel': 'Paliwo',
|
||||
'costs.cat.parkings': 'Parking',
|
||||
'costs.cat.parking': 'Parking',
|
||||
'costs.cat.other': 'Inne',
|
||||
'costs.daysCount': 'Dni: {count}',
|
||||
'costs.travelers': 'Podróżnych: {count}',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Здоровье',
|
||||
'costs.cat.tips': 'Чаевые',
|
||||
'costs.cat.fuel': 'Топливо',
|
||||
'costs.cat.parkings': 'Парковка',
|
||||
'costs.cat.parking': 'Парковка',
|
||||
'costs.cat.other': 'Прочее',
|
||||
'costs.daysCount': '{count} дней',
|
||||
'costs.travelers': '{count} путешественников',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Hälsa',
|
||||
'costs.cat.tips': 'Dricks',
|
||||
'costs.cat.fuel': 'Bränsle',
|
||||
'costs.cat.parkings': 'Parkering',
|
||||
'costs.cat.parking': 'Parkering',
|
||||
'costs.cat.other': 'Annat',
|
||||
'costs.daysCount': '{count} dagar',
|
||||
'costs.travelers': '{count} resenärer',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Sağlık',
|
||||
'costs.cat.tips': 'Bahşişler',
|
||||
'costs.cat.fuel': 'Yakıt',
|
||||
'costs.cat.parkings': 'Otopark',
|
||||
'costs.cat.parking': 'Otopark',
|
||||
'costs.cat.other': 'Diğer',
|
||||
'costs.daysCount': '{count} gün',
|
||||
'costs.travelers': '{count} yolcu',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': 'Здоров’я',
|
||||
'costs.cat.tips': 'Чайові',
|
||||
'costs.cat.fuel': 'Пальне',
|
||||
'costs.cat.parkings': 'Паркування',
|
||||
'costs.cat.parking': 'Паркування',
|
||||
'costs.cat.other': 'Інше',
|
||||
'costs.daysCount': '{count} днів',
|
||||
'costs.travelers': '{count} мандрівників',
|
||||
|
||||
@@ -118,7 +118,7 @@ const budget: TranslationStrings = {
|
||||
'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.parking': 'Bãi đỗ xe',
|
||||
'costs.cat.other': 'Khác',
|
||||
'costs.daysCount': '{count} ngày',
|
||||
'costs.travelers': '{count} người',
|
||||
|
||||
@@ -117,7 +117,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': '健康',
|
||||
'costs.cat.tips': '小費',
|
||||
'costs.cat.fuel': '燃油',
|
||||
'costs.cat.parkings': '停車',
|
||||
'costs.cat.parking': '停車',
|
||||
'costs.cat.other': '其他',
|
||||
'costs.daysCount': '{count} 天',
|
||||
'costs.travelers': '{count} 位旅伴',
|
||||
|
||||
@@ -117,7 +117,7 @@ const budget: TranslationStrings = {
|
||||
'costs.cat.health': '健康',
|
||||
'costs.cat.tips': '小费',
|
||||
'costs.cat.fuel': '燃油',
|
||||
'costs.cat.parkings': '停车',
|
||||
'costs.cat.parking': '停车',
|
||||
'costs.cat.other': '其他',
|
||||
'costs.daysCount': '{count} 天',
|
||||
'costs.travelers': '{count} 位同行者',
|
||||
|
||||
Reference in New Issue
Block a user