@burdenoff/microfe-assethandler 2026.813.2 → 2026.814.1

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.
Files changed (27) hide show
  1. package/dist/assethandler/hooks/useMaintenancePlans.d.ts +2 -0
  2. package/dist/assethandler/hooks/useMaintenancePlans.d.ts.map +1 -1
  3. package/dist/assethandler/hooks/useMaintenancePlans.js +1 -0
  4. package/dist/assethandler/hooks/useMaintenancePlans.js.map +1 -1
  5. package/dist/assethandler/ops/MaintenancePlanDialog.js +14 -7
  6. package/dist/assethandler/ops/MaintenancePlanDialog.js.map +1 -1
  7. package/dist/assethandler/pages/AssetsAssetCreatePage.d.ts.map +1 -1
  8. package/dist/assethandler/pages/AssetsAssetCreatePage.js +115 -101
  9. package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
  10. package/dist/assethandler/pages/AssetsAssetDetailPage.d.ts.map +1 -1
  11. package/dist/assethandler/pages/AssetsAssetDetailPage.js +121 -102
  12. package/dist/assethandler/pages/AssetsAssetDetailPage.js.map +1 -1
  13. package/dist/assethandler/pages/MaintenanceMaintenancePlansPage.d.ts.map +1 -1
  14. package/dist/assethandler/pages/MaintenanceMaintenancePlansPage.js +119 -114
  15. package/dist/assethandler/pages/MaintenanceMaintenancePlansPage.js.map +1 -1
  16. package/dist/assethandler/providers/AssetHandlerProvider.d.ts.map +1 -1
  17. package/dist/assethandler/providers/AssetHandlerProvider.js +1 -0
  18. package/dist/assethandler/providers/AssetHandlerProvider.js.map +1 -1
  19. package/dist/assethandler/store/assethandlerStore.d.ts +3 -0
  20. package/dist/assethandler/store/assethandlerStore.d.ts.map +1 -1
  21. package/dist/assethandler/store/assethandlerStore.js +1 -0
  22. package/dist/assethandler/store/assethandlerStore.js.map +1 -1
  23. package/dist/assethandler/utils/meter.d.ts +51 -0
  24. package/dist/assethandler/utils/meter.d.ts.map +1 -0
  25. package/dist/assethandler/utils/meter.js +51 -0
  26. package/dist/assethandler/utils/meter.js.map +1 -0
  27. package/package.json +1 -1
