improvement: icons added to prosthesis types catalog.
This commit is contained in:
83
frontend/src/components/treatment/prosthesisJobPath.ts
Normal file
83
frontend/src/components/treatment/prosthesisJobPath.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type { ProsthesisCatalogEntry } from '@/types/treatment-catalog';
|
||||
import { splitProsthesisGroupCode } from '@/components/treatment/prosthesisTypeDisplay';
|
||||
|
||||
/** Non-breaking spaces so the gap around · does not collapse between inline spans. */
|
||||
export const PROSTHESIS_JOB_PATH_SEP = '\u00a0·\u00a0';
|
||||
|
||||
type CatalogLite = readonly Pick<
|
||||
ProsthesisCatalogEntry,
|
||||
'code' | 'label' | 'category' | 'subcategory'
|
||||
>[];
|
||||
|
||||
export type ProsthesisMessageFn = (key: string) => string;
|
||||
|
||||
function sameLabel(a: string, b: string): boolean {
|
||||
return a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0;
|
||||
}
|
||||
|
||||
function leafFromCatalogLabel(label: string): string {
|
||||
const parts = label
|
||||
.split(/\s*·\s*/)
|
||||
.map((part) => part.trim())
|
||||
.filter(Boolean);
|
||||
return parts.length > 1 ? parts[parts.length - 1] : label;
|
||||
}
|
||||
|
||||
function humanizeCode(code: string): string {
|
||||
return code
|
||||
.split('_')
|
||||
.filter(Boolean)
|
||||
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
/** Picker root whose title is a slash-joined list of its children (redundant in job titles). */
|
||||
const SKIP_CATEGORY_IN_JOB_PATH = new Set(['indirect']);
|
||||
|
||||
/** Category → subcategory → last picker node for one catalog code. */
|
||||
export function prosthesisJobPathParts(
|
||||
code: string,
|
||||
catalog: CatalogLite,
|
||||
t: ProsthesisMessageFn,
|
||||
): { ancestors: string[]; leaf: string } {
|
||||
const entry = catalog.find((item) => item.code === code);
|
||||
if (!entry) {
|
||||
return { ancestors: [], leaf: humanizeCode(code) };
|
||||
}
|
||||
|
||||
const ancestors: string[] = [];
|
||||
const subcategory = entry.subcategory?.trim() ?? '';
|
||||
const skipCategory = SKIP_CATEGORY_IN_JOB_PATH.has(entry.category);
|
||||
if (entry.category && !skipCategory) {
|
||||
ancestors.push(t(`category_${entry.category}`));
|
||||
}
|
||||
if (subcategory) {
|
||||
ancestors.push(t(`sub_${subcategory}`));
|
||||
}
|
||||
|
||||
const leaf = leafFromCatalogLabel(entry.label) || entry.label || code;
|
||||
const uniqueAncestors: string[] = [];
|
||||
for (const ancestor of ancestors) {
|
||||
if (!ancestor || sameLabel(ancestor, leaf)) continue;
|
||||
if (uniqueAncestors.some((existing) => sameLabel(existing, ancestor))) continue;
|
||||
uniqueAncestors.push(ancestor);
|
||||
}
|
||||
return { ancestors: uniqueAncestors, leaf };
|
||||
}
|
||||
|
||||
function formatProsthesisJobPath(
|
||||
parts: { ancestors: string[]; leaf: string },
|
||||
): string {
|
||||
return [...parts.ancestors, parts.leaf].filter(Boolean).join(PROSTHESIS_JOB_PATH_SEP);
|
||||
}
|
||||
|
||||
/** Full picker path for a type or stacked `code+code` group. */
|
||||
export function prosthesisJobPathLabel(
|
||||
code: string,
|
||||
catalog: CatalogLite,
|
||||
t: ProsthesisMessageFn,
|
||||
): string {
|
||||
return splitProsthesisGroupCode(code)
|
||||
.map((part) => formatProsthesisJobPath(prosthesisJobPathParts(part, catalog, t)))
|
||||
.join(' + ');
|
||||
}
|
||||
Reference in New Issue
Block a user