@burdenoff/microfe-assethandler 2026.826.2 → 2026.826.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"MaintenanceMaintenancePlansPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/MaintenanceMaintenancePlansPage.tsx"],"names":[],"mappings":"AAkHA,wBAAgB,+BAA+B,gCAuU9C"}
1
+ {"version":3,"file":"MaintenanceMaintenancePlansPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/MaintenanceMaintenancePlansPage.tsx"],"names":[],"mappings":"AAkHA,wBAAgB,+BAA+B,gCAwU9C"}
@@ -100,7 +100,7 @@ function j() {
100
100
  children: [/* @__PURE__ */ h(f, { className: "mr-2 size-4" }), "New plan"]
101
101
  })
102
102
  }),
103
- /* @__PURE__ */ h(x, { children: "Say “service the tractor every 90 days” or “every 250 engine hours” once, and let the schedule do the remembering. When a plan falls due it raises a maintenance job with its own checklist; completing that job starts the next cycle." }),
103
+ /* @__PURE__ */ h(x, { children: "Say “service the tractor every 90 days” or “every 250 engine hours” once, and let the schedule do the remembering. When a plan falls due, preview it and choose when to raise a maintenance job with its own checklist; completing that job starts the next cycle." }),
104
104
  /* @__PURE__ */ g("div", {
105
105
  className: "grid gap-4 sm:grid-cols-3",
106
106
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"MaintenanceMaintenancePlansPage.js","names":[],"sources":["../../../src/assethandler/pages/MaintenanceMaintenancePlansPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\n\nimport { formatMeter } from '../utils/meter';\nimport {\n Button,\n GlassCard,\n Input,\n PagePurpose,\n IllustratedEmptyState,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport {\n AlertTriangle,\n CalendarClock,\n Gauge,\n Loader2,\n Pencil,\n Play,\n Plus,\n Search,\n Trash2,\n} from 'lucide-react';\nimport { SectionHeader, StatTile } from '../components';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { cn } from '../utils/cn';\nimport { notify } from '../utils/toast';\nimport { MaintenancePlanDialog } from '../ops/MaintenancePlanDialog';\nimport {\n useMaintenancePlans,\n type DueMaintenancePlan,\n type MaintenancePlan,\n} from '../hooks/useMaintenancePlans';\n\n/**\n * Recurring preventive-maintenance plans (BOFF-6031).\n *\n * The screen answers the question the product could not previously ask: WHAT\n * NEEDS SERVICING? Before plans existed, every service was logged after the\n * fact and nothing was ever due.\n *\n * ── Null is not zero, anywhere on this page ──────────────────────────────────\n *\n * `currentMeter`, `nextDueMeter`, `daysUntilDue` and `meterUntilDue` are all\n * genuinely nullable, and null means \"does not apply\". A plan that counts only\n * in days has no meter due point; an asset nobody meters has no reading. Both\n * render as an em dash. Showing 0 would claim a machine sitting at zero hours,\n * or a service due today.\n */\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\nconst PRIORITY_PILL: Record<string, string> = {\n CRITICAL: 'bg-status-error-subtle text-status-error-text',\n HIGH: 'bg-status-warning-subtle text-status-warning-text',\n MEDIUM: 'bg-status-info-subtle text-status-info-text',\n LOW: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction Pill({ tone, children }: { tone: string; children: React.ReactNode }) {\n return (\n <span\n className={cn('inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', tone)}\n >\n {children}\n </span>\n );\n}\n\nconst formatDate = (iso: string | null): string =>\n iso ? new Date(iso).toLocaleDateString(undefined, { dateStyle: 'medium' }) : '—';\n\n/**\n * How a plan's recurrence reads in one line.\n *\n * The meter unit belongs to the ASSET, not the plan, so it is passed in: \"every 250\"\n * is only meaningful once you know whether that is hours, kilometres or cycles\n * (BOFF-6032). Null renders as the previous bare \"units\" wording.\n */\nfunction describeInterval(plan: MaintenancePlan, meterUnit?: string | null): string {\n const parts: string[] = [];\n if (plan.intervalDays != null) parts.push(`every ${plan.intervalDays} days`);\n if (plan.intervalMeter != null) parts.push(`every ${formatMeter(plan.intervalMeter, meterUnit)}`);\n // A plan with neither cannot be created any more, but one could predate the\n // guard — say so plainly rather than rendering an empty cell.\n return parts.length > 0 ? parts.join(' or ') : 'no interval — never comes due';\n}\n\n/** The due state, in the words a farmer would use. */\nfunction describeDue(entry: DueMaintenancePlan): string {\n if (entry.isOverdue) {\n if (entry.daysUntilDue != null && entry.daysUntilDue < 0) {\n const days = Math.abs(entry.daysUntilDue);\n return `${days} day${days === 1 ? '' : 's'} overdue`;\n }\n if (entry.meterUntilDue != null && entry.meterUntilDue <= 0) {\n return `${formatMeter(Math.abs(entry.meterUntilDue), entry.meterUnit)} past due`;\n }\n return 'Overdue';\n }\n if (entry.daysUntilDue != null) {\n if (entry.daysUntilDue === 0) return 'Due today';\n return `Due in ${entry.daysUntilDue} day${entry.daysUntilDue === 1 ? '' : 's'}`;\n }\n return 'Due now';\n}\n\nexport function MaintenanceMaintenancePlansPage() {\n const { assets } = useAssetHandlerStore();\n const [search, setSearch] = useState('');\n const [horizon, setHorizon] = useState('0');\n const [includeInactive, setIncludeInactive] = useState(false);\n const [dialogOpen, setDialogOpen] = useState(false);\n const [editing, setEditing] = useState<MaintenancePlan | null>(null);\n\n const { plans, duePlans, loading, error, updatePlan, deletePlan, generateRecords, busy } =\n useMaintenancePlans({ includeInactive, withinDays: Number(horizon) });\n\n const assetName = useMemo(() => {\n const byId = new Map(assets.map((asset) => [asset.id, asset.name]));\n return (id: string) => byId.get(id) ?? 'Unknown asset';\n }, [assets]);\n\n // Due rows carry the unit on the entry; plan-table rows have to look it up.\n const assetMeterUnit = useMemo(() => {\n const byId = new Map(assets.map((asset) => [asset.id, asset.meterUnit ?? null]));\n return (id: string) => byId.get(id) ?? null;\n }, [assets]);\n\n const filteredPlans = useMemo(() => {\n const needle = search.trim().toLowerCase();\n if (!needle) return plans;\n return plans.filter(\n (plan) =>\n plan.title.toLowerCase().includes(needle) ||\n assetName(plan.assetId).toLowerCase().includes(needle)\n );\n }, [plans, search, assetName]);\n\n const overdueCount = duePlans.filter((entry) => entry.isOverdue).length;\n\n const openCreate = () => {\n setEditing(null);\n setDialogOpen(true);\n };\n const openEdit = (plan: MaintenancePlan) => {\n setEditing(plan);\n setDialogOpen(true);\n };\n\n const handleDelete = (plan: MaintenancePlan) => {\n void deletePlan({ variables: { id: plan.id } });\n };\n\n const handleToggleActive = (plan: MaintenancePlan) => {\n void updatePlan({ variables: { id: plan.id, input: { isActive: !plan.isActive } } });\n };\n\n const handleGenerate = (dryRun: boolean) => {\n void generateRecords({\n variables: { input: { withinDays: Number(horizon), dryRun } },\n }).then((result) => {\n const outcome = result.data?.generateDueMaintenanceRecords;\n if (!outcome) return;\n if (dryRun) {\n notify.info(\n `${outcome.generated} job${outcome.generated === 1 ? '' : 's'} would be raised`,\n outcome.skipped > 0\n ? `${outcome.skipped} plan${outcome.skipped === 1 ? '' : 's'} skipped — their previous job is still open.`\n : 'Nothing is waiting on an open job.'\n );\n return;\n }\n if (outcome.generated === 0 && outcome.skipped === 0) {\n notify.info('Nothing was due', 'No plan has reached its next service point.');\n return;\n }\n notify.success(\n `${outcome.generated} job${outcome.generated === 1 ? '' : 's'} raised`,\n outcome.skipped > 0\n ? `${outcome.skipped} skipped — their previous job is still open.`\n : undefined\n );\n });\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <SectionHeader\n hero\n icon={CalendarClock}\n title=\"Maintenance Plans\"\n description=\"Recurring service schedules for your machinery\"\n actions={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"mr-2 size-4\" />\n New plan\n </Button>\n }\n />\n\n <PagePurpose>\n Say &ldquo;service the tractor every 90 days&rdquo; or &ldquo;every 250 engine hours&rdquo;\n once, and let the schedule do the remembering. When a plan falls due it raises a maintenance\n job with its own checklist; completing that job starts the next cycle.\n </PagePurpose>\n\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <StatTile\n label=\"Active plans\"\n value={plans.filter((plan) => plan.isActive).length}\n icon={CalendarClock}\n />\n <StatTile label=\"Due now or soon\" value={duePlans.length} icon={Gauge} />\n <StatTile\n label=\"Overdue\"\n value={overdueCount}\n icon={AlertTriangle}\n {...(overdueCount > 0\n ? { delta: 'needs attention', deltaTone: 'negative' as const }\n : {})}\n />\n </div>\n\n {error && (\n <GlassCard className=\"p-4\">\n <p className=\"text-sm text-status-error-text\">\n Could not load maintenance plans: {error.message}\n </p>\n </GlassCard>\n )}\n\n {/* ── What needs servicing ───────────────────────────────────────── */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title=\"What needs servicing\"\n description=\"Plans that have reached their next service point.\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <Select value={horizon} onValueChange={setHorizon}>\n <SelectTrigger className=\"w-44\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"0\">Due now</SelectItem>\n <SelectItem value=\"7\">Within a week</SelectItem>\n <SelectItem value=\"14\">Within a fortnight</SelectItem>\n <SelectItem value=\"30\">Within a month</SelectItem>\n </SelectContent>\n </Select>\n <Button variant=\"outline\" onClick={() => handleGenerate(true)} disabled={busy}>\n Preview\n </Button>\n <Button\n onClick={() => handleGenerate(false)}\n disabled={busy || duePlans.length === 0}\n >\n {busy ? (\n <Loader2 className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <Play className=\"mr-2 size-4\" />\n )}\n Raise the work\n </Button>\n </div>\n }\n />\n\n {loading && duePlans.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-secondary\">Loading…</p>\n ) : duePlans.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n Nothing is due{horizon === '0' ? ' right now' : ` in the next ${horizon} days`}.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-subtle\">\n {duePlans.map((entry) => (\n <li key={entry.plan.id} className=\"flex flex-wrap items-center gap-3 py-3\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {entry.plan.title}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {assetName(entry.plan.assetId)} ·{' '}\n {describeInterval(entry.plan, entry.meterUnit)}\n </p>\n </div>\n <div className=\"text-right text-xs text-text-secondary\">\n <p\n className={cn(\n 'font-medium',\n entry.isOverdue ? 'text-status-error-text' : 'text-text-primary'\n )}\n >\n {describeDue(entry)}\n </p>\n <p>\n {entry.dueReason === 'METER'\n ? `Meter ${entry.currentMeter ?? '—'} of ${entry.nextDueMeter ?? '—'}`\n : `Due ${formatDate(entry.nextDueAt)}`}\n </p>\n </div>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* ── All plans ──────────────────────────────────────────────────── */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title=\"All plans\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 size-4 -translate-y-1/2 text-text-tertiary\" />\n <Input\n className=\"w-56 pl-8\"\n placeholder=\"Search plans or assets\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n <Button variant=\"outline\" onClick={() => setIncludeInactive((prev) => !prev)}>\n {includeInactive ? 'Hide paused' : 'Show paused'}\n </Button>\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 size-4\" /> New plan\n </Button>\n </div>\n }\n />\n\n {filteredPlans.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={plans.length === 0 ? 'No maintenance plans yet' : 'No plans match that search'}\n description={\n plans.length === 0\n ? 'Create a plan to service a machine every so many days or usage units, instead of remembering by hand.'\n : 'Try a different asset or plan name.'\n }\n action={\n plans.length === 0 ? (\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 size-4\" /> New plan\n </Button>\n ) : undefined\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-xs text-text-secondary\">\n <th className=\"py-2 pr-4 font-medium\">Plan</th>\n <th className=\"py-2 pr-4 font-medium\">Asset</th>\n <th className=\"py-2 pr-4 font-medium\">Recurs</th>\n <th className=\"py-2 pr-4 font-medium\">Last serviced</th>\n <th className=\"py-2 pr-4 font-medium\">Priority</th>\n <th className=\"py-2 pr-4 font-medium\">Status</th>\n <th className=\"py-2 font-medium text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {filteredPlans.map((plan) => (\n <tr key={plan.id} className=\"border-b border-border-subtle last:border-0\">\n <td className=\"py-3 pr-4\">\n <p className=\"font-medium text-text-primary\">{plan.title}</p>\n <p className=\"text-xs text-text-secondary\">{humanize(plan.type)}</p>\n </td>\n <td className=\"py-3 pr-4 text-text-secondary\">{assetName(plan.assetId)}</td>\n <td className=\"py-3 pr-4 text-text-secondary\">\n {describeInterval(plan, assetMeterUnit(plan.assetId))}\n </td>\n <td className=\"py-3 pr-4 text-text-secondary\">\n {formatDate(plan.lastServiceAt)}\n </td>\n <td className=\"py-3 pr-4\">\n <Pill tone={PRIORITY_PILL[plan.priority] ?? PRIORITY_PILL.LOW!}>\n {humanize(plan.priority)}\n </Pill>\n </td>\n <td className=\"py-3 pr-4\">\n <Pill\n tone={\n plan.isActive\n ? 'bg-status-success-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-secondary'\n }\n >\n {plan.isActive ? 'Active' : 'Paused'}\n </Pill>\n </td>\n <td className=\"py-3 text-right\">\n <div className=\"flex justify-end gap-1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handleToggleActive(plan)}\n disabled={busy}\n >\n {plan.isActive ? 'Pause' : 'Resume'}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => openEdit(plan)}\n aria-label={`Edit ${plan.title}`}\n >\n <Pencil className=\"size-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => handleDelete(plan)}\n disabled={busy}\n aria-label={`Delete ${plan.title}`}\n >\n <Trash2 className=\"size-4\" />\n </Button>\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </GlassCard>\n\n <MaintenancePlanDialog open={dialogOpen} onOpenChange={setDialogOpen} plan={editing} />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MaintenanceMaintenancePlansPage.js","names":[],"sources":["../../../src/assethandler/pages/MaintenanceMaintenancePlansPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\n\nimport { formatMeter } from '../utils/meter';\nimport {\n Button,\n GlassCard,\n Input,\n PagePurpose,\n IllustratedEmptyState,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport {\n AlertTriangle,\n CalendarClock,\n Gauge,\n Loader2,\n Pencil,\n Play,\n Plus,\n Search,\n Trash2,\n} from 'lucide-react';\nimport { SectionHeader, StatTile } from '../components';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { cn } from '../utils/cn';\nimport { notify } from '../utils/toast';\nimport { MaintenancePlanDialog } from '../ops/MaintenancePlanDialog';\nimport {\n useMaintenancePlans,\n type DueMaintenancePlan,\n type MaintenancePlan,\n} from '../hooks/useMaintenancePlans';\n\n/**\n * Recurring preventive-maintenance plans (BOFF-6031).\n *\n * The screen answers the question the product could not previously ask: WHAT\n * NEEDS SERVICING? Before plans existed, every service was logged after the\n * fact and nothing was ever due.\n *\n * ── Null is not zero, anywhere on this page ──────────────────────────────────\n *\n * `currentMeter`, `nextDueMeter`, `daysUntilDue` and `meterUntilDue` are all\n * genuinely nullable, and null means \"does not apply\". A plan that counts only\n * in days has no meter due point; an asset nobody meters has no reading. Both\n * render as an em dash. Showing 0 would claim a machine sitting at zero hours,\n * or a service due today.\n */\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\nconst PRIORITY_PILL: Record<string, string> = {\n CRITICAL: 'bg-status-error-subtle text-status-error-text',\n HIGH: 'bg-status-warning-subtle text-status-warning-text',\n MEDIUM: 'bg-status-info-subtle text-status-info-text',\n LOW: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction Pill({ tone, children }: { tone: string; children: React.ReactNode }) {\n return (\n <span\n className={cn('inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', tone)}\n >\n {children}\n </span>\n );\n}\n\nconst formatDate = (iso: string | null): string =>\n iso ? new Date(iso).toLocaleDateString(undefined, { dateStyle: 'medium' }) : '—';\n\n/**\n * How a plan's recurrence reads in one line.\n *\n * The meter unit belongs to the ASSET, not the plan, so it is passed in: \"every 250\"\n * is only meaningful once you know whether that is hours, kilometres or cycles\n * (BOFF-6032). Null renders as the previous bare \"units\" wording.\n */\nfunction describeInterval(plan: MaintenancePlan, meterUnit?: string | null): string {\n const parts: string[] = [];\n if (plan.intervalDays != null) parts.push(`every ${plan.intervalDays} days`);\n if (plan.intervalMeter != null) parts.push(`every ${formatMeter(plan.intervalMeter, meterUnit)}`);\n // A plan with neither cannot be created any more, but one could predate the\n // guard — say so plainly rather than rendering an empty cell.\n return parts.length > 0 ? parts.join(' or ') : 'no interval — never comes due';\n}\n\n/** The due state, in the words a farmer would use. */\nfunction describeDue(entry: DueMaintenancePlan): string {\n if (entry.isOverdue) {\n if (entry.daysUntilDue != null && entry.daysUntilDue < 0) {\n const days = Math.abs(entry.daysUntilDue);\n return `${days} day${days === 1 ? '' : 's'} overdue`;\n }\n if (entry.meterUntilDue != null && entry.meterUntilDue <= 0) {\n return `${formatMeter(Math.abs(entry.meterUntilDue), entry.meterUnit)} past due`;\n }\n return 'Overdue';\n }\n if (entry.daysUntilDue != null) {\n if (entry.daysUntilDue === 0) return 'Due today';\n return `Due in ${entry.daysUntilDue} day${entry.daysUntilDue === 1 ? '' : 's'}`;\n }\n return 'Due now';\n}\n\nexport function MaintenanceMaintenancePlansPage() {\n const { assets } = useAssetHandlerStore();\n const [search, setSearch] = useState('');\n const [horizon, setHorizon] = useState('0');\n const [includeInactive, setIncludeInactive] = useState(false);\n const [dialogOpen, setDialogOpen] = useState(false);\n const [editing, setEditing] = useState<MaintenancePlan | null>(null);\n\n const { plans, duePlans, loading, error, updatePlan, deletePlan, generateRecords, busy } =\n useMaintenancePlans({ includeInactive, withinDays: Number(horizon) });\n\n const assetName = useMemo(() => {\n const byId = new Map(assets.map((asset) => [asset.id, asset.name]));\n return (id: string) => byId.get(id) ?? 'Unknown asset';\n }, [assets]);\n\n // Due rows carry the unit on the entry; plan-table rows have to look it up.\n const assetMeterUnit = useMemo(() => {\n const byId = new Map(assets.map((asset) => [asset.id, asset.meterUnit ?? null]));\n return (id: string) => byId.get(id) ?? null;\n }, [assets]);\n\n const filteredPlans = useMemo(() => {\n const needle = search.trim().toLowerCase();\n if (!needle) return plans;\n return plans.filter(\n (plan) =>\n plan.title.toLowerCase().includes(needle) ||\n assetName(plan.assetId).toLowerCase().includes(needle)\n );\n }, [plans, search, assetName]);\n\n const overdueCount = duePlans.filter((entry) => entry.isOverdue).length;\n\n const openCreate = () => {\n setEditing(null);\n setDialogOpen(true);\n };\n const openEdit = (plan: MaintenancePlan) => {\n setEditing(plan);\n setDialogOpen(true);\n };\n\n const handleDelete = (plan: MaintenancePlan) => {\n void deletePlan({ variables: { id: plan.id } });\n };\n\n const handleToggleActive = (plan: MaintenancePlan) => {\n void updatePlan({ variables: { id: plan.id, input: { isActive: !plan.isActive } } });\n };\n\n const handleGenerate = (dryRun: boolean) => {\n void generateRecords({\n variables: { input: { withinDays: Number(horizon), dryRun } },\n }).then((result) => {\n const outcome = result.data?.generateDueMaintenanceRecords;\n if (!outcome) return;\n if (dryRun) {\n notify.info(\n `${outcome.generated} job${outcome.generated === 1 ? '' : 's'} would be raised`,\n outcome.skipped > 0\n ? `${outcome.skipped} plan${outcome.skipped === 1 ? '' : 's'} skipped — their previous job is still open.`\n : 'Nothing is waiting on an open job.'\n );\n return;\n }\n if (outcome.generated === 0 && outcome.skipped === 0) {\n notify.info('Nothing was due', 'No plan has reached its next service point.');\n return;\n }\n notify.success(\n `${outcome.generated} job${outcome.generated === 1 ? '' : 's'} raised`,\n outcome.skipped > 0\n ? `${outcome.skipped} skipped — their previous job is still open.`\n : undefined\n );\n });\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <SectionHeader\n hero\n icon={CalendarClock}\n title=\"Maintenance Plans\"\n description=\"Recurring service schedules for your machinery\"\n actions={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"mr-2 size-4\" />\n New plan\n </Button>\n }\n />\n\n <PagePurpose>\n Say &ldquo;service the tractor every 90 days&rdquo; or &ldquo;every 250 engine hours&rdquo;\n once, and let the schedule do the remembering. When a plan falls due, preview it and choose\n when to raise a maintenance job with its own checklist; completing that job starts the next\n cycle.\n </PagePurpose>\n\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <StatTile\n label=\"Active plans\"\n value={plans.filter((plan) => plan.isActive).length}\n icon={CalendarClock}\n />\n <StatTile label=\"Due now or soon\" value={duePlans.length} icon={Gauge} />\n <StatTile\n label=\"Overdue\"\n value={overdueCount}\n icon={AlertTriangle}\n {...(overdueCount > 0\n ? { delta: 'needs attention', deltaTone: 'negative' as const }\n : {})}\n />\n </div>\n\n {error && (\n <GlassCard className=\"p-4\">\n <p className=\"text-sm text-status-error-text\">\n Could not load maintenance plans: {error.message}\n </p>\n </GlassCard>\n )}\n\n {/* ── What needs servicing ───────────────────────────────────────── */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title=\"What needs servicing\"\n description=\"Plans that have reached their next service point.\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <Select value={horizon} onValueChange={setHorizon}>\n <SelectTrigger className=\"w-44\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"0\">Due now</SelectItem>\n <SelectItem value=\"7\">Within a week</SelectItem>\n <SelectItem value=\"14\">Within a fortnight</SelectItem>\n <SelectItem value=\"30\">Within a month</SelectItem>\n </SelectContent>\n </Select>\n <Button variant=\"outline\" onClick={() => handleGenerate(true)} disabled={busy}>\n Preview\n </Button>\n <Button\n onClick={() => handleGenerate(false)}\n disabled={busy || duePlans.length === 0}\n >\n {busy ? (\n <Loader2 className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <Play className=\"mr-2 size-4\" />\n )}\n Raise the work\n </Button>\n </div>\n }\n />\n\n {loading && duePlans.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-secondary\">Loading…</p>\n ) : duePlans.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n Nothing is due{horizon === '0' ? ' right now' : ` in the next ${horizon} days`}.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-subtle\">\n {duePlans.map((entry) => (\n <li key={entry.plan.id} className=\"flex flex-wrap items-center gap-3 py-3\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {entry.plan.title}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {assetName(entry.plan.assetId)} ·{' '}\n {describeInterval(entry.plan, entry.meterUnit)}\n </p>\n </div>\n <div className=\"text-right text-xs text-text-secondary\">\n <p\n className={cn(\n 'font-medium',\n entry.isOverdue ? 'text-status-error-text' : 'text-text-primary'\n )}\n >\n {describeDue(entry)}\n </p>\n <p>\n {entry.dueReason === 'METER'\n ? `Meter ${entry.currentMeter ?? '—'} of ${entry.nextDueMeter ?? '—'}`\n : `Due ${formatDate(entry.nextDueAt)}`}\n </p>\n </div>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* ── All plans ──────────────────────────────────────────────────── */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title=\"All plans\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 size-4 -translate-y-1/2 text-text-tertiary\" />\n <Input\n className=\"w-56 pl-8\"\n placeholder=\"Search plans or assets\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n <Button variant=\"outline\" onClick={() => setIncludeInactive((prev) => !prev)}>\n {includeInactive ? 'Hide paused' : 'Show paused'}\n </Button>\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 size-4\" /> New plan\n </Button>\n </div>\n }\n />\n\n {filteredPlans.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={plans.length === 0 ? 'No maintenance plans yet' : 'No plans match that search'}\n description={\n plans.length === 0\n ? 'Create a plan to service a machine every so many days or usage units, instead of remembering by hand.'\n : 'Try a different asset or plan name.'\n }\n action={\n plans.length === 0 ? (\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 size-4\" /> New plan\n </Button>\n ) : undefined\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-xs text-text-secondary\">\n <th className=\"py-2 pr-4 font-medium\">Plan</th>\n <th className=\"py-2 pr-4 font-medium\">Asset</th>\n <th className=\"py-2 pr-4 font-medium\">Recurs</th>\n <th className=\"py-2 pr-4 font-medium\">Last serviced</th>\n <th className=\"py-2 pr-4 font-medium\">Priority</th>\n <th className=\"py-2 pr-4 font-medium\">Status</th>\n <th className=\"py-2 font-medium text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {filteredPlans.map((plan) => (\n <tr key={plan.id} className=\"border-b border-border-subtle last:border-0\">\n <td className=\"py-3 pr-4\">\n <p className=\"font-medium text-text-primary\">{plan.title}</p>\n <p className=\"text-xs text-text-secondary\">{humanize(plan.type)}</p>\n </td>\n <td className=\"py-3 pr-4 text-text-secondary\">{assetName(plan.assetId)}</td>\n <td className=\"py-3 pr-4 text-text-secondary\">\n {describeInterval(plan, assetMeterUnit(plan.assetId))}\n </td>\n <td className=\"py-3 pr-4 text-text-secondary\">\n {formatDate(plan.lastServiceAt)}\n </td>\n <td className=\"py-3 pr-4\">\n <Pill tone={PRIORITY_PILL[plan.priority] ?? PRIORITY_PILL.LOW!}>\n {humanize(plan.priority)}\n </Pill>\n </td>\n <td className=\"py-3 pr-4\">\n <Pill\n tone={\n plan.isActive\n ? 'bg-status-success-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-secondary'\n }\n >\n {plan.isActive ? 'Active' : 'Paused'}\n </Pill>\n </td>\n <td className=\"py-3 text-right\">\n <div className=\"flex justify-end gap-1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handleToggleActive(plan)}\n disabled={busy}\n >\n {plan.isActive ? 'Pause' : 'Resume'}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => openEdit(plan)}\n aria-label={`Edit ${plan.title}`}\n >\n <Pencil className=\"size-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => handleDelete(plan)}\n disabled={busy}\n aria-label={`Delete ${plan.title}`}\n >\n <Trash2 className=\"size-4\" />\n </Button>\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </GlassCard>\n\n <MaintenancePlanDialog open={dialogOpen} onOpenChange={setDialogOpen} plan={editing} />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAqDA,IAAM,KAAY,MAChB,EACG,aAAa,CACb,QAAQ,MAAM,IAAI,CAClB,QAAQ,QAAQ,MAAM,EAAE,aAAa,CAAC,EAErC,IAAwC;CAC5C,UAAU;CACV,MAAM;CACN,QAAQ;CACR,KAAK;CACN;AAED,SAAS,EAAK,EAAE,SAAM,eAAyD;AAC7E,QACE,kBAAC,QAAD;EACE,WAAW,EAAG,yEAAyE,EAAK;EAE3F;EACI,CAAA;;AAIX,IAAM,KAAc,MAClB,IAAM,IAAI,KAAK,EAAI,CAAC,mBAAmB,KAAA,GAAW,EAAE,WAAW,UAAU,CAAC,GAAG;AAS/E,SAAS,EAAiB,GAAuB,GAAmC;CAClF,IAAM,IAAkB,EAAE;AAK1B,QAJI,EAAK,gBAAgB,QAAM,EAAM,KAAK,SAAS,EAAK,aAAa,OAAO,EACxE,EAAK,iBAAiB,QAAM,EAAM,KAAK,SAAS,EAAY,EAAK,eAAe,EAAU,GAAG,EAG1F,EAAM,SAAS,IAAI,EAAM,KAAK,OAAO,GAAG;;AAIjD,SAAS,EAAY,GAAmC;AACtD,KAAI,EAAM,WAAW;AACnB,MAAI,EAAM,gBAAgB,QAAQ,EAAM,eAAe,GAAG;GACxD,IAAM,IAAO,KAAK,IAAI,EAAM,aAAa;AACzC,UAAO,GAAG,EAAK,MAAM,MAAS,IAAI,KAAK,IAAI;;AAK7C,SAHI,EAAM,iBAAiB,QAAQ,EAAM,iBAAiB,IACjD,GAAG,EAAY,KAAK,IAAI,EAAM,cAAc,EAAE,EAAM,UAAU,CAAC,aAEjE;;AAMT,QAJI,EAAM,gBAAgB,OAInB,YAHD,EAAM,iBAAiB,IAAU,cAC9B,UAAU,EAAM,aAAa,MAAM,EAAM,iBAAiB,IAAI,KAAK;;AAK9E,SAAgB,IAAkC;CAChD,IAAM,EAAE,cAAW,GAAsB,EACnC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAS,KAAc,EAAS,IAAI,EACrC,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAS,KAAc,EAAiC,KAAK,EAE9D,EAAE,UAAO,aAAU,YAAS,UAAO,eAAY,eAAY,oBAAiB,YAChF,EAAoB;EAAE;EAAiB,YAAY,OAAO,EAAQ;EAAE,CAAC,EAEjE,IAAY,QAAc;EAC9B,IAAM,IAAO,IAAI,IAAI,EAAO,KAAK,MAAU,CAAC,EAAM,IAAI,EAAM,KAAK,CAAC,CAAC;AACnE,UAAQ,MAAe,EAAK,IAAI,EAAG,IAAI;IACtC,CAAC,EAAO,CAAC,EAGN,KAAiB,QAAc;EACnC,IAAM,IAAO,IAAI,IAAI,EAAO,KAAK,MAAU,CAAC,EAAM,IAAI,EAAM,aAAa,KAAK,CAAC,CAAC;AAChF,UAAQ,MAAe,EAAK,IAAI,EAAG,IAAI;IACtC,CAAC,EAAO,CAAC,EAEN,IAAgB,QAAc;EAClC,IAAM,IAAS,EAAO,MAAM,CAAC,aAAa;AAE1C,SADK,IACE,EAAM,QACV,MACC,EAAK,MAAM,aAAa,CAAC,SAAS,EAAO,IACzC,EAAU,EAAK,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAO,CACzD,GALmB;IAMnB;EAAC;EAAO;EAAQ;EAAU,CAAC,EAExB,IAAe,EAAS,QAAQ,MAAU,EAAM,UAAU,CAAC,QAE3D,UAAmB;AAEvB,EADA,EAAW,KAAK,EAChB,EAAc,GAAK;IAEf,MAAY,MAA0B;AAE1C,EADA,EAAW,EAAK,EAChB,EAAc,GAAK;IAGf,MAAgB,MAA0B;AACzC,IAAW,EAAE,WAAW,EAAE,IAAI,EAAK,IAAI,EAAE,CAAC;IAG3C,MAAsB,MAA0B;AAC/C,IAAW,EAAE,WAAW;GAAE,IAAI,EAAK;GAAI,OAAO,EAAE,UAAU,CAAC,EAAK,UAAU;GAAE,EAAE,CAAC;IAGhF,KAAkB,MAAoB;AACrC,IAAgB,EACnB,WAAW,EAAE,OAAO;GAAE,YAAY,OAAO,EAAQ;GAAE;GAAQ,EAAE,EAC9D,CAAC,CAAC,MAAM,MAAW;GAClB,IAAM,IAAU,EAAO,MAAM;AACxB,UACL;QAAI,GAAQ;AACV,OAAO,KACL,GAAG,EAAQ,UAAU,MAAM,EAAQ,cAAc,IAAI,KAAK,IAAI,mBAC9D,EAAQ,UAAU,IACd,GAAG,EAAQ,QAAQ,OAAO,EAAQ,YAAY,IAAI,KAAK,IAAI,gDAC3D,qCACL;AACD;;AAEF,QAAI,EAAQ,cAAc,KAAK,EAAQ,YAAY,GAAG;AACpD,OAAO,KAAK,mBAAmB,8CAA8C;AAC7E;;AAEF,MAAO,QACL,GAAG,EAAQ,UAAU,MAAM,EAAQ,cAAc,IAAI,KAAK,IAAI,UAC9D,EAAQ,UAAU,IACd,GAAG,EAAQ,QAAQ,gDACnB,KAAA,EACL;;IACD;;AAGJ,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,MAAA;IACA,MAAM;IACN,OAAM;IACN,aAAY;IACZ,SACE,kBAAC,GAAD;KAAQ,MAAK;KAAK,SAAS;eAA3B,CACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,WAEzB;;IAEX,CAAA;GAEF,kBAAC,GAAD,EAAA,UAAa,sQAKC,CAAA;GAEd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAM,QAAQ,MAAS,EAAK,SAAS,CAAC;MAC7C,MAAM;MACN,CAAA;KACF,kBAAC,GAAD;MAAU,OAAM;MAAkB,OAAO,EAAS;MAAQ,MAAM;MAAS,CAAA;KACzE,kBAAC,GAAD;MACE,OAAM;MACN,OAAO;MACP,MAAM;MACN,GAAK,IAAe,IAChB;OAAE,OAAO;OAAmB,WAAW;OAAqB,GAC5D,EAAE;MACN,CAAA;KACE;;GAEL,KACC,kBAAC,GAAD;IAAW,WAAU;cACnB,kBAAC,KAAD;KAAG,WAAU;eAAb,CAA8C,sCACT,EAAM,QACvC;;IACM,CAAA;GAId,kBAAC,GAAD;IAAW,WAAU;cAArB,CACE,kBAAC,GAAD;KACE,OAAM;KACN,aAAY;KACZ,SACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,IAAD;QAAQ,OAAO;QAAS,eAAe;kBAAvC,CACE,kBAAC,GAAD;SAAe,WAAU;mBACvB,kBAAC,GAAD,EAAe,CAAA;SACD,CAAA,EAChB,kBAAC,IAAD,EAAA,UAAA;SACE,kBAAC,GAAD;UAAY,OAAM;oBAAI;UAAoB,CAAA;SAC1C,kBAAC,GAAD;UAAY,OAAM;oBAAI;UAA0B,CAAA;SAChD,kBAAC,GAAD;UAAY,OAAM;oBAAK;UAA+B,CAAA;SACtD,kBAAC,GAAD;UAAY,OAAM;oBAAK;UAA2B,CAAA;SACpC,EAAA,CAAA,CACT;;OACT,kBAAC,GAAD;QAAQ,SAAQ;QAAU,eAAe,EAAe,GAAK;QAAE,UAAU;kBAAM;QAEtE,CAAA;OACT,kBAAC,GAAD;QACE,eAAe,EAAe,GAAM;QACpC,UAAU,KAAQ,EAAS,WAAW;kBAFxC,CAIG,IACC,kBAAC,IAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAChC,iBAEK;;OACL;;KAER,CAAA,EAED,KAAW,EAAS,WAAW,IAC9B,kBAAC,KAAD;KAAG,WAAU;eAA+C;KAAY,CAAA,GACtE,EAAS,WAAW,IACtB,kBAAC,KAAD;KAAG,WAAU;eAAb;MAA4D;MAC3C,MAAY,MAAM,eAAe,gBAAgB,EAAQ;MAAO;MAC7E;SAEJ,kBAAC,MAAD;KAAI,WAAU;eACX,EAAS,KAAK,MACb,kBAAC,MAAD;MAAwB,WAAU;gBAAlC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,KAAK;QACV,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAU,EAAM,KAAK,QAAQ;SAAC;SAAG;SACjC,EAAiB,EAAM,MAAM,EAAM,UAAU;SAC5C;UACA;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QACE,WAAW,EACT,eACA,EAAM,YAAY,2BAA2B,oBAC9C;kBAEA,EAAY,EAAM;QACjB,CAAA,EACJ,kBAAC,KAAD,EAAA,UACG,EAAM,cAAc,UACjB,SAAS,EAAM,gBAAgB,IAAI,MAAM,EAAM,gBAAgB,QAC/D,OAAO,EAAW,EAAM,UAAU,IACpC,CAAA,CACA;SACH;QAzBI,EAAM,KAAK,GAyBf,CACL;KACC,CAAA,CAEG;;GAGZ,kBAAC,GAAD;IAAW,WAAU;cAArB,CACE,kBAAC,GAAD;KACE,OAAM;KACN,SACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,sEAAuE,CAAA,EACzF,kBAAC,GAAD;SACE,WAAU;SACV,aAAY;SACZ,OAAO;SACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;SAC1C,CAAA,CACE;;OACN,kBAAC,GAAD;QAAQ,SAAQ;QAAU,eAAe,GAAoB,MAAS,CAAC,EAAK;kBACzE,IAAkB,gBAAgB;QAC5B,CAAA;OACT,kBAAC,GAAD;QAAQ,SAAS;kBAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,YACzB;;OACL;;KAER,CAAA,EAED,EAAc,WAAW,IACxB,kBAAC,GAAD;KACE,cAAa;KACb,OAAO,EAAM,WAAW,IAAI,6BAA6B;KACzD,aACE,EAAM,WAAW,IACb,0GACA;KAEN,QACE,EAAM,WAAW,IACf,kBAAC,GAAD;MAAQ,SAAS;gBAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,YACzB;UACP,KAAA;KAEN,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAwB;SAAS,CAAA;QAC/C,kBAAC,MAAD;SAAI,WAAU;mBAAwB;SAAU,CAAA;QAChD,kBAAC,MAAD;SAAI,WAAU;mBAAwB;SAAW,CAAA;QACjD,kBAAC,MAAD;SAAI,WAAU;mBAAwB;SAAkB,CAAA;QACxD,kBAAC,MAAD;SAAI,WAAU;mBAAwB;SAAa,CAAA;QACnD,kBAAC,MAAD;SAAI,WAAU;mBAAwB;SAAW,CAAA;QACjD,kBAAC,MAAD;SAAI,WAAU;mBAA8B;SAAY,CAAA;QACrD;UACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAc,KAAK,MAClB,kBAAC,MAAD;OAAkB,WAAU;iBAA5B;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAd,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAiC,EAAK;UAAU,CAAA,EAC7D,kBAAC,KAAD;UAAG,WAAU;oBAA+B,EAAS,EAAK,KAAK;UAAK,CAAA,CACjE;;QACL,kBAAC,MAAD;SAAI,WAAU;mBAAiC,EAAU,EAAK,QAAQ;SAAM,CAAA;QAC5E,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAiB,GAAM,GAAe,EAAK,QAAQ,CAAC;SAClD,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAW,EAAK,cAAc;SAC5B,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACZ,kBAAC,GAAD;UAAM,MAAM,EAAc,EAAK,aAAa,EAAc;oBACvD,EAAS,EAAK,SAAS;UACnB,CAAA;SACJ,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACZ,kBAAC,GAAD;UACE,MACE,EAAK,WACD,sDACA;oBAGL,EAAK,WAAW,WAAW;UACvB,CAAA;SACJ,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBACZ,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,GAAD;YACE,SAAQ;YACR,MAAK;YACL,eAAe,GAAmB,EAAK;YACvC,UAAU;sBAET,EAAK,WAAW,UAAU;YACpB,CAAA;WACT,kBAAC,GAAD;YACE,SAAQ;YACR,MAAK;YACL,eAAe,GAAS,EAAK;YAC7B,cAAY,QAAQ,EAAK;sBAEzB,kBAAC,IAAD,EAAQ,WAAU,UAAW,CAAA;YACtB,CAAA;WACT,kBAAC,GAAD;YACE,SAAQ;YACR,MAAK;YACL,eAAe,GAAa,EAAK;YACjC,UAAU;YACV,cAAY,UAAU,EAAK;sBAE3B,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;YACtB,CAAA;WACL;;SACH,CAAA;QACF;SAzDI,EAAK,GAyDT,CACL,EACI,CAAA,CACF;;KACJ,CAAA,CAEE;;GAEZ,kBAAC,GAAD;IAAuB,MAAM;IAAY,cAAc;IAAe,MAAM;IAAW,CAAA;GACnF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-assethandler",
3
- "version": "2026.826.2",
3
+ "version": "2026.826.3",
4
4
  "description": "Burdenoff AssetHandler Microfrontend",
5
5
  "type": "module",
6
6
  "files": [
@@ -27,7 +27,7 @@
27
27
  "scripts": {
28
28
  "dev": "vite",
29
29
  "build": "tsc && bash scripts/build-limits.sh --gate vite build",
30
- "codegen": "graphql-codegen --project wspace && graphql-codegen --project global",
30
+ "codegen": "graphql-codegen --project wspace",
31
31
  "schema:pull": "graphql-codegen --project schema-wspace && graphql-codegen --project schema-global",
32
32
  "lint": "eslint .",
33
33
  "lint:sanity": "if [ \"${CI:-}\" = \"true\" ]; then bun run lint; else bun run lint -- --cache --cache-location .eslintcache; fi",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=global-operations.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"global-operations.d.ts","sourceRoot":"","sources":["../../src/generated/global-operations.ts"],"names":[],"mappings":""}