import type { ICountryFeeScheduleCatalog, IFeeSchedule, IFeeScheduleData, IFeeScheduleFeeRow, IFeeScheduleRuleSection, } from './feeschedules.types.js'; import { GERMANY_FEE_SCHEDULE_CATALOG } from './feeschedules.germany.js'; import { GERMANY_FEE_SCHEDULE_DATA } from './germany/index.js'; export const FEE_SCHEDULE_CATALOGS: ICountryFeeScheduleCatalog[] = [ GERMANY_FEE_SCHEDULE_CATALOG, ]; export class FeeSchedules { private catalogs: ICountryFeeScheduleCatalog[]; private data: IFeeScheduleData[]; constructor( catalogs: ICountryFeeScheduleCatalog[] = FEE_SCHEDULE_CATALOGS, data: IFeeScheduleData[] = GERMANY_FEE_SCHEDULE_DATA, ) { this.catalogs = catalogs; this.data = data; } public getCountries(): ICountryFeeScheduleCatalog[] { return this.catalogs; } public getByCountry(countryCode: string): IFeeSchedule[] { const catalog = this.getCatalog(countryCode); return catalog?.schedules || []; } public getById(id: string): IFeeSchedule | undefined { return this.getAll().find((schedule) => schedule.id === id); } public getDataByScheduleId(scheduleId: string): IFeeScheduleData | undefined { return this.data.find((scheduleData) => scheduleData.scheduleId === scheduleId); } public getFeeRows(scheduleId: string): IFeeScheduleFeeRow[] { return this.getDataByScheduleId(scheduleId)?.feeRows || []; } public getFeeRow(scheduleId: string, code: string): IFeeScheduleFeeRow | undefined { const normalizedCode = normalizeSearchValue(code); return this.getFeeRows(scheduleId).find((row) => { return row.code ? normalizeSearchValue(row.code) === normalizedCode : false; }); } public getRuleSections(scheduleId: string): IFeeScheduleRuleSection[] { return this.getDataByScheduleId(scheduleId)?.ruleSections || []; } public findByAbbreviation(abbreviation: string, countryCode?: string): IFeeSchedule | undefined { const normalizedAbbreviation = normalizeSearchValue(abbreviation); return this.getSchedules(countryCode).find( (schedule) => normalizeSearchValue(schedule.abbreviation) === normalizedAbbreviation, ); } public search(query: string, countryCode?: string): IFeeSchedule[] { const normalizedQuery = normalizeSearchValue(query); if (!normalizedQuery) { return []; } return this.getSchedules(countryCode).filter((schedule) => { return [schedule.id, schedule.area, schedule.abbreviation, schedule.description] .map((value) => normalizeSearchValue(value)) .some((value) => value.includes(normalizedQuery)); }); } public searchFeeRows(query: string, scheduleId?: string): IFeeScheduleFeeRow[] { const normalizedQuery = normalizeSearchValue(query); if (!normalizedQuery) { return []; } return this.getScheduleData(scheduleId) .flatMap((scheduleData) => scheduleData.feeRows) .filter((row) => { return [row.id, row.code, row.description, ...row.cells] .filter((value): value is string => Boolean(value)) .map((value) => normalizeSearchValue(value)) .some((value) => value.includes(normalizedQuery)); }); } public searchRuleSections(query: string, scheduleId?: string): IFeeScheduleRuleSection[] { const normalizedQuery = normalizeSearchValue(query); if (!normalizedQuery) { return []; } return this.getScheduleData(scheduleId) .flatMap((scheduleData) => scheduleData.ruleSections) .filter((section) => { return [section.reference, section.title, section.text] .filter((value): value is string => Boolean(value)) .map((value) => normalizeSearchValue(value)) .some((value) => value.includes(normalizedQuery)); }); } private getAll(): IFeeSchedule[] { return this.catalogs.flatMap((catalog) => catalog.schedules); } private getSchedules(countryCode?: string): IFeeSchedule[] { return countryCode ? this.getByCountry(countryCode) : this.getAll(); } private getScheduleData(scheduleId?: string): IFeeScheduleData[] { return scheduleId ? this.data.filter((scheduleData) => scheduleData.scheduleId === scheduleId) : this.data; } private getCatalog(countryCode: string): ICountryFeeScheduleCatalog | undefined { const normalizedCountryCode = countryCode.toUpperCase(); return this.catalogs.find((catalog) => catalog.countryCode === normalizedCountryCode); } } export const feeSchedules = new FeeSchedules(); const normalizeSearchValue = (value: string): string => { return value.trim().toLocaleLowerCase('de-DE').replace(/\s+/g, ' '); };