@@ -44,6 +44,8 @@ export interface DueMaintenancePlan {
44
44
  nextDueAt: string | null;
45
45
  nextDueMeter: number | null;
46
46
  currentMeter: number | null;
47
+ /** What that reading counts in. Null when the asset carries no unit. */
48
+ meterUnit: string | null;
47
49
  isDue: boolean;
48
50
  isOverdue: boolean;
49
51
  daysUntilDue: number | null;
@@ -1 +1 @@
1
- {"version":3,"file":"useMaintenancePlans.d.ts","sourceRoot":"","sources":["../../../src/assethandler/hooks/useMaintenancePlans.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAY,MAAM,sBAAsB,CAAC;AAG7D;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,uBAAuB,gCAqBnC,CAAC;AAuEF,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;CAC/F;AAED,sDAAsD;AACtD,eAAO,MAAM,UAAU,mEAAoE,CAAC;AAC5F,eAAO,MAAM,eAAe,gDAAiD,CAAC;AAI9E,wBAAgB,mBAAmB,CACjC,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;;;;;;;;;uCAoC/B,cAAc;;;;;EAyBhD"}
1
+ {"version":3,"file":"useMaintenancePlans.d.ts","sourceRoot":"","sources":["../../../src/assethandler/hooks/useMaintenancePlans.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAY,MAAM,sBAAsB,CAAC;AAG7D;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,uBAAuB,gCAqBnC,CAAC;AAwEF,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,wEAAwE;IACxE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;CAC/F;AAED,sDAAsD;AACtD,eAAO,MAAM,UAAU,mEAAoE,CAAC;AAC5F,eAAO,MAAM,eAAe,gDAAiD,CAAC;AAI9E,wBAAgB,mBAAmB,CACjC,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;;;;;;;;;uCAoC/B,cAAc;;;;;EAyBhD"}
@@ -40,6 +40,7 @@ var i = t`
40
40
  nextDueAt
41
41
  nextDueMeter
42
42
  currentMeter
43
+ meterUnit
43
44
  isDue
44
45
  isOverdue
45
46
  daysUntilDue
@@ -1 +1 @@
1
- {"version":3,"file":"useMaintenancePlans.js","names":[],"sources":["../../../src/assethandler/hooks/useMaintenancePlans.ts"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { notify } from '../utils/toast';\n\n/**\n * Recurring preventive-maintenance plans (BOFF-6031).\n *\n * These operations post-date the MFE's bootstrap query, so they are hand-written\n * `gql` documents rather than additions to it: the bootstrap is one large query\n * every page pays for, and plans are only needed on the plans surface.\n *\n * ── What the backend guarantees, and what this must therefore not do ─────────\n *\n * `nextDueAt` / `nextDueMeter` are DERIVED server-side on every read — they are\n * not stored. So this layer never caches a due date or recomputes one locally;\n * it re-reads after anything that could move it.\n *\n * `currentMeter`, `nextDueMeter`, `daysUntilDue` and `meterUntilDue` are all\n * genuinely nullable and null means \"not applicable\", never zero. An asset that\n * nobody meters has no meter reading; rendering that as 0 would claim a machine\n * sitting at zero hours.\n */\n\nexport const MAINTENANCE_PLAN_FIELDS = gql`\n fragment MaintenancePlanFields on MaintenancePlan {\n id\n assetId\n title\n description\n type\n priority\n isActive\n intervalDays\n intervalMeter\n lastServiceAt\n lastServiceMeter\n startAt\n startMeter\n leadTimeDays\n estimatedCost\n checklistTemplate\n createdAt\n updatedAt\n }\n`;\n\nconst MAINTENANCE_PLANS_QUERY = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n query AssetHandlerMaintenancePlans($assetId: ID, $includeInactive: Boolean) {\n maintenancePlans(assetId: $assetId, includeInactive: $includeInactive) {\n ...MaintenancePlanFields\n }\n }\n`;\n\nconst DUE_MAINTENANCE_PLANS_QUERY = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n query AssetHandlerDueMaintenancePlans($assetId: ID, $withinDays: Int) {\n dueMaintenancePlans(assetId: $assetId, withinDays: $withinDays) {\n plan {\n ...MaintenancePlanFields\n }\n nextDueAt\n nextDueMeter\n currentMeter\n isDue\n isOverdue\n daysUntilDue\n meterUntilDue\n dueReason\n }\n }\n`;\n\nconst CREATE_MAINTENANCE_PLAN = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n mutation AssetHandlerCreateMaintenancePlan($input: CreateMaintenancePlanInput!) {\n createMaintenancePlan(input: $input) {\n ...MaintenancePlanFields\n }\n }\n`;\n\nconst UPDATE_MAINTENANCE_PLAN = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n mutation AssetHandlerUpdateMaintenancePlan($id: ID!, $input: UpdateMaintenancePlanInput!) {\n updateMaintenancePlan(id: $id, input: $input) {\n ...MaintenancePlanFields\n }\n }\n`;\n\nconst DELETE_MAINTENANCE_PLAN = gql`\n mutation AssetHandlerDeleteMaintenancePlan($id: ID!) {\n deleteMaintenancePlan(id: $id)\n }\n`;\n\nconst GENERATE_DUE_RECORDS = gql`\n mutation AssetHandlerGenerateDueMaintenanceRecords($input: GenerateMaintenanceRecordsInput) {\n generateDueMaintenanceRecords(input: $input) {\n dryRun\n generated\n skipped\n notes\n records {\n id\n title\n scheduledAt\n dueMeter\n }\n }\n }\n`;\n\nexport interface MaintenancePlan {\n id: string;\n assetId: string;\n title: string;\n description: string | null;\n type: string;\n priority: string;\n isActive: boolean;\n intervalDays: number | null;\n intervalMeter: number | null;\n lastServiceAt: string | null;\n lastServiceMeter: number | null;\n startAt: string | null;\n startMeter: number | null;\n leadTimeDays: number | null;\n estimatedCost: number | null;\n checklistTemplate: Record<string, unknown>[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface DueMaintenancePlan {\n plan: MaintenancePlan;\n nextDueAt: string | null;\n nextDueMeter: number | null;\n currentMeter: number | null;\n isDue: boolean;\n isOverdue: boolean;\n daysUntilDue: number | null;\n meterUntilDue: number | null;\n dueReason: string | null;\n}\n\nexport interface GenerateResult {\n dryRun: boolean;\n generated: number;\n skipped: number;\n notes: string[];\n records: { id: string; title: string; scheduledAt: string | null; dueMeter: number | null }[];\n}\n\n/** The four values the backend accepts for a PLAN. */\nexport const PLAN_TYPES = ['PREVENTIVE', 'CORRECTIVE', 'PREDICTIVE', 'INSPECTION'] as const;\nexport const PLAN_PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\n\nconst REFETCH = ['AssetHandlerMaintenancePlans', 'AssetHandlerDueMaintenancePlans'];\n\nexport function useMaintenancePlans(\n options: { includeInactive?: boolean; withinDays?: number } = {}\n) {\n const { includeInactive = false, withinDays = 0 } = options;\n\n const plansQuery = useQuery<{ maintenancePlans: MaintenancePlan[] }>(MAINTENANCE_PLANS_QUERY, {\n variables: { includeInactive },\n fetchPolicy: 'cache-and-network',\n });\n\n const dueQuery = useQuery<{ dueMaintenancePlans: DueMaintenancePlan[] }>(\n DUE_MAINTENANCE_PLANS_QUERY,\n { variables: { withinDays }, fetchPolicy: 'cache-and-network' }\n );\n\n const [createPlan, createState] = useMutation(CREATE_MAINTENANCE_PLAN, {\n refetchQueries: REFETCH,\n onCompleted: () => notify.success('Plan created', 'It will raise work when it falls due.'),\n onError: (error) => notify.error('Could not create the plan', error.message),\n });\n\n const [updatePlan, updateState] = useMutation(UPDATE_MAINTENANCE_PLAN, {\n refetchQueries: REFETCH,\n onCompleted: () => notify.success('Plan updated'),\n onError: (error) => notify.error('Could not update the plan', error.message),\n });\n\n const [deletePlan, deleteState] = useMutation(DELETE_MAINTENANCE_PLAN, {\n refetchQueries: REFETCH,\n // Said plainly, because it is the reassuring half: deleting a schedule does\n // not delete the service history it produced.\n onCompleted: () =>\n notify.success('Plan deleted', 'The service records it raised have been kept.'),\n onError: (error) => notify.error('Could not delete the plan', error.message),\n });\n\n const [generateRecords, generateState] = useMutation<{\n generateDueMaintenanceRecords: GenerateResult;\n }>(GENERATE_DUE_RECORDS, {\n // Generated records are MaintenanceRecords, which the bootstrap owns — so\n // the scheduler and calendar pages must be refreshed too, or the new work\n // is invisible everywhere except here.\n refetchQueries: [...REFETCH, 'AssetHandlerBootstrap'],\n onError: (error) => notify.error('Could not raise the work', error.message),\n });\n\n return {\n plans: plansQuery.data?.maintenancePlans ?? [],\n duePlans: dueQuery.data?.dueMaintenancePlans ?? [],\n loading: plansQuery.loading || dueQuery.loading,\n error: plansQuery.error ?? dueQuery.error ?? null,\n refetch: () => {\n void plansQuery.refetch();\n void dueQuery.refetch();\n },\n createPlan,\n updatePlan,\n deletePlan,\n generateRecords,\n busy:\n createState.loading || updateState.loading || deleteState.loading || generateState.loading,\n };\n}\n"],"mappings":";;;;AAuBA,IAAa,IAA0B,CAAG;;;;;;;;;;;;;;;;;;;;;GAuBpC,IAA0B,CAAG;IAC/B,EAAwB;;;;;;GAQtB,IAA8B,CAAG;IACnC,EAAwB;;;;;;;;;;;;;;;;GAkBtB,IAA0B,CAAG;IAC/B,EAAwB;;;;;;GAQtB,IAA0B,CAAG;IAC/B,EAAwB;;;;;;GAQtB,IAA0B,CAAG;;;;GAM7B,IAAuB,CAAG;;;;;;;;;;;;;;;GA2DnB,IAAa;CAAC;CAAc;CAAc;CAAc;CAAa,EACrE,IAAkB;CAAC;CAAY;CAAQ;CAAU;CAAM,EAE9D,IAAU,CAAC,gCAAgC,kCAAkC;AAEnF,SAAgB,EACd,IAA8D,EAAE,EAChE;CACA,IAAM,EAAE,qBAAkB,IAAO,gBAAa,MAAM,GAE9C,IAAa,EAAkD,GAAyB;EAC5F,WAAW,EAAE,oBAAiB;EAC9B,aAAa;EACd,CAAC,EAEI,IAAW,EACf,GACA;EAAE,WAAW,EAAE,eAAY;EAAE,aAAa;EAAqB,CAChE,EAEK,CAAC,GAAY,KAAe,EAAY,GAAyB;EACrE,gBAAgB;EAChB,mBAAmB,EAAO,QAAQ,gBAAgB,wCAAwC;EAC1F,UAAU,MAAU,EAAO,MAAM,6BAA6B,EAAM,QAAQ;EAC7E,CAAC,EAEI,CAAC,GAAY,KAAe,EAAY,GAAyB;EACrE,gBAAgB;EAChB,mBAAmB,EAAO,QAAQ,eAAe;EACjD,UAAU,MAAU,EAAO,MAAM,6BAA6B,EAAM,QAAQ;EAC7E,CAAC,EAEI,CAAC,GAAY,KAAe,EAAY,GAAyB;EACrE,gBAAgB;EAGhB,mBACE,EAAO,QAAQ,gBAAgB,gDAAgD;EACjF,UAAU,MAAU,EAAO,MAAM,6BAA6B,EAAM,QAAQ;EAC7E,CAAC,EAEI,CAAC,GAAiB,KAAiB,EAEtC,GAAsB;EAIvB,gBAAgB,CAAC,GAAG,GAAS,wBAAwB;EACrD,UAAU,MAAU,EAAO,MAAM,4BAA4B,EAAM,QAAQ;EAC5E,CAAC;AAEF,QAAO;EACL,OAAO,EAAW,MAAM,oBAAoB,EAAE;EAC9C,UAAU,EAAS,MAAM,uBAAuB,EAAE;EAClD,SAAS,EAAW,WAAW,EAAS;EACxC,OAAO,EAAW,SAAS,EAAS,SAAS;EAC7C,eAAe;AAER,GADA,EAAW,SAAS,EACpB,EAAS,SAAS;;EAEzB;EACA;EACA;EACA;EACA,MACE,EAAY,WAAW,EAAY,WAAW,EAAY,WAAW,EAAc;EACtF"}
1
+ {"version":3,"file":"useMaintenancePlans.js","names":[],"sources":["../../../src/assethandler/hooks/useMaintenancePlans.ts"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { notify } from '../utils/toast';\n\n/**\n * Recurring preventive-maintenance plans (BOFF-6031).\n *\n * These operations post-date the MFE's bootstrap query, so they are hand-written\n * `gql` documents rather than additions to it: the bootstrap is one large query\n * every page pays for, and plans are only needed on the plans surface.\n *\n * ── What the backend guarantees, and what this must therefore not do ─────────\n *\n * `nextDueAt` / `nextDueMeter` are DERIVED server-side on every read — they are\n * not stored. So this layer never caches a due date or recomputes one locally;\n * it re-reads after anything that could move it.\n *\n * `currentMeter`, `nextDueMeter`, `daysUntilDue` and `meterUntilDue` are all\n * genuinely nullable and null means \"not applicable\", never zero. An asset that\n * nobody meters has no meter reading; rendering that as 0 would claim a machine\n * sitting at zero hours.\n */\n\nexport const MAINTENANCE_PLAN_FIELDS = gql`\n fragment MaintenancePlanFields on MaintenancePlan {\n id\n assetId\n title\n description\n type\n priority\n isActive\n intervalDays\n intervalMeter\n lastServiceAt\n lastServiceMeter\n startAt\n startMeter\n leadTimeDays\n estimatedCost\n checklistTemplate\n createdAt\n updatedAt\n }\n`;\n\nconst MAINTENANCE_PLANS_QUERY = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n query AssetHandlerMaintenancePlans($assetId: ID, $includeInactive: Boolean) {\n maintenancePlans(assetId: $assetId, includeInactive: $includeInactive) {\n ...MaintenancePlanFields\n }\n }\n`;\n\nconst DUE_MAINTENANCE_PLANS_QUERY = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n query AssetHandlerDueMaintenancePlans($assetId: ID, $withinDays: Int) {\n dueMaintenancePlans(assetId: $assetId, withinDays: $withinDays) {\n plan {\n ...MaintenancePlanFields\n }\n nextDueAt\n nextDueMeter\n currentMeter\n meterUnit\n isDue\n isOverdue\n daysUntilDue\n meterUntilDue\n dueReason\n }\n }\n`;\n\nconst CREATE_MAINTENANCE_PLAN = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n mutation AssetHandlerCreateMaintenancePlan($input: CreateMaintenancePlanInput!) {\n createMaintenancePlan(input: $input) {\n ...MaintenancePlanFields\n }\n }\n`;\n\nconst UPDATE_MAINTENANCE_PLAN = gql`\n ${MAINTENANCE_PLAN_FIELDS}\n mutation AssetHandlerUpdateMaintenancePlan($id: ID!, $input: UpdateMaintenancePlanInput!) {\n updateMaintenancePlan(id: $id, input: $input) {\n ...MaintenancePlanFields\n }\n }\n`;\n\nconst DELETE_MAINTENANCE_PLAN = gql`\n mutation AssetHandlerDeleteMaintenancePlan($id: ID!) {\n deleteMaintenancePlan(id: $id)\n }\n`;\n\nconst GENERATE_DUE_RECORDS = gql`\n mutation AssetHandlerGenerateDueMaintenanceRecords($input: GenerateMaintenanceRecordsInput) {\n generateDueMaintenanceRecords(input: $input) {\n dryRun\n generated\n skipped\n notes\n records {\n id\n title\n scheduledAt\n dueMeter\n }\n }\n }\n`;\n\nexport interface MaintenancePlan {\n id: string;\n assetId: string;\n title: string;\n description: string | null;\n type: string;\n priority: string;\n isActive: boolean;\n intervalDays: number | null;\n intervalMeter: number | null;\n lastServiceAt: string | null;\n lastServiceMeter: number | null;\n startAt: string | null;\n startMeter: number | null;\n leadTimeDays: number | null;\n estimatedCost: number | null;\n checklistTemplate: Record<string, unknown>[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface DueMaintenancePlan {\n plan: MaintenancePlan;\n nextDueAt: string | null;\n nextDueMeter: number | null;\n currentMeter: number | null;\n /** What that reading counts in. Null when the asset carries no unit. */\n meterUnit: string | null;\n isDue: boolean;\n isOverdue: boolean;\n daysUntilDue: number | null;\n meterUntilDue: number | null;\n dueReason: string | null;\n}\n\nexport interface GenerateResult {\n dryRun: boolean;\n generated: number;\n skipped: number;\n notes: string[];\n records: { id: string; title: string; scheduledAt: string | null; dueMeter: number | null }[];\n}\n\n/** The four values the backend accepts for a PLAN. */\nexport const PLAN_TYPES = ['PREVENTIVE', 'CORRECTIVE', 'PREDICTIVE', 'INSPECTION'] as const;\nexport const PLAN_PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\n\nconst REFETCH = ['AssetHandlerMaintenancePlans', 'AssetHandlerDueMaintenancePlans'];\n\nexport function useMaintenancePlans(\n options: { includeInactive?: boolean; withinDays?: number } = {}\n) {\n const { includeInactive = false, withinDays = 0 } = options;\n\n const plansQuery = useQuery<{ maintenancePlans: MaintenancePlan[] }>(MAINTENANCE_PLANS_QUERY, {\n variables: { includeInactive },\n fetchPolicy: 'cache-and-network',\n });\n\n const dueQuery = useQuery<{ dueMaintenancePlans: DueMaintenancePlan[] }>(\n DUE_MAINTENANCE_PLANS_QUERY,\n { variables: { withinDays }, fetchPolicy: 'cache-and-network' }\n );\n\n const [createPlan, createState] = useMutation(CREATE_MAINTENANCE_PLAN, {\n refetchQueries: REFETCH,\n onCompleted: () => notify.success('Plan created', 'It will raise work when it falls due.'),\n onError: (error) => notify.error('Could not create the plan', error.message),\n });\n\n const [updatePlan, updateState] = useMutation(UPDATE_MAINTENANCE_PLAN, {\n refetchQueries: REFETCH,\n onCompleted: () => notify.success('Plan updated'),\n onError: (error) => notify.error('Could not update the plan', error.message),\n });\n\n const [deletePlan, deleteState] = useMutation(DELETE_MAINTENANCE_PLAN, {\n refetchQueries: REFETCH,\n // Said plainly, because it is the reassuring half: deleting a schedule does\n // not delete the service history it produced.\n onCompleted: () =>\n notify.success('Plan deleted', 'The service records it raised have been kept.'),\n onError: (error) => notify.error('Could not delete the plan', error.message),\n });\n\n const [generateRecords, generateState] = useMutation<{\n generateDueMaintenanceRecords: GenerateResult;\n }>(GENERATE_DUE_RECORDS, {\n // Generated records are MaintenanceRecords, which the bootstrap owns — so\n // the scheduler and calendar pages must be refreshed too, or the new work\n // is invisible everywhere except here.\n refetchQueries: [...REFETCH, 'AssetHandlerBootstrap'],\n onError: (error) => notify.error('Could not raise the work', error.message),\n });\n\n return {\n plans: plansQuery.data?.maintenancePlans ?? [],\n duePlans: dueQuery.data?.dueMaintenancePlans ?? [],\n loading: plansQuery.loading || dueQuery.loading,\n error: plansQuery.error ?? dueQuery.error ?? null,\n refetch: () => {\n void plansQuery.refetch();\n void dueQuery.refetch();\n },\n createPlan,\n updatePlan,\n deletePlan,\n generateRecords,\n busy:\n createState.loading || updateState.loading || deleteState.loading || generateState.loading,\n };\n}\n"],"mappings":";;;;AAuBA,IAAa,IAA0B,CAAG;;;;;;;;;;;;;;;;;;;;;GAuBpC,IAA0B,CAAG;IAC/B,EAAwB;;;;;;GAQtB,IAA8B,CAAG;IACnC,EAAwB;;;;;;;;;;;;;;;;;GAmBtB,IAA0B,CAAG;IAC/B,EAAwB;;;;;;GAQtB,IAA0B,CAAG;IAC/B,EAAwB;;;;;;GAQtB,IAA0B,CAAG;;;;GAM7B,IAAuB,CAAG;;;;;;;;;;;;;;;GA6DnB,IAAa;CAAC;CAAc;CAAc;CAAc;CAAa,EACrE,IAAkB;CAAC;CAAY;CAAQ;CAAU;CAAM,EAE9D,IAAU,CAAC,gCAAgC,kCAAkC;AAEnF,SAAgB,EACd,IAA8D,EAAE,EAChE;CACA,IAAM,EAAE,qBAAkB,IAAO,gBAAa,MAAM,GAE9C,IAAa,EAAkD,GAAyB;EAC5F,WAAW,EAAE,oBAAiB;EAC9B,aAAa;EACd,CAAC,EAEI,IAAW,EACf,GACA;EAAE,WAAW,EAAE,eAAY;EAAE,aAAa;EAAqB,CAChE,EAEK,CAAC,GAAY,KAAe,EAAY,GAAyB;EACrE,gBAAgB;EAChB,mBAAmB,EAAO,QAAQ,gBAAgB,wCAAwC;EAC1F,UAAU,MAAU,EAAO,MAAM,6BAA6B,EAAM,QAAQ;EAC7E,CAAC,EAEI,CAAC,GAAY,KAAe,EAAY,GAAyB;EACrE,gBAAgB;EAChB,mBAAmB,EAAO,QAAQ,eAAe;EACjD,UAAU,MAAU,EAAO,MAAM,6BAA6B,EAAM,QAAQ;EAC7E,CAAC,EAEI,CAAC,GAAY,KAAe,EAAY,GAAyB;EACrE,gBAAgB;EAGhB,mBACE,EAAO,QAAQ,gBAAgB,gDAAgD;EACjF,UAAU,MAAU,EAAO,MAAM,6BAA6B,EAAM,QAAQ;EAC7E,CAAC,EAEI,CAAC,GAAiB,KAAiB,EAEtC,GAAsB;EAIvB,gBAAgB,CAAC,GAAG,GAAS,wBAAwB;EACrD,UAAU,MAAU,EAAO,MAAM,4BAA4B,EAAM,QAAQ;EAC5E,CAAC;AAEF,QAAO;EACL,OAAO,EAAW,MAAM,oBAAoB,EAAE;EAC9C,UAAU,EAAS,MAAM,uBAAuB,EAAE;EAClD,SAAS,EAAW,WAAW,EAAS;EACxC,OAAO,EAAW,SAAS,EAAS,SAAS;EAC7C,eAAe;AAER,GADA,EAAW,SAAS,EACpB,EAAS,SAAS;;EAEzB;EACA;EACA;EACA;EACA,MACE,EAAY,WAAW,EAAY,WAAW,EAAY,WAAW,EAAc;EACtF"}
@@ -149,13 +149,20 @@ function k({ plan: f, onOpenChange: p }) {
149
149
  }),
150
150
  /* @__PURE__ */ c("div", {
151
151
  className: "space-y-2",
152
- children: [/* @__PURE__ */ s(d, { children: "Meter at start" }), /* @__PURE__ */ s(u, {
153
- type: "number",
154
- min: 0,
155
- placeholder: "Current reading",
156
- value: J,
157
- onChange: (e) => Y(e.target.value)
158
- })]
152
+ children: [
153
+ /* @__PURE__ */ s(d, { children: "Meter at start" }),
154
+ /* @__PURE__ */ s(u, {
155
+ type: "number",
156
+ min: 0,
157
+ placeholder: "e.g. 1200",
158
+ value: J,
159
+ onChange: (e) => Y(e.target.value)
160
+ }),
161
+ /* @__PURE__ */ s("p", {
162
+ className: "text-xs text-text-tertiary",
163
+ children: "Where this plan starts counting from — not the asset’s reading. Record that on the asset itself."
164
+ })
165
+ ]
159
166
  }),
160
167
  /* @__PURE__ */ c("div", {
161
168
  className: "space-y-2",
@@ -1 +1 @@
1
- {"version":3,"file":"MaintenancePlanDialog.js","names":[],"sources":["../../../src/assethandler/ops/MaintenancePlanDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { Loader2, Plus, X } from 'lucide-react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport {\n PLAN_PRIORITIES,\n PLAN_TYPES,\n type MaintenancePlan,\n useMaintenancePlans,\n} from '../hooks/useMaintenancePlans';\n\n/**\n * Create or edit a recurring maintenance plan (BOFF-6031).\n *\n * The one rule this form has to get right: a plan needs AT LEAST ONE interval.\n * The backend refuses a plan without one — such a plan can never come due, which\n * is the exact gap the feature closes — so the form says so up front rather than\n * letting someone fill in eight fields and be rejected on submit.\n *\n * Only the four backend plan types are offered. The one-off \"Schedule\n * Maintenance\" dialog also lists CALIBRATION and EMERGENCY, which a free-form\n * MaintenanceRecord.type accepts but a plan does not.\n */\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\n/** `<input type=\"date\">` wants YYYY-MM-DD; the API speaks ISO-8601. */\nconst toDateInput = (iso: string | null): string => (iso ? iso.slice(0, 10) : '');\n\nconst numberOrNull = (raw: string): number | null => {\n const trimmed = raw.trim();\n if (trimmed === '') return null;\n const parsed = Number(trimmed);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\nconst checklistTasks = (plan?: MaintenancePlan | null): string[] => {\n const tasks = (plan?.checklistTemplate ?? [])\n .map((item) => (typeof item?.task === 'string' ? item.task : ''))\n .filter((task) => task !== '');\n return tasks.length > 0 ? tasks : [''];\n};\n\n/**\n * The dialog is a thin shell; the form below is REMOUNTED per plan via `key`.\n *\n * That is what keeps the fields correct when you edit one plan, close, then edit\n * another. Re-seeding the same mounted form from an effect would do the same job\n * but through a cascade of setState calls during render — which React (and this\n * repo's lint) rightly rejects. A fresh mount reads its initial state straight\n * from props, with no synchronisation to get wrong.\n */\nexport function MaintenancePlanDialog({\n open,\n onOpenChange,\n plan,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Present when editing; absent when creating. */\n plan?: MaintenancePlan | null;\n}) {\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n {open && (\n <MaintenancePlanForm\n key={plan?.id ?? 'new'}\n plan={plan ?? null}\n onOpenChange={onOpenChange}\n />\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nfunction MaintenancePlanForm({\n plan,\n onOpenChange,\n}: {\n plan: MaintenancePlan | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const { assets } = useAssetHandlerStore();\n const { createPlan, updatePlan, busy } = useMaintenancePlans();\n const isEdit = Boolean(plan);\n\n const [assetId, setAssetId] = useState(plan?.assetId ?? '');\n const [title, setTitle] = useState(plan?.title ?? '');\n const [description, setDescription] = useState(plan?.description ?? '');\n const [type, setType] = useState<string>(plan?.type ?? 'PREVENTIVE');\n const [priority, setPriority] = useState<string>(plan?.priority ?? 'MEDIUM');\n const [intervalDays, setIntervalDays] = useState(\n plan?.intervalDays != null ? String(plan.intervalDays) : ''\n );\n const [intervalMeter, setIntervalMeter] = useState(\n plan?.intervalMeter != null ? String(plan.intervalMeter) : ''\n );\n const [startAt, setStartAt] = useState(toDateInput(plan?.startAt ?? null));\n const [startMeter, setStartMeter] = useState(\n plan?.startMeter != null ? String(plan.startMeter) : ''\n );\n const [leadTimeDays, setLeadTimeDays] = useState(\n plan?.leadTimeDays != null ? String(plan.leadTimeDays) : ''\n );\n const [estimatedCost, setEstimatedCost] = useState(\n plan?.estimatedCost != null ? String(plan.estimatedCost) : ''\n );\n const [checklist, setChecklist] = useState<string[]>(checklistTasks(plan));\n\n const setChecklistItem = (index: number, value: string) =>\n setChecklist((prev) => prev.map((task, i) => (i === index ? value : task)));\n const addChecklistItem = () => setChecklist((prev) => [...prev, '']);\n const removeChecklistItem = (index: number) =>\n setChecklist((prev) => (prev.length === 1 ? prev : prev.filter((_, i) => i !== index)));\n\n const handleSubmit = () => {\n if (!isEdit && !assetId) {\n notify.warning('Asset required', 'Pick the machine this plan is for.');\n return;\n }\n if (!title.trim()) {\n notify.warning('Title required', 'Give the plan a name, e.g. \"250-hour tractor service\".');\n return;\n }\n const days = numberOrNull(intervalDays);\n const meter = numberOrNull(intervalMeter);\n if (days === null && meter === null) {\n notify.warning(\n 'This plan needs an interval',\n 'Set a day interval, a usage interval, or both — otherwise it can never come due.'\n );\n return;\n }\n\n const cleanedChecklist = checklist\n .map((task) => task.trim())\n .filter((task) => task !== '')\n .map((task) => ({ task, completed: false }));\n\n const input = {\n title: title.trim(),\n description: description.trim() || null,\n type,\n priority,\n intervalDays: days,\n intervalMeter: meter,\n startAt: startAt ? new Date(startAt).toISOString() : null,\n startMeter: numberOrNull(startMeter),\n leadTimeDays: numberOrNull(leadTimeDays),\n estimatedCost: numberOrNull(estimatedCost),\n checklistTemplate: cleanedChecklist,\n };\n\n const done = () => onOpenChange(false);\n if (isEdit && plan) {\n void updatePlan({ variables: { id: plan.id, input } }).then(done);\n } else {\n void createPlan({ variables: { input: { ...input, assetId } } }).then(done);\n }\n };\n\n return (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {isEdit ? 'Edit maintenance plan' : 'New maintenance plan'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n A recurring schedule. When it falls due it raises a maintenance job, and completing that\n job starts the next cycle.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4\">\n {!isEdit && (\n <div className=\"space-y-2\">\n <Label>Asset *</Label>\n <Select value={assetId} onValueChange={setAssetId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an asset\" />\n </SelectTrigger>\n <SelectContent>\n {assets.map((asset) => (\n <SelectItem key={asset.id} value={asset.id}>\n {asset.name}\n {asset.assetTag ? ` · ${asset.assetTag}` : ''}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input\n placeholder=\"e.g., 250-hour tractor service\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Description</Label>\n <Textarea\n placeholder=\"What does this service involve?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={type} onValueChange={setType}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PLAN_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {humanize(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Priority</Label>\n <Select value={priority} onValueChange={setPriority}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PLAN_PRIORITIES.map((p) => (\n <SelectItem key={p} value={p}>\n {humanize(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken p-4\">\n <p className=\"text-sm font-medium text-text-primary\">How often? *</p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Set at least one. With both, the plan comes due at whichever arrives first — the way a\n service book reads &ldquo;every 90 days or 250 hours&rdquo;.\n </p>\n <div className=\"mt-3 grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Every N days</Label>\n <Input\n type=\"number\"\n min={1}\n placeholder=\"90\"\n value={intervalDays}\n onChange={(e) => setIntervalDays(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Every N usage units</Label>\n <Input\n type=\"number\"\n min={1}\n placeholder=\"250\"\n value={intervalMeter}\n onChange={(e) => setIntervalMeter(e.target.value)}\n />\n <p className=\"text-xs text-text-tertiary\">\n Counts against the asset&rsquo;s usage meter. Assets without one never come due this\n way.\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Start counting from</Label>\n <Input type=\"date\" value={startAt} onChange={(e) => setStartAt(e.target.value)} />\n <p className=\"text-xs text-text-tertiary\">Defaults to today.</p>\n </div>\n <div className=\"space-y-2\">\n <Label>Meter at start</Label>\n <Input\n type=\"number\"\n min={0}\n placeholder=\"Current reading\"\n value={startMeter}\n onChange={(e) => setStartMeter(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Raise work N days early</Label>\n <Input\n type=\"number\"\n min={0}\n placeholder=\"0\"\n value={leadTimeDays}\n onChange={(e) => setLeadTimeDays(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label>Estimated cost</Label>\n <Input\n type=\"number\"\n min={0}\n placeholder=\"0.00\"\n value={estimatedCost}\n onChange={(e) => setEstimatedCost(e.target.value)}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Checklist template</Label>\n <p className=\"text-xs text-text-tertiary\">\n Copied onto every job this plan raises, so the procedure travels with the schedule.\n </p>\n {checklist.map((task, index) => (\n <div key={index} className=\"flex items-center gap-2\">\n <Input\n placeholder={`Step ${index + 1}`}\n value={task}\n onChange={(e) => setChecklistItem(index, e.target.value)}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => removeChecklistItem(index)}\n aria-label={`Remove step ${index + 1}`}\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n ))}\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addChecklistItem}>\n <Plus className=\"mr-1 size-4\" /> Add step\n </Button>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={busy}>\n {busy && <Loader2 className=\"mr-2 size-4 animate-spin\" />}\n {isEdit ? 'Save plan' : 'Create plan'}\n </Button>\n </ResponsiveDialogFooter>\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MaintenancePlanDialog.js","names":[],"sources":["../../../src/assethandler/ops/MaintenancePlanDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { Loader2, Plus, X } from 'lucide-react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport {\n PLAN_PRIORITIES,\n PLAN_TYPES,\n type MaintenancePlan,\n useMaintenancePlans,\n} from '../hooks/useMaintenancePlans';\n\n/**\n * Create or edit a recurring maintenance plan (BOFF-6031).\n *\n * The one rule this form has to get right: a plan needs AT LEAST ONE interval.\n * The backend refuses a plan without one — such a plan can never come due, which\n * is the exact gap the feature closes — so the form says so up front rather than\n * letting someone fill in eight fields and be rejected on submit.\n *\n * Only the four backend plan types are offered. The one-off \"Schedule\n * Maintenance\" dialog also lists CALIBRATION and EMERGENCY, which a free-form\n * MaintenanceRecord.type accepts but a plan does not.\n */\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\n/** `<input type=\"date\">` wants YYYY-MM-DD; the API speaks ISO-8601. */\nconst toDateInput = (iso: string | null): string => (iso ? iso.slice(0, 10) : '');\n\nconst numberOrNull = (raw: string): number | null => {\n const trimmed = raw.trim();\n if (trimmed === '') return null;\n const parsed = Number(trimmed);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\nconst checklistTasks = (plan?: MaintenancePlan | null): string[] => {\n const tasks = (plan?.checklistTemplate ?? [])\n .map((item) => (typeof item?.task === 'string' ? item.task : ''))\n .filter((task) => task !== '');\n return tasks.length > 0 ? tasks : [''];\n};\n\n/**\n * The dialog is a thin shell; the form below is REMOUNTED per plan via `key`.\n *\n * That is what keeps the fields correct when you edit one plan, close, then edit\n * another. Re-seeding the same mounted form from an effect would do the same job\n * but through a cascade of setState calls during render — which React (and this\n * repo's lint) rightly rejects. A fresh mount reads its initial state straight\n * from props, with no synchronisation to get wrong.\n */\nexport function MaintenancePlanDialog({\n open,\n onOpenChange,\n plan,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Present when editing; absent when creating. */\n plan?: MaintenancePlan | null;\n}) {\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n {open && (\n <MaintenancePlanForm\n key={plan?.id ?? 'new'}\n plan={plan ?? null}\n onOpenChange={onOpenChange}\n />\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nfunction MaintenancePlanForm({\n plan,\n onOpenChange,\n}: {\n plan: MaintenancePlan | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const { assets } = useAssetHandlerStore();\n const { createPlan, updatePlan, busy } = useMaintenancePlans();\n const isEdit = Boolean(plan);\n\n const [assetId, setAssetId] = useState(plan?.assetId ?? '');\n const [title, setTitle] = useState(plan?.title ?? '');\n const [description, setDescription] = useState(plan?.description ?? '');\n const [type, setType] = useState<string>(plan?.type ?? 'PREVENTIVE');\n const [priority, setPriority] = useState<string>(plan?.priority ?? 'MEDIUM');\n const [intervalDays, setIntervalDays] = useState(\n plan?.intervalDays != null ? String(plan.intervalDays) : ''\n );\n const [intervalMeter, setIntervalMeter] = useState(\n plan?.intervalMeter != null ? String(plan.intervalMeter) : ''\n );\n const [startAt, setStartAt] = useState(toDateInput(plan?.startAt ?? null));\n const [startMeter, setStartMeter] = useState(\n plan?.startMeter != null ? String(plan.startMeter) : ''\n );\n const [leadTimeDays, setLeadTimeDays] = useState(\n plan?.leadTimeDays != null ? String(plan.leadTimeDays) : ''\n );\n const [estimatedCost, setEstimatedCost] = useState(\n plan?.estimatedCost != null ? String(plan.estimatedCost) : ''\n );\n const [checklist, setChecklist] = useState<string[]>(checklistTasks(plan));\n\n const setChecklistItem = (index: number, value: string) =>\n setChecklist((prev) => prev.map((task, i) => (i === index ? value : task)));\n const addChecklistItem = () => setChecklist((prev) => [...prev, '']);\n const removeChecklistItem = (index: number) =>\n setChecklist((prev) => (prev.length === 1 ? prev : prev.filter((_, i) => i !== index)));\n\n const handleSubmit = () => {\n if (!isEdit && !assetId) {\n notify.warning('Asset required', 'Pick the machine this plan is for.');\n return;\n }\n if (!title.trim()) {\n notify.warning('Title required', 'Give the plan a name, e.g. \"250-hour tractor service\".');\n return;\n }\n const days = numberOrNull(intervalDays);\n const meter = numberOrNull(intervalMeter);\n if (days === null && meter === null) {\n notify.warning(\n 'This plan needs an interval',\n 'Set a day interval, a usage interval, or both — otherwise it can never come due.'\n );\n return;\n }\n\n const cleanedChecklist = checklist\n .map((task) => task.trim())\n .filter((task) => task !== '')\n .map((task) => ({ task, completed: false }));\n\n const input = {\n title: title.trim(),\n description: description.trim() || null,\n type,\n priority,\n intervalDays: days,\n intervalMeter: meter,\n startAt: startAt ? new Date(startAt).toISOString() : null,\n startMeter: numberOrNull(startMeter),\n leadTimeDays: numberOrNull(leadTimeDays),\n estimatedCost: numberOrNull(estimatedCost),\n checklistTemplate: cleanedChecklist,\n };\n\n const done = () => onOpenChange(false);\n if (isEdit && plan) {\n void updatePlan({ variables: { id: plan.id, input } }).then(done);\n } else {\n void createPlan({ variables: { input: { ...input, assetId } } }).then(done);\n }\n };\n\n return (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {isEdit ? 'Edit maintenance plan' : 'New maintenance plan'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n A recurring schedule. When it falls due it raises a maintenance job, and completing that\n job starts the next cycle.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4\">\n {!isEdit && (\n <div className=\"space-y-2\">\n <Label>Asset *</Label>\n <Select value={assetId} onValueChange={setAssetId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an asset\" />\n </SelectTrigger>\n <SelectContent>\n {assets.map((asset) => (\n <SelectItem key={asset.id} value={asset.id}>\n {asset.name}\n {asset.assetTag ? ` · ${asset.assetTag}` : ''}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input\n placeholder=\"e.g., 250-hour tractor service\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Description</Label>\n <Textarea\n placeholder=\"What does this service involve?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={type} onValueChange={setType}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PLAN_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {humanize(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Priority</Label>\n <Select value={priority} onValueChange={setPriority}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PLAN_PRIORITIES.map((p) => (\n <SelectItem key={p} value={p}>\n {humanize(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken p-4\">\n <p className=\"text-sm font-medium text-text-primary\">How often? *</p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Set at least one. With both, the plan comes due at whichever arrives first — the way a\n service book reads &ldquo;every 90 days or 250 hours&rdquo;.\n </p>\n <div className=\"mt-3 grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Every N days</Label>\n <Input\n type=\"number\"\n min={1}\n placeholder=\"90\"\n value={intervalDays}\n onChange={(e) => setIntervalDays(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Every N usage units</Label>\n <Input\n type=\"number\"\n min={1}\n placeholder=\"250\"\n value={intervalMeter}\n onChange={(e) => setIntervalMeter(e.target.value)}\n />\n <p className=\"text-xs text-text-tertiary\">\n Counts against the asset&rsquo;s usage meter. Assets without one never come due this\n way.\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Start counting from</Label>\n <Input type=\"date\" value={startAt} onChange={(e) => setStartAt(e.target.value)} />\n <p className=\"text-xs text-text-tertiary\">Defaults to today.</p>\n </div>\n <div className=\"space-y-2\">\n <Label>Meter at start</Label>\n <Input\n type=\"number\"\n min={0}\n placeholder=\"e.g. 1200\"\n value={startMeter}\n onChange={(e) => setStartMeter(e.target.value)}\n />\n {/*\n This writes the PLAN's anchor, not the asset's meter. Its placeholder used\n to read \"Current reading\", which made it look like the field where you\n record the tractor's hours — type 1200 there and the plan anchors at 1200,\n computes a next-due of 1450, and then never comes due because the ASSET's\n meter is still empty. Two inputs that looked identical and behaved\n completely differently (BOFF-6032).\n */}\n <p className=\"text-xs text-text-tertiary\">\n Where this plan starts counting from — not the asset&rsquo;s reading. Record that on\n the asset itself.\n </p>\n </div>\n <div className=\"space-y-2\">\n <Label>Raise work N days early</Label>\n <Input\n type=\"number\"\n min={0}\n placeholder=\"0\"\n value={leadTimeDays}\n onChange={(e) => setLeadTimeDays(e.target.value)}\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label>Estimated cost</Label>\n <Input\n type=\"number\"\n min={0}\n placeholder=\"0.00\"\n value={estimatedCost}\n onChange={(e) => setEstimatedCost(e.target.value)}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Checklist template</Label>\n <p className=\"text-xs text-text-tertiary\">\n Copied onto every job this plan raises, so the procedure travels with the schedule.\n </p>\n {checklist.map((task, index) => (\n <div key={index} className=\"flex items-center gap-2\">\n <Input\n placeholder={`Step ${index + 1}`}\n value={task}\n onChange={(e) => setChecklistItem(index, e.target.value)}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => removeChecklistItem(index)}\n aria-label={`Remove step ${index + 1}`}\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n ))}\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addChecklistItem}>\n <Plus className=\"mr-1 size-4\" /> Add step\n </Button>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={busy}>\n {busy && <Loader2 className=\"mr-2 size-4 animate-spin\" />}\n {isEdit ? 'Save plan' : 'Create plan'}\n </Button>\n </ResponsiveDialogFooter>\n </>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsAssetCreatePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCreatePage.tsx"],"names":[],"mappings":"AAkLA,wBAAgB,qBAAqB,gCA8jBpC"}
1
+ {"version":3,"file":"AssetsAssetCreatePage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/AssetsAssetCreatePage.tsx"],"names":[],"mappings":"AAqLA,wBAAgB,qBAAqB,gCAwlBpC"}