import { documentExtensions, documentMimePatterns, spreadsheetExtensions, spreadsheetMimePatterns, videoExtensions, } from "../constants"; export type FileCategory = | "document" | "spreadsheet" | "pdf" | "video" | "image" | "other"; interface GetFileCategoryParams { readonly fileName?: string; readonly fileType?: string; } function extensionOf(fileName: string): string { const dot = fileName.lastIndexOf("."); return dot < 0 ? "" : fileName.slice(dot + 1).toLowerCase(); } function matchesAnyExtension( fileName: string | undefined, list: { type: string }[], ): boolean { if (!fileName) return false; const ext = extensionOf(fileName); if (!ext) return false; return list.some(({ type }) => type === ext); } function matchesAnyMimePattern( fileType: string | undefined, patterns: string[], ): boolean { if (!fileType) return false; const lower = fileType.toLowerCase(); return patterns.some(pattern => lower.includes(pattern)); } /** * Bucket a file into one of the generic categories the design system * recognises. Decoupled from icon names so that: * * 1. Consumers can use generic terminology in copy, accessibility * labels, and filter UI ("Document", "Spreadsheet") without * coupling to the brand-named icon assets (`word`, `excel`). * 2. When `@jobber/design` adds generic-document / generic-spreadsheet * icons, the icon mapping can be updated without breaking * consumers who already use this categorisation. * * Detection precedence: `fileType` (MIME) first, then `fileName` * extension. The extension lists cover OpenDocument Text / Spreadsheet, * RTF, Apple Pages / Numbers, CSV, and WordPerfect alongside the * Microsoft Office formats so callers do not have to enumerate them. * * `fileType` matching is case-insensitive and accepts both full MIME * strings (`"video/mp4"`, `"image/jpeg"`) and coarse type strings * (`"video"`, `"image"`) since `parseFile` collapses video MIMEs to * the bare token `"video"` for external files. */ export function getFileCategory({ fileName, fileType, }: GetFileCategoryParams): FileCategory { const normalizedType = fileType?.toLowerCase(); if (normalizedType?.includes("pdf") || fileName?.match(/\.pdf$/i)) { return "pdf"; } if (normalizedType?.includes("image")) { return "image"; } if ( normalizedType?.includes("video") || matchesAnyExtension(fileName, videoExtensions) ) { return "video"; } if ( matchesAnyMimePattern(normalizedType, documentMimePatterns) || matchesAnyExtension(fileName, documentExtensions) ) { return "document"; } if ( matchesAnyMimePattern(normalizedType, spreadsheetMimePatterns) || matchesAnyExtension(fileName, spreadsheetExtensions) ) { return "spreadsheet"; } return "other"; }