@burdenoff/microfe-assethandler 2026.703.5 → 2026.703.7

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 (67) hide show
  1. package/dist/assethandler/components/ActivityTimeline.js.map +1 -1
  2. package/dist/assethandler/components/AttachmentsPanel.js +2 -1
  3. package/dist/assethandler/components/AttachmentsPanel.js.map +1 -1
  4. package/dist/assethandler/ops/NewWorkOrderDialog.js +164 -0
  5. package/dist/assethandler/ops/NewWorkOrderDialog.js.map +1 -0
  6. package/dist/assethandler/ops/ScheduleMaintenanceDialog.js +197 -0
  7. package/dist/assethandler/ops/ScheduleMaintenanceDialog.js.map +1 -0
  8. package/dist/assethandler/pages/AiPredictionsPage.js +112 -54
  9. package/dist/assethandler/pages/AiPredictionsPage.js.map +1 -1
  10. package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
  11. package/dist/assethandler/pages/AssetsAssetDetailPage.js.map +1 -1
  12. package/dist/assethandler/pages/CmdbBrowserPage.js +195 -36
  13. package/dist/assethandler/pages/CmdbBrowserPage.js.map +1 -1
  14. package/dist/assethandler/pages/DigitalTwinPage.js +162 -31
  15. package/dist/assethandler/pages/DigitalTwinPage.js.map +1 -1
  16. package/dist/assethandler/pages/DiscoveryJobsPage.js +85 -55
  17. package/dist/assethandler/pages/DiscoveryJobsPage.js.map +1 -1
  18. package/dist/assethandler/pages/EsgDashboardPage.js +189 -60
  19. package/dist/assethandler/pages/EsgDashboardPage.js.map +1 -1
  20. package/dist/assethandler/pages/FinanceBudgetDetailPage.js.map +1 -1
  21. package/dist/assethandler/pages/FinanceFinanceHubPage.js.map +1 -1
  22. package/dist/assethandler/pages/FinanceInvoiceDetailPage.js.map +1 -1
  23. package/dist/assethandler/pages/IotDevicesPage.js +206 -38
  24. package/dist/assethandler/pages/IotDevicesPage.js.map +1 -1
  25. package/dist/assethandler/pages/ItilChangeDetailPage.js +221 -114
  26. package/dist/assethandler/pages/ItilChangeDetailPage.js.map +1 -1
  27. package/dist/assethandler/pages/ItilChangeManagementPage.js +86 -61
  28. package/dist/assethandler/pages/ItilChangeManagementPage.js.map +1 -1
  29. package/dist/assethandler/pages/ItilDialogs.js +361 -0
  30. package/dist/assethandler/pages/ItilDialogs.js.map +1 -0
  31. package/dist/assethandler/pages/ItilIncidentDetailPage.js +370 -105
  32. package/dist/assethandler/pages/ItilIncidentDetailPage.js.map +1 -1
  33. package/dist/assethandler/pages/ItilIncidentManagementPage.js +130 -103
  34. package/dist/assethandler/pages/ItilIncidentManagementPage.js.map +1 -1
  35. package/dist/assethandler/pages/ItilItilhubPage.js +186 -132
  36. package/dist/assethandler/pages/ItilItilhubPage.js.map +1 -1
  37. package/dist/assethandler/pages/ItilProblemDetailPage.js +195 -96
  38. package/dist/assethandler/pages/ItilProblemDetailPage.js.map +1 -1
  39. package/dist/assethandler/pages/ItilProblemManagementPage.js +83 -58
  40. package/dist/assethandler/pages/ItilProblemManagementPage.js.map +1 -1
  41. package/dist/assethandler/pages/KnowledgeBasePage.js +205 -75
  42. package/dist/assethandler/pages/KnowledgeBasePage.js.map +1 -1
  43. package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js +212 -96
  44. package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js.map +1 -1
  45. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js +70 -38
  46. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js.map +1 -1
  47. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js +470 -116
  48. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js.map +1 -1
  49. package/dist/assethandler/pages/OperationsOperationsHubPage.js +154 -102
  50. package/dist/assethandler/pages/OperationsOperationsHubPage.js.map +1 -1
  51. package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js +386 -79
  52. package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js.map +1 -1
  53. package/dist/assethandler/pages/ProvenancePage.js +102 -56
  54. package/dist/assethandler/pages/ProvenancePage.js.map +1 -1
  55. package/dist/assethandler/pages/ReportsDashboardPage.js.map +1 -1
  56. package/dist/assethandler/pages/SamLicensesPage.js +186 -36
  57. package/dist/assethandler/pages/SamLicensesPage.js.map +1 -1
  58. package/dist/assethandler/pages/SecurityComplianceDetailPage.js +147 -65
  59. package/dist/assethandler/pages/SecurityComplianceDetailPage.js.map +1 -1
  60. package/dist/assethandler/pages/SecurityPolicyDetailPage.js +349 -124
  61. package/dist/assethandler/pages/SecurityPolicyDetailPage.js.map +1 -1
  62. package/dist/assethandler/pages/SecuritySecurityEventDetailPage.js +191 -116
  63. package/dist/assethandler/pages/SecuritySecurityEventDetailPage.js.map +1 -1
  64. package/dist/assethandler/pages/SecuritySecurityHubPage.js +421 -184
  65. package/dist/assethandler/pages/SecuritySecurityHubPage.js.map +1 -1
  66. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js.map +1 -1
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ActivityTimeline.js","names":[],"sources":["../../../src/assethandler/components/ActivityTimeline.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Activity } from 'lucide-react';\nimport { useAssetHandler } from '../providers/AssetHandlerProvider';\n\n/**\n * Recent activity for a single AssetHandler resource, sourced from the\n * federated wspace-activity-svc `getActivities` query (fed by be-sdk\n * `trackOperation` on the backend). Read-only, with a graceful empty state.\n */\nexport interface ActivityTimelineProps {\n resourceId: string;\n /** Optional cap on rows shown. Defaults to 20. */\n limit?: number;\n}\n\ninterface ActivityLogNode {\n id: string;\n action: string;\n description?: string | null;\n timestamp: string;\n userId: string;\n resourceType?: string | null;\n}\n\ninterface GetActivitiesResult {\n getActivities: ActivityLogNode[];\n}\n\nconst GET_ACTIVITIES_QUERY = gql`\n query AssetHandlerResourceActivities($filter: ActivityLogFilter) {\n getActivities(filter: $filter) {\n id\n action\n description\n timestamp\n userId\n resourceType\n }\n }\n`;\n\nexport function ActivityTimeline({ resourceId, limit = 20 }: ActivityTimelineProps) {\n const { workspaceId } = useAssetHandler();\n\n const { data, loading } = useQuery<GetActivitiesResult>(GET_ACTIVITIES_QUERY, {\n variables: {\n filter: {\n resourceId,\n ...(workspaceId ? { workspaceId } : {}),\n limit,\n },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n skip: !resourceId,\n });\n\n const activities = useMemo(() => data?.getActivities ?? [], [data]);\n\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Activity className=\"h-4 w-4\" />\n Activity\n </CardTitle>\n </CardHeader>\n <CardContent>\n {loading && activities.length === 0 ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : activities.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No activity yet\"\n description=\"Changes to this record — updates, status changes and assignments — will show up here.\"\n />\n ) : (\n <ol className=\"relative space-y-4 border-l border-border-subtle pl-4\">\n {activities.map((item) => (\n <li key={item.id} className=\"relative\">\n <span\n className=\"absolute -left-[1.3125rem] top-1.5 h-2 w-2 rounded-full bg-primary\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm font-medium capitalize\">{item.action.replace(/[._]/g, ' ')}</p>\n {item.description ? (\n <p className=\"text-sm text-text-secondary\">{item.description}</p>\n ) : null}\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {new Date(item.timestamp).toLocaleString()}\n </p>\n </li>\n ))}\n </ol>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;;;;AAqCA,IAAM,IAAuB,CAAG;;;;;;;;;;;;AAahC,SAAgB,EAAiB,EAAE,eAAY,WAAQ,MAA6B;CAClF,IAAM,EAAE,mBAAgB,GAAiB,EAEnC,EAAE,SAAM,eAAY,EAA8B,GAAsB;EAC5E,WAAW,EACT,QAAQ;GACN;GACA,GAAI,IAAc,EAAE,gBAAa,GAAG,EAAE;GACtC;GACD,EACF;EACD,aAAa;EACb,aAAa;EACb,MAAM,CAAC;EACR,CAAC,EAEI,IAAa,QAAc,GAAM,iBAAiB,EAAE,EAAE,CAAC,EAAK,CAAC;AAEnE,QACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAW,WAAU;YAArB,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,WAEtB;KACD,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,KAAW,EAAW,WAAW,IAChC,kBAAC,OAAD;EAAK,WAAU;YACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAEE,WAAU,+DACV,EAFK,EAEL,CACF;EACE,CAAA,GACJ,EAAW,WAAW,IACxB,kBAAC,GAAD;EACE,cAAa;EACb,OAAM;EACN,aAAY;EACZ,CAAA,GAEF,kBAAC,MAAD;EAAI,WAAU;YACX,EAAW,KAAK,MACf,kBAAC,MAAD;GAAkB,WAAU;aAA5B;IACE,kBAAC,QAAD;KACE,WAAU;KACV,eAAY;KACZ,CAAA;IACF,kBAAC,KAAD;KAAG,WAAU;eAAkC,EAAK,OAAO,QAAQ,SAAS,IAAI;KAAK,CAAA;IACpF,EAAK,cACJ,kBAAC,KAAD;KAAG,WAAU;eAA+B,EAAK;KAAgB,CAAA,GAC/D;IACJ,kBAAC,KAAD;KAAG,WAAU;eACV,IAAI,KAAK,EAAK,UAAU,CAAC,gBAAgB;KACxC,CAAA;IACD;KAZI,EAAK,GAYT,CACL;EACC,CAAA,EAEK,CAAA,CACT,EAAA,CAAA"}
1
+ {"version":3,"file":"ActivityTimeline.js","names":[],"sources":["../../../src/assethandler/components/ActivityTimeline.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Activity } from 'lucide-react';\nimport { useAssetHandler } from '../providers/AssetHandlerProvider';\n\n/**\n * Recent activity for a single AssetHandler resource, sourced from the\n * federated wspace-activity-svc `getActivities` query (fed by be-sdk\n * `trackOperation` on the backend). Read-only, with a graceful empty state.\n */\nexport interface ActivityTimelineProps {\n resourceId: string;\n /** Optional cap on rows shown. Defaults to 20. */\n limit?: number;\n}\n\ninterface ActivityLogNode {\n id: string;\n action: string;\n description?: string | null;\n timestamp: string;\n userId: string;\n resourceType?: string | null;\n}\n\ninterface GetActivitiesResult {\n getActivities: ActivityLogNode[];\n}\n\nconst GET_ACTIVITIES_QUERY = gql`\n query AssetHandlerResourceActivities($filter: ActivityLogFilter) {\n getActivities(filter: $filter) {\n id\n action\n description\n timestamp\n userId\n resourceType\n }\n }\n`;\n\nexport function ActivityTimeline({ resourceId, limit = 20 }: ActivityTimelineProps) {\n const { workspaceId } = useAssetHandler();\n\n const { data, loading } = useQuery<GetActivitiesResult>(GET_ACTIVITIES_QUERY, {\n variables: {\n filter: {\n resourceId,\n ...(workspaceId ? { workspaceId } : {}),\n limit,\n },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n skip: !resourceId,\n });\n\n const activities = useMemo(() => data?.getActivities ?? [], [data]);\n\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Activity className=\"h-4 w-4\" />\n Activity\n </CardTitle>\n </CardHeader>\n <CardContent>\n {loading && activities.length === 0 ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : activities.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"No activity yet\"\n description=\"Changes to this record — updates, status changes and assignments — will show up here.\"\n />\n ) : (\n <ol className=\"relative space-y-4 border-l border-border-subtle pl-4\">\n {activities.map((item) => (\n <li key={item.id} className=\"relative\">\n <span\n className=\"absolute -left-[1.3125rem] top-1.5 h-2 w-2 rounded-full bg-primary\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm font-medium capitalize\">\n {item.action.replace(/[._]/g, ' ')}\n </p>\n {item.description ? (\n <p className=\"text-sm text-text-secondary\">{item.description}</p>\n ) : null}\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {new Date(item.timestamp).toLocaleString()}\n </p>\n </li>\n ))}\n </ol>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;;;;AAqCA,IAAM,IAAuB,CAAG;;;;;;;;;;;;AAahC,SAAgB,EAAiB,EAAE,eAAY,WAAQ,MAA6B;CAClF,IAAM,EAAE,mBAAgB,GAAiB,EAEnC,EAAE,SAAM,eAAY,EAA8B,GAAsB;EAC5E,WAAW,EACT,QAAQ;GACN;GACA,GAAI,IAAc,EAAE,gBAAa,GAAG,EAAE;GACtC;GACD,EACF;EACD,aAAa;EACb,aAAa;EACb,MAAM,CAAC;EACR,CAAC,EAEI,IAAa,QAAc,GAAM,iBAAiB,EAAE,EAAE,CAAC,EAAK,CAAC;AAEnE,QACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAW,WAAU;YAArB,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,WAEtB;KACD,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,KAAW,EAAW,WAAW,IAChC,kBAAC,OAAD;EAAK,WAAU;YACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAEE,WAAU,+DACV,EAFK,EAEL,CACF;EACE,CAAA,GACJ,EAAW,WAAW,IACxB,kBAAC,GAAD;EACE,cAAa;EACb,OAAM;EACN,aAAY;EACZ,CAAA,GAEF,kBAAC,MAAD;EAAI,WAAU;YACX,EAAW,KAAK,MACf,kBAAC,MAAD;GAAkB,WAAU;aAA5B;IACE,kBAAC,QAAD;KACE,WAAU;KACV,eAAY;KACZ,CAAA;IACF,kBAAC,KAAD;KAAG,WAAU;eACV,EAAK,OAAO,QAAQ,SAAS,IAAI;KAChC,CAAA;IACH,EAAK,cACJ,kBAAC,KAAD;KAAG,WAAU;eAA+B,EAAK;KAAgB,CAAA,GAC/D;IACJ,kBAAC,KAAD;KAAG,WAAU;eACV,IAAI,KAAK,EAAK,UAAU,CAAC,gBAAgB;KACxC,CAAA;IACD;KAdI,EAAK,GAcT,CACL;EACC,CAAA,EAEK,CAAA,CACT,EAAA,CAAA"}
@@ -172,7 +172,8 @@ function D({ entityType: u, entityId: D }) {
172
172
  className: "text-xs text-text-secondary",
173
173
  children: [
174
174
  T(Number(e.size)),
175
- " · ",
175
+ " ·",
176
+ " ",
176
177
  new Date(e.createdAt).toLocaleDateString()
177
178
  ]
178
179
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"AttachmentsPanel.js","names":[],"sources":["../../../src/assethandler/components/AttachmentsPanel.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Paperclip, Upload, Link2, FileText, Loader2, Download } from 'lucide-react';\nimport { notify } from '../utils/toast';\n\n/**\n * Attach and reference files for an AssetHandler entity, backed by the\n * federated wspace-files-svc (same wspace gateway/Apollo client). Fully real —\n * lists workspace files via `getFiles`, uploads new files through the\n * `initiateUpload` → presigned PUT → `completeUpload` flow, and mints public\n * share links via `createFileShareLink`. No mock data.\n *\n * `entityType`/`entityId` scope the panel to the record it is mounted on and\n * are used for context labelling; uploaded files land in the workspace file\n * store so they can be shared and reused.\n */\nexport interface AttachmentsPanelProps {\n entityType: string;\n entityId: string;\n}\n\ninterface FileNode {\n id: string;\n name: string;\n extension: string;\n mimeType: string;\n size: number;\n downloadUrl?: string | null;\n createdAt: string;\n}\n\ninterface GetFilesResult {\n getFiles: {\n files: FileNode[];\n total: number;\n hasMore: boolean;\n };\n}\n\nconst GET_FILES_QUERY = gql`\n query AssetHandlerGetFiles($pagination: PaginationInput, $sort: FileSortInput) {\n getFiles(pagination: $pagination, sort: $sort) {\n total\n hasMore\n files {\n id\n name\n extension\n mimeType\n size\n downloadUrl\n createdAt\n }\n }\n }\n`;\n\nconst INITIATE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session {\n id\n }\n }\n }\n`;\n\nconst COMPLETE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file {\n id\n name\n }\n }\n }\n`;\n\nconst CREATE_SHARE_LINK_MUTATION = gql`\n mutation AssetHandlerCreateFileShareLink($input: CreateShareGrantLinkInput!) {\n createFileShareLink(input: $input) {\n id\n shareUrl\n }\n }\n`;\n\nfunction formatSize(bytes: number): string {\n if (!bytes) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n const value = bytes / Math.pow(1024, exponent);\n return `${value.toFixed(exponent === 0 ? 0 : 1)} ${units[exponent]}`;\n}\n\nfunction extensionOf(fileName: string): string {\n const dot = fileName.lastIndexOf('.');\n return dot >= 0 ? fileName.slice(dot + 1).toLowerCase() : 'bin';\n}\n\nexport function AttachmentsPanel({ entityType, entityId }: AttachmentsPanelProps) {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [uploading, setUploading] = useState(false);\n const [sharingId, setSharingId] = useState<string | null>(null);\n\n const { data, loading, refetch } = useQuery<GetFilesResult>(GET_FILES_QUERY, {\n variables: {\n pagination: { limit: 25, offset: 0 },\n sort: { field: 'CREATED_AT', order: 'DESC' },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n\n const [initiateUpload] = useMutation<{\n initiateUpload: { uploadUrl?: string | null; session: { id: string } };\n }>(INITIATE_UPLOAD_MUTATION);\n const [completeUpload] = useMutation(COMPLETE_UPLOAD_MUTATION);\n const [createShareLink] = useMutation<{\n createFileShareLink: { id: string; shareUrl?: string | null };\n }>(CREATE_SHARE_LINK_MUTATION);\n\n const files = useMemo(() => data?.getFiles.files ?? [], [data]);\n\n const handleFileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the input so selecting the same file again re-triggers change.\n event.target.value = '';\n if (!file) return;\n\n setUploading(true);\n try {\n const initiated = await initiateUpload({\n variables: {\n input: {\n fileName: file.name,\n extension: extensionOf(file.name),\n fileSize: file.size,\n mimeType: file.type || 'application/octet-stream',\n visibility: 'WORKSPACE',\n },\n },\n });\n\n const uploadUrl = initiated.data?.initiateUpload.uploadUrl;\n const sessionId = initiated.data?.initiateUpload.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('Upload could not be initialised.');\n }\n\n const putResponse = await fetch(uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!putResponse.ok) {\n throw new Error(`Storage rejected the upload (HTTP ${putResponse.status}).`);\n }\n\n await completeUpload({\n variables: { input: { sessionId, fileSize: file.size } },\n });\n\n notify.success('File uploaded', `${file.name} is now available on this record.`);\n await refetch();\n } catch (error) {\n notify.error(\n 'Upload failed',\n error instanceof Error ? error.message : 'The file could not be uploaded.'\n );\n } finally {\n setUploading(false);\n }\n };\n\n const handleShare = async (fileId: string) => {\n setSharingId(fileId);\n try {\n const result = await createShareLink({\n variables: { input: { resourceId: fileId, role: 'VIEWER' } },\n });\n const url = result.data?.createFileShareLink.shareUrl;\n if (!url) {\n throw new Error('No share link was returned.');\n }\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(url);\n notify.success('Share link copied', 'A view-only link is on your clipboard.');\n } else {\n notify.info('Share link ready', url);\n }\n } catch (error) {\n notify.error(\n 'Could not create share link',\n error instanceof Error ? error.message : 'Please try again.'\n );\n } finally {\n setSharingId(null);\n }\n };\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2 space-y-0\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Paperclip className=\"h-4 w-4\" />\n Attachments\n {data?.getFiles.total ? (\n <span className=\"text-sm font-normal text-text-secondary\">\n ({data.getFiles.total})\n </span>\n ) : null}\n </CardTitle>\n <div>\n <input\n ref={fileInputRef}\n type=\"file\"\n className=\"hidden\"\n onChange={(event) => void handleFileSelected(event)}\n aria-label={`Upload a file for ${entityType} ${entityId}`}\n />\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={uploading}\n onClick={() => fileInputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Upload className=\"mr-2 h-3.5 w-3.5\" />\n )}\n {uploading ? 'Uploading…' : 'Upload'}\n </Button>\n </div>\n </CardHeader>\n <CardContent>\n {loading && files.length === 0 ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, i) => (\n <div\n key={i}\n className=\"h-12 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : files.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No files yet\"\n description=\"Upload photos, receipts, warranties or documents to keep them with this record.\"\n />\n ) : (\n <ul className=\"divide-y divide-border-subtle\">\n {files.map((file) => (\n <li key={file.id} className=\"flex items-center gap-3 py-2.5\">\n <FileText className=\"h-5 w-5 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{file.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatSize(Number(file.size))} · {new Date(file.createdAt).toLocaleDateString()}\n </p>\n </div>\n {file.downloadUrl ? (\n <a\n href={file.downloadUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-text-secondary hover:text-text-primary\"\n aria-label={`Download ${file.name}`}\n >\n <Download className=\"h-4 w-4\" />\n </a>\n ) : null}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={sharingId === file.id}\n onClick={() => void handleShare(file.id)}\n aria-label={`Share ${file.name}`}\n >\n {sharingId === file.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Link2 className=\"h-3.5 w-3.5\" />\n )}\n </Button>\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AttachmentsPanel.js","names":[],"sources":["../../../src/assethandler/components/AttachmentsPanel.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Paperclip, Upload, Link2, FileText, Loader2, Download } from 'lucide-react';\nimport { notify } from '../utils/toast';\n\n/**\n * Attach and reference files for an AssetHandler entity, backed by the\n * federated wspace-files-svc (same wspace gateway/Apollo client). Fully real —\n * lists workspace files via `getFiles`, uploads new files through the\n * `initiateUpload` → presigned PUT → `completeUpload` flow, and mints public\n * share links via `createFileShareLink`. No mock data.\n *\n * `entityType`/`entityId` scope the panel to the record it is mounted on and\n * are used for context labelling; uploaded files land in the workspace file\n * store so they can be shared and reused.\n */\nexport interface AttachmentsPanelProps {\n entityType: string;\n entityId: string;\n}\n\ninterface FileNode {\n id: string;\n name: string;\n extension: string;\n mimeType: string;\n size: number;\n downloadUrl?: string | null;\n createdAt: string;\n}\n\ninterface GetFilesResult {\n getFiles: {\n files: FileNode[];\n total: number;\n hasMore: boolean;\n };\n}\n\nconst GET_FILES_QUERY = gql`\n query AssetHandlerGetFiles($pagination: PaginationInput, $sort: FileSortInput) {\n getFiles(pagination: $pagination, sort: $sort) {\n total\n hasMore\n files {\n id\n name\n extension\n mimeType\n size\n downloadUrl\n createdAt\n }\n }\n }\n`;\n\nconst INITIATE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session {\n id\n }\n }\n }\n`;\n\nconst COMPLETE_UPLOAD_MUTATION = gql`\n mutation AssetHandlerCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file {\n id\n name\n }\n }\n }\n`;\n\nconst CREATE_SHARE_LINK_MUTATION = gql`\n mutation AssetHandlerCreateFileShareLink($input: CreateShareGrantLinkInput!) {\n createFileShareLink(input: $input) {\n id\n shareUrl\n }\n }\n`;\n\nfunction formatSize(bytes: number): string {\n if (!bytes) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n const value = bytes / Math.pow(1024, exponent);\n return `${value.toFixed(exponent === 0 ? 0 : 1)} ${units[exponent]}`;\n}\n\nfunction extensionOf(fileName: string): string {\n const dot = fileName.lastIndexOf('.');\n return dot >= 0 ? fileName.slice(dot + 1).toLowerCase() : 'bin';\n}\n\nexport function AttachmentsPanel({ entityType, entityId }: AttachmentsPanelProps) {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [uploading, setUploading] = useState(false);\n const [sharingId, setSharingId] = useState<string | null>(null);\n\n const { data, loading, refetch } = useQuery<GetFilesResult>(GET_FILES_QUERY, {\n variables: {\n pagination: { limit: 25, offset: 0 },\n sort: { field: 'CREATED_AT', order: 'DESC' },\n },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n\n const [initiateUpload] = useMutation<{\n initiateUpload: { uploadUrl?: string | null; session: { id: string } };\n }>(INITIATE_UPLOAD_MUTATION);\n const [completeUpload] = useMutation(COMPLETE_UPLOAD_MUTATION);\n const [createShareLink] = useMutation<{\n createFileShareLink: { id: string; shareUrl?: string | null };\n }>(CREATE_SHARE_LINK_MUTATION);\n\n const files = useMemo(() => data?.getFiles.files ?? [], [data]);\n\n const handleFileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the input so selecting the same file again re-triggers change.\n event.target.value = '';\n if (!file) return;\n\n setUploading(true);\n try {\n const initiated = await initiateUpload({\n variables: {\n input: {\n fileName: file.name,\n extension: extensionOf(file.name),\n fileSize: file.size,\n mimeType: file.type || 'application/octet-stream',\n visibility: 'WORKSPACE',\n },\n },\n });\n\n const uploadUrl = initiated.data?.initiateUpload.uploadUrl;\n const sessionId = initiated.data?.initiateUpload.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('Upload could not be initialised.');\n }\n\n const putResponse = await fetch(uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!putResponse.ok) {\n throw new Error(`Storage rejected the upload (HTTP ${putResponse.status}).`);\n }\n\n await completeUpload({\n variables: { input: { sessionId, fileSize: file.size } },\n });\n\n notify.success('File uploaded', `${file.name} is now available on this record.`);\n await refetch();\n } catch (error) {\n notify.error(\n 'Upload failed',\n error instanceof Error ? error.message : 'The file could not be uploaded.'\n );\n } finally {\n setUploading(false);\n }\n };\n\n const handleShare = async (fileId: string) => {\n setSharingId(fileId);\n try {\n const result = await createShareLink({\n variables: { input: { resourceId: fileId, role: 'VIEWER' } },\n });\n const url = result.data?.createFileShareLink.shareUrl;\n if (!url) {\n throw new Error('No share link was returned.');\n }\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(url);\n notify.success('Share link copied', 'A view-only link is on your clipboard.');\n } else {\n notify.info('Share link ready', url);\n }\n } catch (error) {\n notify.error(\n 'Could not create share link',\n error instanceof Error ? error.message : 'Please try again.'\n );\n } finally {\n setSharingId(null);\n }\n };\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2 space-y-0\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Paperclip className=\"h-4 w-4\" />\n Attachments\n {data?.getFiles.total ? (\n <span className=\"text-sm font-normal text-text-secondary\">({data.getFiles.total})</span>\n ) : null}\n </CardTitle>\n <div>\n <input\n ref={fileInputRef}\n type=\"file\"\n className=\"hidden\"\n onChange={(event) => void handleFileSelected(event)}\n aria-label={`Upload a file for ${entityType} ${entityId}`}\n />\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={uploading}\n onClick={() => fileInputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"mr-2 h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Upload className=\"mr-2 h-3.5 w-3.5\" />\n )}\n {uploading ? 'Uploading…' : 'Upload'}\n </Button>\n </div>\n </CardHeader>\n <CardContent>\n {loading && files.length === 0 ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, i) => (\n <div\n key={i}\n className=\"h-12 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : files.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No files yet\"\n description=\"Upload photos, receipts, warranties or documents to keep them with this record.\"\n />\n ) : (\n <ul className=\"divide-y divide-border-subtle\">\n {files.map((file) => (\n <li key={file.id} className=\"flex items-center gap-3 py-2.5\">\n <FileText className=\"h-5 w-5 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{file.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatSize(Number(file.size))} ·{' '}\n {new Date(file.createdAt).toLocaleDateString()}\n </p>\n </div>\n {file.downloadUrl ? (\n <a\n href={file.downloadUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-text-secondary hover:text-text-primary\"\n aria-label={`Download ${file.name}`}\n >\n <Download className=\"h-4 w-4\" />\n </a>\n ) : null}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={sharingId === file.id}\n onClick={() => void handleShare(file.id)}\n aria-label={`Share ${file.name}`}\n >\n {sharingId === file.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Link2 className=\"h-3.5 w-3.5\" />\n )}\n </Button>\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ import { useAssetHandlerStore as e } from "../store/assethandlerStore.js";
2
+ import { notify as t } from "../utils/toast.js";
3
+ import { useState as n } from "react";
4
+ import { Loader2 as r } from "lucide-react";
5
+ import { gql as i } from "@apollo/client";
6
+ import { useMutation as a } from "@apollo/client/react";
7
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
8
+ import { Button as c, Input as l, Label as u, ResponsiveDialog as d, ResponsiveDialogContent as f, ResponsiveDialogDescription as p, ResponsiveDialogFooter as m, ResponsiveDialogHeader as h, ResponsiveDialogTitle as g, Select as _, SelectContent as v, SelectItem as y, SelectTrigger as b, SelectValue as x, Textarea as S } from "@burdenoff/fe-libs/ui";
9
+ //#region src/assethandler/ops/NewWorkOrderDialog.tsx
10
+ var C = i`
11
+ mutation AssetHandlerCreateWorkOrder($input: CreateWorkOrderInput!) {
12
+ createWorkOrder(input: $input) {
13
+ id
14
+ title
15
+ }
16
+ }
17
+ `, w = [
18
+ "CORRECTIVE",
19
+ "PREVENTIVE",
20
+ "PREDICTIVE",
21
+ "EMERGENCY"
22
+ ], T = [
23
+ "DRAFT",
24
+ "SCHEDULED",
25
+ "APPROVED",
26
+ "IN_PROGRESS",
27
+ "COMPLETED",
28
+ "CANCELLED"
29
+ ], E = [
30
+ "CRITICAL",
31
+ "HIGH",
32
+ "MEDIUM",
33
+ "LOW"
34
+ ], D = (e) => e.toLowerCase().replace(/_/g, " ").replace(/^\w/, (e) => e.toUpperCase());
35
+ function O({ open: i, onOpenChange: O }) {
36
+ let { assets: k } = e(), [A, j] = n(""), [M, N] = n(""), [P, F] = n(""), [I, L] = n(""), [R, z] = n("CORRECTIVE"), [B, V] = n("MEDIUM"), [H, U] = n("DRAFT"), W = () => {
37
+ j(""), N(""), F(""), L(""), z("CORRECTIVE"), V("MEDIUM"), U("DRAFT");
38
+ }, [G, { loading: K }] = a(C, {
39
+ refetchQueries: ["AssetHandlerBootstrap"],
40
+ onCompleted: (e) => {
41
+ t.success("Work order created", `${e.createWorkOrder.title} was raised.`), W(), O(!1);
42
+ },
43
+ onError: (e) => t.error("Could not create work order", e.message)
44
+ });
45
+ return /* @__PURE__ */ o(d, {
46
+ open: i,
47
+ onOpenChange: O,
48
+ children: /* @__PURE__ */ s(f, {
49
+ className: "max-h-[85vh] overflow-y-auto sm:max-w-xl",
50
+ children: [
51
+ /* @__PURE__ */ s(h, { children: [/* @__PURE__ */ o(g, { children: "New Work Order" }), /* @__PURE__ */ o(p, { children: "Raise a work order when an asset needs repair, inspection or attention." })] }),
52
+ /* @__PURE__ */ s("div", {
53
+ className: "space-y-4",
54
+ children: [
55
+ /* @__PURE__ */ s("div", {
56
+ className: "space-y-2",
57
+ children: [/* @__PURE__ */ o(u, { children: "Title *" }), /* @__PURE__ */ o(l, {
58
+ placeholder: "e.g., Replace worn drive belt",
59
+ value: A,
60
+ onChange: (e) => j(e.target.value)
61
+ })]
62
+ }),
63
+ /* @__PURE__ */ s("div", {
64
+ className: "space-y-2",
65
+ children: [/* @__PURE__ */ o(u, { children: "Description" }), /* @__PURE__ */ o(S, {
66
+ placeholder: "Describe the work required",
67
+ value: M,
68
+ onChange: (e) => N(e.target.value),
69
+ rows: 2
70
+ })]
71
+ }),
72
+ /* @__PURE__ */ s("div", {
73
+ className: "grid gap-4 md:grid-cols-2",
74
+ children: [/* @__PURE__ */ s("div", {
75
+ className: "space-y-2",
76
+ children: [/* @__PURE__ */ o(u, { children: "Asset" }), /* @__PURE__ */ s(_, {
77
+ value: P,
78
+ onValueChange: F,
79
+ children: [/* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(x, { placeholder: "Unassigned" }) }), /* @__PURE__ */ o(v, { children: k.map((e) => /* @__PURE__ */ s(y, {
80
+ value: e.id,
81
+ children: [e.name, e.assetTag ? ` · ${e.assetTag}` : ""]
82
+ }, e.id)) })]
83
+ })]
84
+ }), /* @__PURE__ */ s("div", {
85
+ className: "space-y-2",
86
+ children: [/* @__PURE__ */ o(u, { children: "Assigned To" }), /* @__PURE__ */ o(l, {
87
+ placeholder: "e.g., Facilities team",
88
+ value: I,
89
+ onChange: (e) => L(e.target.value)
90
+ })]
91
+ })]
92
+ }),
93
+ /* @__PURE__ */ s("div", {
94
+ className: "grid gap-4 md:grid-cols-3",
95
+ children: [
96
+ /* @__PURE__ */ s("div", {
97
+ className: "space-y-2",
98
+ children: [/* @__PURE__ */ o(u, { children: "Type" }), /* @__PURE__ */ s(_, {
99
+ value: R,
100
+ onValueChange: (e) => z(e),
101
+ children: [/* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(x, {}) }), /* @__PURE__ */ o(v, { children: w.map((e) => /* @__PURE__ */ o(y, {
102
+ value: e,
103
+ children: D(e)
104
+ }, e)) })]
105
+ })]
106
+ }),
107
+ /* @__PURE__ */ s("div", {
108
+ className: "space-y-2",
109
+ children: [/* @__PURE__ */ o(u, { children: "Priority" }), /* @__PURE__ */ s(_, {
110
+ value: B,
111
+ onValueChange: (e) => V(e),
112
+ children: [/* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(x, {}) }), /* @__PURE__ */ o(v, { children: E.map((e) => /* @__PURE__ */ o(y, {
113
+ value: e,
114
+ children: D(e)
115
+ }, e)) })]
116
+ })]
117
+ }),
118
+ /* @__PURE__ */ s("div", {
119
+ className: "space-y-2",
120
+ children: [/* @__PURE__ */ o(u, { children: "Status" }), /* @__PURE__ */ s(_, {
121
+ value: H,
122
+ onValueChange: (e) => U(e),
123
+ children: [/* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(x, {}) }), /* @__PURE__ */ o(v, { children: T.map((e) => /* @__PURE__ */ o(y, {
124
+ value: e,
125
+ children: D(e)
126
+ }, e)) })]
127
+ })]
128
+ })
129
+ ]
130
+ })
131
+ ]
132
+ }),
133
+ /* @__PURE__ */ s(m, { children: [/* @__PURE__ */ o(c, {
134
+ variant: "outline",
135
+ onClick: () => O(!1),
136
+ disabled: K,
137
+ children: "Cancel"
138
+ }), /* @__PURE__ */ s(c, {
139
+ onClick: () => {
140
+ if (!A.trim()) {
141
+ t.warning("Title required", "Give the work order a title.");
142
+ return;
143
+ }
144
+ G({ variables: { input: {
145
+ title: A.trim(),
146
+ description: M.trim() || null,
147
+ assetId: P || null,
148
+ assignedTo: I.trim() || null,
149
+ type: R,
150
+ priority: B,
151
+ status: H
152
+ } } });
153
+ },
154
+ disabled: K,
155
+ children: [K ? /* @__PURE__ */ o(r, { className: "mr-2 h-4 w-4 animate-spin" }) : null, K ? "Creating…" : "Create Work Order"]
156
+ })] })
157
+ ]
158
+ })
159
+ });
160
+ }
161
+ //#endregion
162
+ export { O as NewWorkOrderDialog };
163
+
164
+ //# sourceMappingURL=NewWorkOrderDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NewWorkOrderDialog.js","names":[],"sources":["../../../src/assethandler/ops/NewWorkOrderDialog.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 } from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\n\n/**\n * Reusable \"New Work Order\" create dialog, wired to the real createWorkOrder\n * mutation (wspace-assethandler-svc). Used by the Operations Hub.\n */\nconst CREATE_WORK_ORDER_MUTATION = gql`\n mutation AssetHandlerCreateWorkOrder($input: CreateWorkOrderInput!) {\n createWorkOrder(input: $input) {\n id\n title\n }\n }\n`;\n\ninterface CreateWorkOrderResult {\n createWorkOrder: { id: string; title: string };\n}\n\nconst WORK_ORDER_TYPES = ['CORRECTIVE', 'PREVENTIVE', 'PREDICTIVE', 'EMERGENCY'] as const;\nconst WORK_ORDER_STATUSES = [\n 'DRAFT',\n 'SCHEDULED',\n 'APPROVED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'CANCELLED',\n] as const;\nconst PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\nexport function NewWorkOrderDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const { assets } = useAssetHandlerStore();\n\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [assetId, setAssetId] = useState('');\n const [assignedTo, setAssignedTo] = useState('');\n const [type, setType] = useState<(typeof WORK_ORDER_TYPES)[number]>('CORRECTIVE');\n const [priority, setPriority] = useState<(typeof PRIORITIES)[number]>('MEDIUM');\n const [status, setStatus] = useState<(typeof WORK_ORDER_STATUSES)[number]>('DRAFT');\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setAssetId('');\n setAssignedTo('');\n setType('CORRECTIVE');\n setPriority('MEDIUM');\n setStatus('DRAFT');\n };\n\n const [createWorkOrder, { loading }] = useMutation<CreateWorkOrderResult>(\n CREATE_WORK_ORDER_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Work order created', `${data.createWorkOrder.title} was raised.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create work order', error.message),\n }\n );\n\n const handleSubmit = () => {\n if (!title.trim()) {\n notify.warning('Title required', 'Give the work order a title.');\n return;\n }\n void createWorkOrder({\n variables: {\n input: {\n title: title.trim(),\n description: description.trim() || null,\n assetId: assetId || null,\n assignedTo: assignedTo.trim() || null,\n type,\n priority,\n status,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Work Order</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Raise a work order when an asset needs repair, inspection or attention.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input\n placeholder=\"e.g., Replace worn drive belt\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Description</Label>\n <Textarea\n placeholder=\"Describe the work required\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Asset</Label>\n <Select value={assetId} onValueChange={setAssetId}>\n <SelectTrigger>\n <SelectValue placeholder=\"Unassigned\" />\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 <div className=\"space-y-2\">\n <Label>Assigned To</Label>\n <Input\n placeholder=\"e.g., Facilities team\"\n value={assignedTo}\n onChange={(e) => setAssignedTo(e.target.value)}\n />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={type} onValueChange={(v) => setType(v as typeof type)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {WORK_ORDER_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={(v) => setPriority(v as typeof priority)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRIORITIES.map((p) => (\n <SelectItem key={p} value={p}>\n {humanize(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={(v) => setStatus(v as typeof status)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {WORK_ORDER_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Creating…' : 'Create Work Order'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { useAssetHandlerStore as e } from "../store/assethandlerStore.js";
2
+ import { notify as t } from "../utils/toast.js";
3
+ import { useState as n } from "react";
4
+ import { Loader2 as r, Plus as i, X as a } from "lucide-react";
5
+ import { gql as o } from "@apollo/client";
6
+ import { useMutation as s } from "@apollo/client/react";
7
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
+ import { Button as u, Input as d, Label as f, ResponsiveDialog as p, ResponsiveDialogContent as m, ResponsiveDialogDescription as h, ResponsiveDialogFooter as g, ResponsiveDialogHeader as _, ResponsiveDialogTitle as v, Select as y, SelectContent as b, SelectItem as x, SelectTrigger as S, SelectValue as C, Textarea as w } from "@burdenoff/fe-libs/ui";
9
+ //#region src/assethandler/ops/ScheduleMaintenanceDialog.tsx
10
+ var T = o`
11
+ mutation AssetHandlerCreateMaintenanceRecord($input: CreateMaintenanceRecordInput!) {
12
+ createMaintenanceRecord(input: $input) {
13
+ id
14
+ title
15
+ }
16
+ }
17
+ `, E = [
18
+ "PREVENTIVE",
19
+ "CORRECTIVE",
20
+ "INSPECTION",
21
+ "CALIBRATION",
22
+ "EMERGENCY"
23
+ ], D = [
24
+ "CRITICAL",
25
+ "HIGH",
26
+ "MEDIUM",
27
+ "LOW"
28
+ ], O = (e) => e.toLowerCase().replace(/_/g, " ").replace(/^\w/, (e) => e.toUpperCase());
29
+ function k({ open: o, onOpenChange: k }) {
30
+ let { assets: A } = e(), [j, M] = n(""), [N, P] = n(""), [F, I] = n(""), [L, R] = n("PREVENTIVE"), [z, B] = n("MEDIUM"), [V, H] = n(""), [U, W] = n(""), [G, K] = n([""]), q = () => {
31
+ M(""), P(""), I(""), R("PREVENTIVE"), B("MEDIUM"), H(""), W(""), K([""]);
32
+ }, [J, { loading: Y }] = s(T, {
33
+ refetchQueries: ["AssetHandlerBootstrap"],
34
+ onCompleted: (e) => {
35
+ t.success("Maintenance scheduled", `${e.createMaintenanceRecord.title} was added.`), q(), k(!1);
36
+ },
37
+ onError: (e) => t.error("Could not schedule maintenance", e.message)
38
+ }), X = (e, t) => K((n) => n.map((n, r) => r === e ? t : n)), Z = () => K((e) => [...e, ""]), Q = (e) => K((t) => t.length === 1 ? t : t.filter((t, n) => n !== e));
39
+ return /* @__PURE__ */ c(p, {
40
+ open: o,
41
+ onOpenChange: k,
42
+ children: /* @__PURE__ */ l(m, {
43
+ className: "max-h-[85vh] overflow-y-auto sm:max-w-2xl",
44
+ children: [
45
+ /* @__PURE__ */ l(_, { children: [/* @__PURE__ */ c(v, { children: "Schedule Maintenance" }), /* @__PURE__ */ c(h, { children: "Plan a maintenance job against an asset, with its own checklist and cost estimate." })] }),
46
+ /* @__PURE__ */ l("div", {
47
+ className: "space-y-4",
48
+ children: [
49
+ /* @__PURE__ */ l("div", {
50
+ className: "space-y-2",
51
+ children: [/* @__PURE__ */ c(f, { children: "Asset *" }), /* @__PURE__ */ l(y, {
52
+ value: j,
53
+ onValueChange: M,
54
+ children: [/* @__PURE__ */ c(S, { children: /* @__PURE__ */ c(C, { placeholder: "Select an asset" }) }), /* @__PURE__ */ c(b, { children: A.map((e) => /* @__PURE__ */ l(x, {
55
+ value: e.id,
56
+ children: [e.name, e.assetTag ? ` · ${e.assetTag}` : ""]
57
+ }, e.id)) })]
58
+ })]
59
+ }),
60
+ /* @__PURE__ */ l("div", {
61
+ className: "space-y-2",
62
+ children: [/* @__PURE__ */ c(f, { children: "Title *" }), /* @__PURE__ */ c(d, {
63
+ placeholder: "e.g., Quarterly HVAC service",
64
+ value: N,
65
+ onChange: (e) => P(e.target.value)
66
+ })]
67
+ }),
68
+ /* @__PURE__ */ l("div", {
69
+ className: "space-y-2",
70
+ children: [/* @__PURE__ */ c(f, { children: "Description" }), /* @__PURE__ */ c(w, {
71
+ placeholder: "What does this job involve?",
72
+ value: F,
73
+ onChange: (e) => I(e.target.value),
74
+ rows: 2
75
+ })]
76
+ }),
77
+ /* @__PURE__ */ l("div", {
78
+ className: "grid gap-4 md:grid-cols-3",
79
+ children: [
80
+ /* @__PURE__ */ l("div", {
81
+ className: "space-y-2",
82
+ children: [/* @__PURE__ */ c(f, { children: "Type" }), /* @__PURE__ */ l(y, {
83
+ value: L,
84
+ onValueChange: (e) => R(e),
85
+ children: [/* @__PURE__ */ c(S, { children: /* @__PURE__ */ c(C, {}) }), /* @__PURE__ */ c(b, { children: E.map((e) => /* @__PURE__ */ c(x, {
86
+ value: e,
87
+ children: O(e)
88
+ }, e)) })]
89
+ })]
90
+ }),
91
+ /* @__PURE__ */ l("div", {
92
+ className: "space-y-2",
93
+ children: [/* @__PURE__ */ c(f, { children: "Priority" }), /* @__PURE__ */ l(y, {
94
+ value: z,
95
+ onValueChange: (e) => B(e),
96
+ children: [/* @__PURE__ */ c(S, { children: /* @__PURE__ */ c(C, {}) }), /* @__PURE__ */ c(b, { children: D.map((e) => /* @__PURE__ */ c(x, {
97
+ value: e,
98
+ children: O(e)
99
+ }, e)) })]
100
+ })]
101
+ }),
102
+ /* @__PURE__ */ l("div", {
103
+ className: "space-y-2",
104
+ children: [/* @__PURE__ */ c(f, { children: "Estimated Cost" }), /* @__PURE__ */ c(d, {
105
+ type: "number",
106
+ min: "0",
107
+ step: "0.01",
108
+ placeholder: "e.g., 500",
109
+ value: U,
110
+ onChange: (e) => W(e.target.value)
111
+ })]
112
+ })
113
+ ]
114
+ }),
115
+ /* @__PURE__ */ l("div", {
116
+ className: "space-y-2",
117
+ children: [/* @__PURE__ */ c(f, { children: "Scheduled Date" }), /* @__PURE__ */ c(d, {
118
+ type: "date",
119
+ value: V,
120
+ onChange: (e) => H(e.target.value)
121
+ })]
122
+ }),
123
+ /* @__PURE__ */ l("div", {
124
+ className: "space-y-2",
125
+ children: [/* @__PURE__ */ l("div", {
126
+ className: "flex items-center justify-between",
127
+ children: [/* @__PURE__ */ c(f, { children: "Checklist" }), /* @__PURE__ */ l(u, {
128
+ type: "button",
129
+ size: "sm",
130
+ variant: "ghost",
131
+ onClick: Z,
132
+ children: [/* @__PURE__ */ c(i, { className: "mr-1 h-3.5 w-3.5" }), "Add task"]
133
+ })]
134
+ }), /* @__PURE__ */ c("div", {
135
+ className: "space-y-2",
136
+ children: G.map((e, t) => /* @__PURE__ */ l("div", {
137
+ className: "flex items-center gap-2",
138
+ children: [/* @__PURE__ */ c(d, {
139
+ placeholder: `Task ${t + 1}`,
140
+ value: e,
141
+ onChange: (e) => X(t, e.target.value)
142
+ }), /* @__PURE__ */ c(u, {
143
+ type: "button",
144
+ size: "icon",
145
+ variant: "ghost",
146
+ onClick: () => Q(t),
147
+ disabled: G.length === 1,
148
+ "aria-label": "Remove task",
149
+ children: /* @__PURE__ */ c(a, { className: "h-4 w-4" })
150
+ })]
151
+ }, t))
152
+ })]
153
+ })
154
+ ]
155
+ }),
156
+ /* @__PURE__ */ l(g, { children: [/* @__PURE__ */ c(u, {
157
+ variant: "outline",
158
+ onClick: () => k(!1),
159
+ disabled: Y,
160
+ children: "Cancel"
161
+ }), /* @__PURE__ */ l(u, {
162
+ onClick: () => {
163
+ if (!j) {
164
+ t.warning("Asset required", "Pick the asset this maintenance is for.");
165
+ return;
166
+ }
167
+ if (!N.trim()) {
168
+ t.warning("Title required", "Give the maintenance job a title.");
169
+ return;
170
+ }
171
+ let e = Number(U), n = G.map((e) => e.trim()).filter((e) => e !== "").map((e) => ({
172
+ task: e,
173
+ completed: !1
174
+ }));
175
+ J({ variables: { input: {
176
+ assetId: j,
177
+ title: N.trim(),
178
+ description: F.trim() || null,
179
+ type: L,
180
+ status: "SCHEDULED",
181
+ priority: z,
182
+ scheduledAt: V ? new Date(V).toISOString() : null,
183
+ estimatedCost: U.trim() !== "" && Number.isFinite(e) ? e : null,
184
+ checklist: n
185
+ } } });
186
+ },
187
+ disabled: Y,
188
+ children: [Y ? /* @__PURE__ */ c(r, { className: "mr-2 h-4 w-4 animate-spin" }) : null, Y ? "Scheduling…" : "Schedule Maintenance"]
189
+ })] })
190
+ ]
191
+ })
192
+ });
193
+ }
194
+ //#endregion
195
+ export { k as ScheduleMaintenanceDialog };
196
+
197
+ //# sourceMappingURL=ScheduleMaintenanceDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScheduleMaintenanceDialog.js","names":[],"sources":["../../../src/assethandler/ops/ScheduleMaintenanceDialog.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 { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\n\n/**\n * Reusable \"Schedule Maintenance\" create dialog. Wired to the real\n * createMaintenanceRecord mutation (wspace-assethandler-svc). Shared by the\n * Maintenance Scheduler and Maintenance Calendar pages so both entry points\n * write through one contract.\n *\n * Reuse note: a wspace-calendar-svc CalendarEvent projection is deliberately\n * deferred — createEvent requires a calendarId that the AssetHandler MFE has no\n * handle on. The Maintenance Calendar therefore renders the real\n * maintenanceRecords (scheduledAt) rather than shared-platform CalendarEvents.\n */\nconst CREATE_MAINTENANCE_RECORD_MUTATION = gql`\n mutation AssetHandlerCreateMaintenanceRecord($input: CreateMaintenanceRecordInput!) {\n createMaintenanceRecord(input: $input) {\n id\n title\n }\n }\n`;\n\ninterface CreateMaintenanceRecordResult {\n createMaintenanceRecord: { id: string; title: string };\n}\n\n// Maintenance `type` is a free-form String on the backend; these are the\n// canonical categories the product exposes in the UI.\nconst MAINTENANCE_TYPES = [\n 'PREVENTIVE',\n 'CORRECTIVE',\n 'INSPECTION',\n 'CALIBRATION',\n 'EMERGENCY',\n] as const;\nconst PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\nexport function ScheduleMaintenanceDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const { assets } = useAssetHandlerStore();\n\n const [assetId, setAssetId] = useState('');\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [type, setType] = useState<(typeof MAINTENANCE_TYPES)[number]>('PREVENTIVE');\n const [priority, setPriority] = useState<(typeof PRIORITIES)[number]>('MEDIUM');\n const [scheduledAt, setScheduledAt] = useState('');\n const [estimatedCost, setEstimatedCost] = useState('');\n const [checklist, setChecklist] = useState<string[]>(['']);\n\n const reset = () => {\n setAssetId('');\n setTitle('');\n setDescription('');\n setType('PREVENTIVE');\n setPriority('MEDIUM');\n setScheduledAt('');\n setEstimatedCost('');\n setChecklist(['']);\n };\n\n const [createMaintenanceRecord, { loading }] = useMutation<CreateMaintenanceRecordResult>(\n CREATE_MAINTENANCE_RECORD_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Maintenance scheduled', `${data.createMaintenanceRecord.title} was added.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not schedule maintenance', error.message),\n }\n );\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 (!assetId) {\n notify.warning('Asset required', 'Pick the asset this maintenance is for.');\n return;\n }\n if (!title.trim()) {\n notify.warning('Title required', 'Give the maintenance job a title.');\n return;\n }\n const parsedCost = Number(estimatedCost);\n const cleanedChecklist = checklist\n .map((task) => task.trim())\n .filter((task) => task !== '')\n .map((task) => ({ task, completed: false }));\n void createMaintenanceRecord({\n variables: {\n input: {\n assetId,\n title: title.trim(),\n description: description.trim() || null,\n type,\n status: 'SCHEDULED',\n priority,\n scheduledAt: scheduledAt ? new Date(scheduledAt).toISOString() : null,\n estimatedCost:\n estimatedCost.trim() !== '' && Number.isFinite(parsedCost) ? parsedCost : null,\n checklist: cleanedChecklist,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule Maintenance</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Plan a maintenance job against an asset, with its own checklist and cost estimate.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\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 <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input\n placeholder=\"e.g., Quarterly HVAC service\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Description</Label>\n <Textarea\n placeholder=\"What does this job involve?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={type} onValueChange={(v) => setType(v as typeof type)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {MAINTENANCE_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={(v) => setPriority(v as typeof priority)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRIORITIES.map((p) => (\n <SelectItem key={p} value={p}>\n {humanize(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Estimated Cost</Label>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n placeholder=\"e.g., 500\"\n value={estimatedCost}\n onChange={(e) => setEstimatedCost(e.target.value)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Scheduled Date</Label>\n <Input\n type=\"date\"\n value={scheduledAt}\n onChange={(e) => setScheduledAt(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <Label>Checklist</Label>\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={addChecklistItem}>\n <Plus className=\"mr-1 h-3.5 w-3.5\" />\n Add task\n </Button>\n </div>\n <div className=\"space-y-2\">\n {checklist.map((task, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <Input\n placeholder={`Task ${i + 1}`}\n value={task}\n onChange={(e) => setChecklistItem(i, e.target.value)}\n />\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n onClick={() => removeChecklistItem(i)}\n disabled={checklist.length === 1}\n aria-label=\"Remove task\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n ))}\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Scheduling…' : 'Schedule Maintenance'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}