@burdenoff/microfe-assethandler 2026.703.7 → 2026.703.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"ItilChangeDetailPage.js","names":[],"sources":["../../../src/assethandler/pages/ItilChangeDetailPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n Label,\n Textarea,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowLeft, CheckCircle, XCircle } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport { AttachmentsPanel, CommentsPanel, ActivityTimeline, NotFoundState } from '../components';\n\nconst CHANGE_QUERY = gql`\n query AssetHandlerChangeRequest($id: ID!) {\n changeRequest(id: $id) {\n id\n title\n description\n type\n status\n risk\n requestedBy\n assignedTo\n affectedAssets\n scheduledStart\n scheduledEnd\n approvals\n createdAt\n }\n }\n`;\n\nconst APPROVE_CHANGE_MUTATION = gql`\n mutation AssetHandlerApproveChangeRequest($id: ID!) {\n approveChangeRequest(id: $id) {\n id\n status\n }\n }\n`;\n\nconst REJECT_CHANGE_MUTATION = gql`\n mutation AssetHandlerRejectChangeRequest($id: ID!, $reason: String) {\n rejectChangeRequest(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\ninterface ChangeApproval {\n name?: string | null;\n status?: string | null;\n date?: string | null;\n}\n\ninterface ChangeRecord {\n id: string;\n title: string;\n description: string;\n type: string;\n status: string;\n risk: string;\n requestedBy: string;\n assignedTo?: string | null;\n affectedAssets: string[];\n scheduledStart?: string | null;\n scheduledEnd?: string | null;\n approvals?: ChangeApproval[] | null;\n createdAt: string;\n}\n\ninterface ChangeQueryResult {\n changeRequest: ChangeRecord | null;\n}\n\nconst riskColors: Record<string, 'destructive' | 'default' | 'secondary' | 'outline'> = {\n critical: 'destructive',\n high: 'destructive',\n medium: 'default',\n low: 'secondary',\n};\n\nconst formatDate = (value?: string | null) => (value ? new Date(value).toLocaleString() : '—');\n\nexport function ItilChangeDetailPage() {\n const navigate = useAssetHandlerNavigate();\n const { changeId } = useParams<{ changeId: string }>();\n const { assets } = useAssetHandlerStore();\n\n const { data, loading, refetch } = useQuery<ChangeQueryResult>(CHANGE_QUERY, {\n variables: { id: changeId ?? '' },\n skip: !changeId,\n fetchPolicy: 'cache-and-network',\n });\n\n const [showReject, setShowReject] = useState(false);\n const [reason, setReason] = useState('');\n\n const [approveChange, { loading: approving }] = useMutation(APPROVE_CHANGE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Change approved', 'The change request has been approved.');\n void refetch();\n },\n onError: (error) => notify.error('Could not approve change', error.message),\n });\n\n const [rejectChange, { loading: rejecting }] = useMutation(REJECT_CHANGE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Change rejected', 'The change request has been rejected.');\n setShowReject(false);\n setReason('');\n void refetch();\n },\n onError: (error) => notify.error('Could not reject change', error.message),\n });\n\n if (loading && !data) {\n return (\n <div className=\"space-y-3 p-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n );\n }\n\n const raw = data?.changeRequest;\n if (!raw) {\n return (\n <NotFoundState\n entityLabel=\"Change request\"\n description=\"This change may have been completed or removed. Return to change management to find it.\"\n backLabel=\"Back to changes\"\n onBack={() => navigate('/itil/change-management')}\n />\n );\n }\n\n const change = {\n ...raw,\n status: raw.status.toLowerCase(),\n risk: raw.risk.toLowerCase(),\n type: raw.type.toLowerCase(),\n };\n const approvals = raw.approvals ?? [];\n const affectedAssetDetails = assets.filter((a) => raw.affectedAssets.includes(a.id));\n const isDecided =\n change.status === 'approved' ||\n change.status === 'completed' ||\n change.status === 'rejected';\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-4\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigate('/itil/change-management')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-bold\">\n <GradientText>{change.id}</GradientText>\n </h1>\n <Badge\n variant={change.type === 'emergency' ? 'destructive' : 'outline'}\n className=\"capitalize\"\n >\n {change.type}\n </Badge>\n <Badge variant={riskColors[change.risk] ?? 'outline'}>Risk: {change.risk}</Badge>\n <Badge variant=\"outline\">{change.status.replace(/_/g, ' ')}</Badge>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">{change.title}</p>\n </div>\n </div>\n {!isDecided ? (\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowReject(true)}>\n <XCircle className=\"mr-2 h-4 w-4\" />\n Reject\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void approveChange({ variables: { id: change.id } })}\n disabled={approving}\n >\n <CheckCircle className=\"mr-2 h-4 w-4\" />\n Approve\n </Button>\n </div>\n ) : null}\n </div>\n\n <PagePurpose>\n The full record of a single planned change: what is changing, its risk and schedule, which\n assets it touches, and where it stands in the approval chain. Use this page to approve,\n reject or track a change so it lands safely in its agreed window.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"assethandler-change-detail\"\n steps={[\n {\n id: 'approve',\n label: 'Approve or reject this change',\n description: 'Sign off on the change once you have reviewed its risk.',\n done: isDecided,\n },\n {\n id: 'assets',\n label: 'Review affected assets',\n description: 'Confirm the blast radius before the change window.',\n onClick: () => navigate('/assets/asset-list'),\n },\n {\n id: 'calendar',\n label: 'Check the maintenance calendar',\n description: 'Make sure the schedule does not clash with other work.',\n onClick: () => navigate('/maintenance/maintenance-calendar'),\n },\n ]}\n />\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <EmphasisPanel className=\"lg:col-span-2\">\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle>Change Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4 px-0 pb-0\">\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary\">Description</h4>\n <p className=\"text-sm mt-1\">{change.description}</p>\n </div>\n <Separator />\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-text-secondary\">Scheduled Start</span>\n <p className=\"font-medium\">{formatDate(change.scheduledStart)}</p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Scheduled End</span>\n <p className=\"font-medium\">{formatDate(change.scheduledEnd)}</p>\n </div>\n </div>\n <Separator />\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary mb-2\">Affected Assets</h4>\n {affectedAssetDetails.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">No linked assets on record.</p>\n ) : (\n <div className=\"space-y-2\">\n {affectedAssetDetails.map((a) => (\n <div\n key={a.id}\n className=\"flex items-center justify-between rounded border p-2\"\n >\n <div>\n <p className=\"text-sm font-medium\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{a.assetTag}</p>\n </div>\n <Badge variant=\"outline\">{a.status.replace(/_/g, ' ')}</Badge>\n </div>\n ))}\n </div>\n )}\n </div>\n </CardContent>\n </EmphasisPanel>\n\n <div className=\"space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Properties</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Type</dt>\n <dd className=\"font-medium capitalize\">{change.type}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Risk</dt>\n <dd>\n <Badge variant={riskColors[change.risk] ?? 'outline'}>{change.risk}</Badge>\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd>\n <Badge variant=\"outline\">{change.status.replace(/_/g, ' ')}</Badge>\n </dd>\n </div>\n <Separator />\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Requested By</dt>\n <dd className=\"text-xs\">{change.requestedBy}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Assigned To</dt>\n <dd className=\"text-xs\">{change.assignedTo || 'Unassigned'}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-xs\">{new Date(change.createdAt).toLocaleDateString()}</dd>\n </div>\n </dl>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Approvals</CardTitle>\n </CardHeader>\n <CardContent>\n {approvals.length === 0 ? (\n <p className=\"text-sm text-text-secondary text-center py-4\">No approvals yet</p>\n ) : (\n <div className=\"space-y-2\">\n {approvals.map((approval, i) => (\n <div key={i} className=\"flex items-center justify-between rounded border p-2\">\n <div className=\"flex items-center gap-2\">\n {approval.status === 'approved' ? (\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n ) : (\n <XCircle className=\"h-4 w-4 text-status-error-text\" />\n )}\n <span className=\"text-sm\">{approval.name ?? 'Approver'}</span>\n </div>\n <span className=\"text-xs text-text-secondary\">\n {approval.date ? new Date(approval.date).toLocaleDateString() : '—'}\n </span>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n\n <AttachmentsPanel entityType=\"assethandler-change-request\" entityId={change.id} />\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <CommentsPanel entityType=\"assethandler-change-request\" entityId={change.id} />\n <ActivityTimeline resourceId={change.id} />\n </div>\n\n {/* Reject dialog */}\n <ResponsiveDialog open={showReject} onOpenChange={setShowReject}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Reject change request</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Give a reason so the requester understands why this change was declined.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-2\">\n <Label>Reason</Label>\n <Textarea\n placeholder=\"Why is this change being rejected?\"\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n />\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setShowReject(false)} disabled={rejecting}>\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() =>\n void rejectChange({\n variables: { id: change.id, reason: reason.trim() || null },\n })\n }\n disabled={rejecting}\n >\n {rejecting ? 'Rejecting…' : 'Reject change'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ItilChangeDetailPage.js","names":[],"sources":["../../../src/assethandler/pages/ItilChangeDetailPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n Label,\n Textarea,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowLeft, CheckCircle, XCircle } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport { AttachmentsPanel, CommentsPanel, ActivityTimeline, NotFoundState } from '../components';\n\nconst CHANGE_QUERY = gql`\n query AssetHandlerChangeRequest($id: ID!) {\n changeRequest(id: $id) {\n id\n title\n description\n type\n status\n risk\n requestedBy\n assignedTo\n affectedAssets\n scheduledStart\n scheduledEnd\n approvals\n createdAt\n }\n }\n`;\n\nconst APPROVE_CHANGE_MUTATION = gql`\n mutation AssetHandlerApproveChangeRequest($id: ID!) {\n approveChangeRequest(id: $id) {\n id\n status\n }\n }\n`;\n\nconst REJECT_CHANGE_MUTATION = gql`\n mutation AssetHandlerRejectChangeRequest($id: ID!, $reason: String) {\n rejectChangeRequest(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\ninterface ChangeApproval {\n name?: string | null;\n status?: string | null;\n date?: string | null;\n}\n\ninterface ChangeRecord {\n id: string;\n title: string;\n description: string;\n type: string;\n status: string;\n risk: string;\n requestedBy: string;\n assignedTo?: string | null;\n affectedAssets: string[];\n scheduledStart?: string | null;\n scheduledEnd?: string | null;\n approvals?: ChangeApproval[] | null;\n createdAt: string;\n}\n\ninterface ChangeQueryResult {\n changeRequest: ChangeRecord | null;\n}\n\nconst riskColors: Record<string, 'destructive' | 'default' | 'secondary' | 'outline'> = {\n critical: 'destructive',\n high: 'destructive',\n medium: 'default',\n low: 'secondary',\n};\n\nconst formatDate = (value?: string | null) => (value ? new Date(value).toLocaleString() : '—');\n\nexport function ItilChangeDetailPage() {\n const navigate = useAssetHandlerNavigate();\n const { changeId } = useParams<{ changeId: string }>();\n const { assets } = useAssetHandlerStore();\n\n const { data, loading, refetch } = useQuery<ChangeQueryResult>(CHANGE_QUERY, {\n variables: { id: changeId ?? '' },\n skip: !changeId,\n fetchPolicy: 'cache-and-network',\n });\n\n const [showReject, setShowReject] = useState(false);\n const [reason, setReason] = useState('');\n\n const [approveChange, { loading: approving }] = useMutation(APPROVE_CHANGE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Change approved', 'The change request has been approved.');\n void refetch();\n },\n onError: (error) => notify.error('Could not approve change', error.message),\n });\n\n const [rejectChange, { loading: rejecting }] = useMutation(REJECT_CHANGE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Change rejected', 'The change request has been rejected.');\n setShowReject(false);\n setReason('');\n void refetch();\n },\n onError: (error) => notify.error('Could not reject change', error.message),\n });\n\n if (loading && !data) {\n return (\n <div className=\"space-y-3 p-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n );\n }\n\n const raw = data?.changeRequest;\n if (!raw) {\n return (\n <NotFoundState\n entityLabel=\"Change request\"\n description=\"This change may have been completed or removed. Return to change management to find it.\"\n backLabel=\"Back to changes\"\n onBack={() => navigate('/itil/change-management')}\n />\n );\n }\n\n const change = {\n ...raw,\n status: raw.status.toLowerCase(),\n risk: raw.risk.toLowerCase(),\n type: raw.type.toLowerCase(),\n };\n const approvals = raw.approvals ?? [];\n const affectedAssetDetails = assets.filter((a) => raw.affectedAssets.includes(a.id));\n const isDecided =\n change.status === 'approved' || change.status === 'completed' || change.status === 'rejected';\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-4\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigate('/itil/change-management')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-bold\">\n <GradientText>{change.id}</GradientText>\n </h1>\n <Badge\n variant={change.type === 'emergency' ? 'destructive' : 'outline'}\n className=\"capitalize\"\n >\n {change.type}\n </Badge>\n <Badge variant={riskColors[change.risk] ?? 'outline'}>Risk: {change.risk}</Badge>\n <Badge variant=\"outline\">{change.status.replace(/_/g, ' ')}</Badge>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">{change.title}</p>\n </div>\n </div>\n {!isDecided ? (\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => setShowReject(true)}>\n <XCircle className=\"mr-2 h-4 w-4\" />\n Reject\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void approveChange({ variables: { id: change.id } })}\n disabled={approving}\n >\n <CheckCircle className=\"mr-2 h-4 w-4\" />\n Approve\n </Button>\n </div>\n ) : null}\n </div>\n\n <PagePurpose>\n The full record of a single planned change: what is changing, its risk and schedule, which\n assets it touches, and where it stands in the approval chain. Use this page to approve,\n reject or track a change so it lands safely in its agreed window.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"assethandler-change-detail\"\n steps={[\n {\n id: 'approve',\n label: 'Approve or reject this change',\n description: 'Sign off on the change once you have reviewed its risk.',\n done: isDecided,\n },\n {\n id: 'assets',\n label: 'Review affected assets',\n description: 'Confirm the blast radius before the change window.',\n onClick: () => navigate('/assets/asset-list'),\n },\n {\n id: 'calendar',\n label: 'Check the maintenance calendar',\n description: 'Make sure the schedule does not clash with other work.',\n onClick: () => navigate('/maintenance/maintenance-calendar'),\n },\n ]}\n />\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <EmphasisPanel className=\"lg:col-span-2\">\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle>Change Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4 px-0 pb-0\">\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary\">Description</h4>\n <p className=\"text-sm mt-1\">{change.description}</p>\n </div>\n <Separator />\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-text-secondary\">Scheduled Start</span>\n <p className=\"font-medium\">{formatDate(change.scheduledStart)}</p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Scheduled End</span>\n <p className=\"font-medium\">{formatDate(change.scheduledEnd)}</p>\n </div>\n </div>\n <Separator />\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary mb-2\">Affected Assets</h4>\n {affectedAssetDetails.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">No linked assets on record.</p>\n ) : (\n <div className=\"space-y-2\">\n {affectedAssetDetails.map((a) => (\n <div\n key={a.id}\n className=\"flex items-center justify-between rounded border p-2\"\n >\n <div>\n <p className=\"text-sm font-medium\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{a.assetTag}</p>\n </div>\n <Badge variant=\"outline\">{a.status.replace(/_/g, ' ')}</Badge>\n </div>\n ))}\n </div>\n )}\n </div>\n </CardContent>\n </EmphasisPanel>\n\n <div className=\"space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Properties</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Type</dt>\n <dd className=\"font-medium capitalize\">{change.type}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Risk</dt>\n <dd>\n <Badge variant={riskColors[change.risk] ?? 'outline'}>{change.risk}</Badge>\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd>\n <Badge variant=\"outline\">{change.status.replace(/_/g, ' ')}</Badge>\n </dd>\n </div>\n <Separator />\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Requested By</dt>\n <dd className=\"text-xs\">{change.requestedBy}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Assigned To</dt>\n <dd className=\"text-xs\">{change.assignedTo || 'Unassigned'}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-xs\">{new Date(change.createdAt).toLocaleDateString()}</dd>\n </div>\n </dl>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Approvals</CardTitle>\n </CardHeader>\n <CardContent>\n {approvals.length === 0 ? (\n <p className=\"text-sm text-text-secondary text-center py-4\">No approvals yet</p>\n ) : (\n <div className=\"space-y-2\">\n {approvals.map((approval, i) => (\n <div key={i} className=\"flex items-center justify-between rounded border p-2\">\n <div className=\"flex items-center gap-2\">\n {approval.status === 'approved' ? (\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n ) : (\n <XCircle className=\"h-4 w-4 text-status-error-text\" />\n )}\n <span className=\"text-sm\">{approval.name ?? 'Approver'}</span>\n </div>\n <span className=\"text-xs text-text-secondary\">\n {approval.date ? new Date(approval.date).toLocaleDateString() : '—'}\n </span>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n\n <AttachmentsPanel entityType=\"assethandler-change-request\" entityId={change.id} />\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <CommentsPanel entityType=\"assethandler-change-request\" entityId={change.id} />\n <ActivityTimeline resourceId={change.id} />\n </div>\n\n {/* Reject dialog */}\n <ResponsiveDialog open={showReject} onOpenChange={setShowReject}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Reject change request</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Give a reason so the requester understands why this change was declined.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-2\">\n <Label>Reason</Label>\n <Textarea\n placeholder=\"Why is this change being rejected?\"\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n />\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setShowReject(false)} disabled={rejecting}>\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() =>\n void rejectChange({\n variables: { id: change.id, reason: reason.trim() || null },\n })\n }\n disabled={rejecting}\n >\n {rejecting ? 'Rejecting…' : 'Reject change'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ItilDialogs.js","names":[],"sources":["../../../src/assethandler/pages/ItilDialogs.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 { notify } from '../utils/toast';\n\n// ─── Mutations ────────────────────────────────────────────────────────────────\nconst CREATE_INCIDENT_MUTATION = gql`\n mutation AssetHandlerCreateIncident($input: CreateIncidentInput!) {\n createIncident(input: $input) {\n id\n title\n }\n }\n`;\n\nconst CREATE_CHANGE_REQUEST_MUTATION = gql`\n mutation AssetHandlerCreateChangeRequest($input: CreateChangeRequestInput!) {\n createChangeRequest(input: $input) {\n id\n title\n }\n }\n`;\n\nconst CREATE_PROBLEM_MUTATION = gql`\n mutation AssetHandlerCreateProblem($input: CreateProblemInput!) {\n createProblem(input: $input) {\n id\n title\n }\n }\n`;\n\ninterface CreateIncidentResult {\n createIncident: { id: string; title: string };\n}\ninterface CreateChangeRequestResult {\n createChangeRequest: { id: string; title: string };\n}\ninterface CreateProblemResult {\n createProblem: { id: string; title: string };\n}\n\nconst PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\nconst RISKS = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\nconst CHANGE_TYPES = ['STANDARD', 'NORMAL', 'EMERGENCY'] as const;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\ninterface DialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\n// ─── New Incident dialog ──────────────────────────────────────────────────────\nexport function NewIncidentDialog({ open, onOpenChange }: DialogProps) {\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [category, setCategory] = useState('');\n const [priority, setPriority] = useState<(typeof PRIORITIES)[number]>('MEDIUM');\n const [assignedTo, setAssignedTo] = useState('');\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setCategory('');\n setPriority('MEDIUM');\n setAssignedTo('');\n };\n\n const [createIncident, { loading }] = useMutation<CreateIncidentResult>(CREATE_INCIDENT_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Incident raised', `${data.createIncident.title} is now being tracked.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not raise incident', error.message),\n });\n\n const handleSubmit = () => {\n if (!title.trim() || !description.trim() || !category.trim()) {\n notify.warning('Missing details', 'Title, description and category are required.');\n return;\n }\n void createIncident({\n variables: {\n input: {\n title: title.trim(),\n description: description.trim(),\n category: category.trim(),\n priority,\n assignedTo: assignedTo.trim() || null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Incident</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Raise a service-affecting incident for triage and resolution.\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., Printer offline on floor 3\"\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 is happening and what is the impact?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input\n placeholder=\"e.g., hardware\"\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n />\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>\n <div className=\"space-y-2\">\n <Label>Assigned To</Label>\n <Input value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} />\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 ? 'Raising…' : 'Raise Incident'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Change Request dialog ────────────────────────────────────────────────\nexport function NewChangeRequestDialog({ open, onOpenChange }: DialogProps) {\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [type, setType] = useState<(typeof CHANGE_TYPES)[number]>('NORMAL');\n const [risk, setRisk] = useState<(typeof RISKS)[number]>('MEDIUM');\n const [assignedTo, setAssignedTo] = useState('');\n const [scheduledStart, setScheduledStart] = useState('');\n const [scheduledEnd, setScheduledEnd] = useState('');\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setType('NORMAL');\n setRisk('MEDIUM');\n setAssignedTo('');\n setScheduledStart('');\n setScheduledEnd('');\n };\n\n const [createChangeRequest, { loading }] = useMutation<CreateChangeRequestResult>(\n CREATE_CHANGE_REQUEST_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Change requested', `${data.createChangeRequest.title} was submitted.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create change request', error.message),\n }\n );\n\n const handleSubmit = () => {\n if (!title.trim() || !description.trim()) {\n notify.warning('Missing details', 'Title and description are required.');\n return;\n }\n void createChangeRequest({\n variables: {\n input: {\n title: title.trim(),\n description: description.trim(),\n type,\n risk,\n assignedTo: assignedTo.trim() || null,\n scheduledStart: scheduledStart ? new Date(scheduledStart).toISOString() : null,\n scheduledEnd: scheduledEnd ? new Date(scheduledEnd).toISOString() : null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Change Request</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Plan and risk-assess a change to your assets or services.\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., Upgrade core switch firmware\"\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 is changing and why?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\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={(v) => setType(v as typeof type)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CHANGE_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>Risk</Label>\n <Select value={risk} onValueChange={(v) => setRisk(v as typeof risk)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RISKS.map((r) => (\n <SelectItem key={r} value={r}>\n {humanize(r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Scheduled Start</Label>\n <Input\n type=\"datetime-local\"\n value={scheduledStart}\n onChange={(e) => setScheduledStart(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Scheduled End</Label>\n <Input\n type=\"datetime-local\"\n value={scheduledEnd}\n onChange={(e) => setScheduledEnd(e.target.value)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Assigned To</Label>\n <Input value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} />\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 ? 'Submitting…' : 'Submit Change'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Problem dialog ───────────────────────────────────────────────────────\nexport function NewProblemDialog({ open, onOpenChange }: DialogProps) {\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [category, setCategory] = useState('');\n const [priority, setPriority] = useState<(typeof PRIORITIES)[number]>('MEDIUM');\n const [assignedTo, setAssignedTo] = useState('');\n const [rootCause, setRootCause] = useState('');\n const [workaround, setWorkaround] = useState('');\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setCategory('');\n setPriority('MEDIUM');\n setAssignedTo('');\n setRootCause('');\n setWorkaround('');\n };\n\n const [createProblem, { loading }] = useMutation<CreateProblemResult>(CREATE_PROBLEM_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Problem opened', `${data.createProblem.title} is under investigation.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not open problem', error.message),\n });\n\n const handleSubmit = () => {\n if (!title.trim() || !description.trim() || !category.trim()) {\n notify.warning('Missing details', 'Title, description and category are required.');\n return;\n }\n void createProblem({\n variables: {\n input: {\n title: title.trim(),\n description: description.trim(),\n category: category.trim(),\n priority,\n assignedTo: assignedTo.trim() || null,\n rootCause: rootCause.trim() || null,\n workaround: workaround.trim() || null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Problem</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Open a problem record to investigate the root cause behind recurring incidents.\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., Recurring VPN drops\"\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 pattern of incidents does this explain?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input\n placeholder=\"e.g., network\"\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n />\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>\n <div className=\"space-y-2\">\n <Label>Assigned To</Label>\n <Input value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Root Cause</Label>\n <Textarea\n placeholder=\"If already known\"\n value={rootCause}\n onChange={(e) => setRootCause(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Workaround</Label>\n <Textarea\n placeholder=\"Any temporary mitigation\"\n value={workaround}\n onChange={(e) => setWorkaround(e.target.value)}\n rows={2}\n />\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 ? 'Opening…' : 'Open Problem'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ItilDialogs.js","names":[],"sources":["../../../src/assethandler/pages/ItilDialogs.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 { notify } from '../utils/toast';\n\n// ─── Mutations ────────────────────────────────────────────────────────────────\nconst CREATE_INCIDENT_MUTATION = gql`\n mutation AssetHandlerCreateIncident($input: CreateIncidentInput!) {\n createIncident(input: $input) {\n id\n title\n }\n }\n`;\n\nconst CREATE_CHANGE_REQUEST_MUTATION = gql`\n mutation AssetHandlerCreateChangeRequest($input: CreateChangeRequestInput!) {\n createChangeRequest(input: $input) {\n id\n title\n }\n }\n`;\n\nconst CREATE_PROBLEM_MUTATION = gql`\n mutation AssetHandlerCreateProblem($input: CreateProblemInput!) {\n createProblem(input: $input) {\n id\n title\n }\n }\n`;\n\ninterface CreateIncidentResult {\n createIncident: { id: string; title: string };\n}\ninterface CreateChangeRequestResult {\n createChangeRequest: { id: string; title: string };\n}\ninterface CreateProblemResult {\n createProblem: { id: string; title: string };\n}\n\nconst PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\nconst RISKS = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\nconst CHANGE_TYPES = ['STANDARD', 'NORMAL', 'EMERGENCY'] as const;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\ninterface DialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\n// ─── New Incident dialog ──────────────────────────────────────────────────────\nexport function NewIncidentDialog({ open, onOpenChange }: DialogProps) {\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [category, setCategory] = useState('');\n const [priority, setPriority] = useState<(typeof PRIORITIES)[number]>('MEDIUM');\n const [assignedTo, setAssignedTo] = useState('');\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setCategory('');\n setPriority('MEDIUM');\n setAssignedTo('');\n };\n\n const [createIncident, { loading }] = useMutation<CreateIncidentResult>(\n CREATE_INCIDENT_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Incident raised', `${data.createIncident.title} is now being tracked.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not raise incident', error.message),\n }\n );\n\n const handleSubmit = () => {\n if (!title.trim() || !description.trim() || !category.trim()) {\n notify.warning('Missing details', 'Title, description and category are required.');\n return;\n }\n void createIncident({\n variables: {\n input: {\n title: title.trim(),\n description: description.trim(),\n category: category.trim(),\n priority,\n assignedTo: assignedTo.trim() || null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Incident</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Raise a service-affecting incident for triage and resolution.\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., Printer offline on floor 3\"\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 is happening and what is the impact?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input\n placeholder=\"e.g., hardware\"\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n />\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>\n <div className=\"space-y-2\">\n <Label>Assigned To</Label>\n <Input value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} />\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 ? 'Raising…' : 'Raise Incident'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Change Request dialog ────────────────────────────────────────────────\nexport function NewChangeRequestDialog({ open, onOpenChange }: DialogProps) {\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [type, setType] = useState<(typeof CHANGE_TYPES)[number]>('NORMAL');\n const [risk, setRisk] = useState<(typeof RISKS)[number]>('MEDIUM');\n const [assignedTo, setAssignedTo] = useState('');\n const [scheduledStart, setScheduledStart] = useState('');\n const [scheduledEnd, setScheduledEnd] = useState('');\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setType('NORMAL');\n setRisk('MEDIUM');\n setAssignedTo('');\n setScheduledStart('');\n setScheduledEnd('');\n };\n\n const [createChangeRequest, { loading }] = useMutation<CreateChangeRequestResult>(\n CREATE_CHANGE_REQUEST_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Change requested', `${data.createChangeRequest.title} was submitted.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create change request', error.message),\n }\n );\n\n const handleSubmit = () => {\n if (!title.trim() || !description.trim()) {\n notify.warning('Missing details', 'Title and description are required.');\n return;\n }\n void createChangeRequest({\n variables: {\n input: {\n title: title.trim(),\n description: description.trim(),\n type,\n risk,\n assignedTo: assignedTo.trim() || null,\n scheduledStart: scheduledStart ? new Date(scheduledStart).toISOString() : null,\n scheduledEnd: scheduledEnd ? new Date(scheduledEnd).toISOString() : null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Change Request</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Plan and risk-assess a change to your assets or services.\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., Upgrade core switch firmware\"\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 is changing and why?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\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={(v) => setType(v as typeof type)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CHANGE_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>Risk</Label>\n <Select value={risk} onValueChange={(v) => setRisk(v as typeof risk)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RISKS.map((r) => (\n <SelectItem key={r} value={r}>\n {humanize(r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Scheduled Start</Label>\n <Input\n type=\"datetime-local\"\n value={scheduledStart}\n onChange={(e) => setScheduledStart(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Scheduled End</Label>\n <Input\n type=\"datetime-local\"\n value={scheduledEnd}\n onChange={(e) => setScheduledEnd(e.target.value)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Assigned To</Label>\n <Input value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} />\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 ? 'Submitting…' : 'Submit Change'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Problem dialog ───────────────────────────────────────────────────────\nexport function NewProblemDialog({ open, onOpenChange }: DialogProps) {\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [category, setCategory] = useState('');\n const [priority, setPriority] = useState<(typeof PRIORITIES)[number]>('MEDIUM');\n const [assignedTo, setAssignedTo] = useState('');\n const [rootCause, setRootCause] = useState('');\n const [workaround, setWorkaround] = useState('');\n\n const reset = () => {\n setTitle('');\n setDescription('');\n setCategory('');\n setPriority('MEDIUM');\n setAssignedTo('');\n setRootCause('');\n setWorkaround('');\n };\n\n const [createProblem, { loading }] = useMutation<CreateProblemResult>(CREATE_PROBLEM_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Problem opened', `${data.createProblem.title} is under investigation.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not open problem', error.message),\n });\n\n const handleSubmit = () => {\n if (!title.trim() || !description.trim() || !category.trim()) {\n notify.warning('Missing details', 'Title, description and category are required.');\n return;\n }\n void createProblem({\n variables: {\n input: {\n title: title.trim(),\n description: description.trim(),\n category: category.trim(),\n priority,\n assignedTo: assignedTo.trim() || null,\n rootCause: rootCause.trim() || null,\n workaround: workaround.trim() || null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Problem</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Open a problem record to investigate the root cause behind recurring incidents.\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., Recurring VPN drops\"\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 pattern of incidents does this explain?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input\n placeholder=\"e.g., network\"\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n />\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>\n <div className=\"space-y-2\">\n <Label>Assigned To</Label>\n <Input value={assignedTo} onChange={(e) => setAssignedTo(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Root Cause</Label>\n <Textarea\n placeholder=\"If already known\"\n value={rootCause}\n onChange={(e) => setRootCause(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Workaround</Label>\n <Textarea\n placeholder=\"Any temporary mitigation\"\n value={workaround}\n onChange={(e) => setWorkaround(e.target.value)}\n rows={2}\n />\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 ? 'Opening…' : 'Open Problem'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"KnowledgeBasePage.js","names":[],"sources":["../../../src/assethandler/pages/KnowledgeBasePage.tsx"],"sourcesContent":["import { BookOpen, Plus, Search } from 'lucide-react';\nimport {\n Badge,\n Button,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\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 { useMemo, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { Pagination, usePagination } from '../components';\nimport { notify } from '../utils/toast';\n\nconst KNOWLEDGE_ARTICLES_QUERY = gql`\n query AssetHandlerKnowledgeArticles {\n knowledgeArticles {\n id\n title\n slug\n summary\n content\n status\n tags\n updatedAt\n }\n }\n`;\n\nconst CREATE_KNOWLEDGE_ARTICLE_MUTATION = gql`\n mutation AssetHandlerCreateKnowledgeArticle($input: CreateKnowledgeArticleInput!) {\n createKnowledgeArticle(input: $input) {\n id\n title\n }\n }\n`;\n\ninterface KnowledgeArticle {\n id: string;\n title: string;\n slug: string;\n summary: string | null;\n content: string;\n status: string;\n tags: string[];\n updatedAt: string;\n}\n\ninterface CreateKnowledgeArticleResult {\n createKnowledgeArticle: { id: string; title: string };\n}\n\nconst ARTICLE_STATUSES = ['DRAFT', 'PUBLISHED', 'ARCHIVED'] as const;\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\nfunction PublishArticleDialog({\n open,\n onOpenChange,\n onPublished,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onPublished: () => void;\n}) {\n const [title, setTitle] = useState('');\n const [slug, setSlug] = useState('');\n const [slugTouched, setSlugTouched] = useState(false);\n const [summary, setSummary] = useState('');\n const [content, setContent] = useState('');\n const [tags, setTags] = useState('');\n const [status, setStatus] = useState<(typeof ARTICLE_STATUSES)[number]>('PUBLISHED');\n\n const reset = () => {\n setTitle('');\n setSlug('');\n setSlugTouched(false);\n setSummary('');\n setContent('');\n setTags('');\n setStatus('PUBLISHED');\n };\n\n const [createArticle, { loading }] = useMutation<CreateKnowledgeArticleResult>(\n CREATE_KNOWLEDGE_ARTICLE_MUTATION,\n {\n onCompleted: (data) => {\n notify.success('Article published', `${data.createKnowledgeArticle.title} is now available.`);\n reset();\n onOpenChange(false);\n onPublished();\n },\n onError: (error) => notify.error('Could not publish article', error.message),\n }\n );\n\n const handleTitleChange = (next: string) => {\n setTitle(next);\n if (!slugTouched) setSlug(slugify(next));\n };\n\n const handleSubmit = () => {\n if (!title.trim() || !slug.trim()) {\n notify.warning('Missing details', 'Title and slug are required.');\n return;\n }\n if (!content.trim()) {\n notify.warning('Body required', 'Write the article body before publishing.');\n return;\n }\n const parsedTags = tags\n .split(',')\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n void createArticle({\n variables: {\n input: {\n title: title.trim(),\n slug: slug.trim(),\n summary: summary.trim() || null,\n content: content.trim(),\n tags: parsedTags,\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-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Publish Article</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Capture a runbook or fix so your team can find it later.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input value={title} onChange={(e) => handleTitleChange(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Slug *</Label>\n <Input\n value={slug}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(e.target.value);\n }}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Summary</Label>\n <Input\n placeholder=\"One-line description\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Body *</Label>\n <Textarea\n placeholder=\"Write the full article body — steps, context, links…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={8}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Tags</Label>\n <Input\n placeholder=\"comma,separated,tags\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n />\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 {ARTICLE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {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 ? 'Publishing…' : 'Publish article'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport function KnowledgeBasePage() {\n const [dialogOpen, setDialogOpen] = useState(false);\n const [search, setSearch] = useState('');\n\n const { data, loading, refetch } = useQuery<{ knowledgeArticles: KnowledgeArticle[] }>(\n KNOWLEDGE_ARTICLES_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const articles = useMemo(() => data?.knowledgeArticles ?? [], [data?.knowledgeArticles]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n if (!term) return articles;\n return articles.filter((article) => {\n const haystack = [\n article.title,\n article.summary ?? '',\n article.content,\n article.tags.join(' '),\n ]\n .join(' ')\n .toLowerCase();\n return haystack.includes(term);\n });\n }, [articles, search]);\n\n const pagination = usePagination(filtered, { pageSize: 8, resetKey: search });\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <BookOpen className=\"h-6 w-6\" /> <GradientText>Knowledge Base</GradientText>\n </h1>\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n Publish Article\n </Button>\n </div>\n <PagePurpose>\n Capture the fixes and runbooks your team relies on so the next person doesn&apos;t start\n from scratch. Link articles to incidents and assets, then search them when troubleshooting —\n turning one-off resolutions into repeatable, shareable operational knowledge.\n </PagePurpose>\n\n <PublishArticleDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n onPublished={() => void refetch()}\n />\n\n <div className=\"relative max-w-md\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n className=\"pl-9\"\n placeholder=\"Search articles by title, body or tag…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label=\"Search knowledge articles\"\n />\n </div>\n\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && articles.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && filtered.length === 0 ? (\n <IllustratedEmptyState\n illustration={search ? 'empty-search' : 'empty-data'}\n title={search ? 'No articles match your search' : 'No knowledge articles found'}\n description={\n search\n ? 'Try a different term, or clear the search to see all articles.'\n : 'Publish an article to start building your knowledge base.'\n }\n />\n ) : null}\n {pagination.pageItems.map((article) => (\n <article key={article.id} className=\"rounded-lg border p-4\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">{article.status}</p>\n <h2 className=\"mt-1 text-base font-semibold\">{article.title}</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {article.summary ?? 'No summary available.'}\n </p>\n {article.tags.length > 0 ? (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {article.tags.map((tag) => (\n <Badge key={tag} variant=\"secondary\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n ) : null}\n <p className=\"mt-2 text-xs text-text-secondary\">/{article.slug}</p>\n </article>\n ))}\n </div>\n <Pagination\n page={pagination.page}\n pageCount={pagination.pageCount}\n rangeStart={pagination.rangeStart}\n rangeEnd={pagination.rangeEnd}\n totalItems={pagination.totalItems}\n canPrev={pagination.canPrev}\n canNext={pagination.canNext}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"articles\"\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"KnowledgeBasePage.js","names":[],"sources":["../../../src/assethandler/pages/KnowledgeBasePage.tsx"],"sourcesContent":["import { BookOpen, Plus, Search } from 'lucide-react';\nimport {\n Badge,\n Button,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\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 { useMemo, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { Pagination, usePagination } from '../components';\nimport { notify } from '../utils/toast';\n\nconst KNOWLEDGE_ARTICLES_QUERY = gql`\n query AssetHandlerKnowledgeArticles {\n knowledgeArticles {\n id\n title\n slug\n summary\n content\n status\n tags\n updatedAt\n }\n }\n`;\n\nconst CREATE_KNOWLEDGE_ARTICLE_MUTATION = gql`\n mutation AssetHandlerCreateKnowledgeArticle($input: CreateKnowledgeArticleInput!) {\n createKnowledgeArticle(input: $input) {\n id\n title\n }\n }\n`;\n\ninterface KnowledgeArticle {\n id: string;\n title: string;\n slug: string;\n summary: string | null;\n content: string;\n status: string;\n tags: string[];\n updatedAt: string;\n}\n\ninterface CreateKnowledgeArticleResult {\n createKnowledgeArticle: { id: string; title: string };\n}\n\nconst ARTICLE_STATUSES = ['DRAFT', 'PUBLISHED', 'ARCHIVED'] as const;\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\nfunction PublishArticleDialog({\n open,\n onOpenChange,\n onPublished,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onPublished: () => void;\n}) {\n const [title, setTitle] = useState('');\n const [slug, setSlug] = useState('');\n const [slugTouched, setSlugTouched] = useState(false);\n const [summary, setSummary] = useState('');\n const [content, setContent] = useState('');\n const [tags, setTags] = useState('');\n const [status, setStatus] = useState<(typeof ARTICLE_STATUSES)[number]>('PUBLISHED');\n\n const reset = () => {\n setTitle('');\n setSlug('');\n setSlugTouched(false);\n setSummary('');\n setContent('');\n setTags('');\n setStatus('PUBLISHED');\n };\n\n const [createArticle, { loading }] = useMutation<CreateKnowledgeArticleResult>(\n CREATE_KNOWLEDGE_ARTICLE_MUTATION,\n {\n onCompleted: (data) => {\n notify.success(\n 'Article published',\n `${data.createKnowledgeArticle.title} is now available.`\n );\n reset();\n onOpenChange(false);\n onPublished();\n },\n onError: (error) => notify.error('Could not publish article', error.message),\n }\n );\n\n const handleTitleChange = (next: string) => {\n setTitle(next);\n if (!slugTouched) setSlug(slugify(next));\n };\n\n const handleSubmit = () => {\n if (!title.trim() || !slug.trim()) {\n notify.warning('Missing details', 'Title and slug are required.');\n return;\n }\n if (!content.trim()) {\n notify.warning('Body required', 'Write the article body before publishing.');\n return;\n }\n const parsedTags = tags\n .split(',')\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n void createArticle({\n variables: {\n input: {\n title: title.trim(),\n slug: slug.trim(),\n summary: summary.trim() || null,\n content: content.trim(),\n tags: parsedTags,\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-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Publish Article</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Capture a runbook or fix so your team can find it later.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Title *</Label>\n <Input value={title} onChange={(e) => handleTitleChange(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Slug *</Label>\n <Input\n value={slug}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(e.target.value);\n }}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Summary</Label>\n <Input\n placeholder=\"One-line description\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Body *</Label>\n <Textarea\n placeholder=\"Write the full article body — steps, context, links…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n rows={8}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Tags</Label>\n <Input\n placeholder=\"comma,separated,tags\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n />\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 {ARTICLE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {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 ? 'Publishing…' : 'Publish article'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport function KnowledgeBasePage() {\n const [dialogOpen, setDialogOpen] = useState(false);\n const [search, setSearch] = useState('');\n\n const { data, loading, refetch } = useQuery<{ knowledgeArticles: KnowledgeArticle[] }>(\n KNOWLEDGE_ARTICLES_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const articles = useMemo(() => data?.knowledgeArticles ?? [], [data?.knowledgeArticles]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n if (!term) return articles;\n return articles.filter((article) => {\n const haystack = [\n article.title,\n article.summary ?? '',\n article.content,\n article.tags.join(' '),\n ]\n .join(' ')\n .toLowerCase();\n return haystack.includes(term);\n });\n }, [articles, search]);\n\n const pagination = usePagination(filtered, { pageSize: 8, resetKey: search });\n\n return (\n <div className=\"space-y-4 p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <BookOpen className=\"h-6 w-6\" /> <GradientText>Knowledge Base</GradientText>\n </h1>\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n Publish Article\n </Button>\n </div>\n <PagePurpose>\n Capture the fixes and runbooks your team relies on so the next person doesn&apos;t start\n from scratch. Link articles to incidents and assets, then search them when troubleshooting —\n turning one-off resolutions into repeatable, shareable operational knowledge.\n </PagePurpose>\n\n <PublishArticleDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n onPublished={() => void refetch()}\n />\n\n <div className=\"relative max-w-md\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n className=\"pl-9\"\n placeholder=\"Search articles by title, body or tag…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label=\"Search knowledge articles\"\n />\n </div>\n\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && articles.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && filtered.length === 0 ? (\n <IllustratedEmptyState\n illustration={search ? 'empty-search' : 'empty-data'}\n title={search ? 'No articles match your search' : 'No knowledge articles found'}\n description={\n search\n ? 'Try a different term, or clear the search to see all articles.'\n : 'Publish an article to start building your knowledge base.'\n }\n />\n ) : null}\n {pagination.pageItems.map((article) => (\n <article key={article.id} className=\"rounded-lg border p-4\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">{article.status}</p>\n <h2 className=\"mt-1 text-base font-semibold\">{article.title}</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {article.summary ?? 'No summary available.'}\n </p>\n {article.tags.length > 0 ? (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {article.tags.map((tag) => (\n <Badge key={tag} variant=\"secondary\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n ) : null}\n <p className=\"mt-2 text-xs text-text-secondary\">/{article.slug}</p>\n </article>\n ))}\n </div>\n <Pagination\n page={pagination.page}\n pageCount={pagination.pageCount}\n rangeStart={pagination.rangeStart}\n rangeEnd={pagination.rangeEnd}\n totalItems={pagination.totalItems}\n canPrev={pagination.canPrev}\n canNext={pagination.canNext}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"articles\"\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"OperationsMaintenanceDetailPage.js","names":[],"sources":["../../../src/assethandler/pages/OperationsMaintenanceDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\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 { ArrowLeft, CheckCircle, Pencil, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport { AttachmentsPanel, CommentsPanel, ActivityTimeline, NotFoundState } from '../components';\n\nconst MAINTENANCE_QUERY = gql`\n query AssetHandlerMaintenanceRecord($id: ID!) {\n maintenanceRecord(id: $id) {\n id\n assetId\n title\n description\n type\n status\n priority\n scheduledAt\n completedAt\n estimatedCost\n actualCost\n checklist\n }\n }\n`;\n\n// checklist is a JSON scalar on the wire — model each item loosely and coerce.\ninterface RawChecklistItem {\n task?: unknown;\n completed?: unknown;\n}\n\ninterface MaintenanceRecordDetail {\n id: string;\n assetId: string;\n title: string;\n description?: string | null;\n type: string;\n status: string;\n priority: string;\n scheduledAt?: string | null;\n completedAt?: string | null;\n estimatedCost?: number | null;\n actualCost?: number | null;\n checklist?: RawChecklistItem[] | null;\n}\n\ninterface MaintenanceQueryResult {\n maintenanceRecord: MaintenanceRecordDetail | null;\n}\n\nconst COMPLETE_MAINTENANCE_MUTATION = gql`\n mutation AssetHandlerCompleteMaintenance($id: ID!, $input: CompleteMaintenanceRecordInput!) {\n completeMaintenanceRecord(id: $id, input: $input) {\n id\n status\n completedAt\n }\n }\n`;\n\nconst UPDATE_MAINTENANCE_MUTATION = gql`\n mutation AssetHandlerUpdateMaintenance($id: ID!, $input: UpdateMaintenanceRecordInput!) {\n updateMaintenanceRecord(id: $id, input: $input) {\n id\n }\n }\n`;\n\nconst SET_CHECKLIST_ITEM_MUTATION = gql`\n mutation AssetHandlerSetChecklistItem($recordId: ID!, $index: Int!, $completed: Boolean!) {\n setMaintenanceChecklistItem(recordId: $recordId, index: $index, completed: $completed) {\n id\n checklist\n }\n }\n`;\n\nconst MAINTENANCE_TYPES = [\n 'PREVENTIVE',\n 'CORRECTIVE',\n 'INSPECTION',\n 'CALIBRATION',\n 'EMERGENCY',\n] as const;\nconst MAINTENANCE_STATUSES = ['SCHEDULED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'] as const;\nconst PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\n\nconst priorityColors: Record<string, 'destructive' | 'default' | 'secondary' | 'outline'> = {\n critical: 'destructive',\n high: 'destructive',\n medium: 'default',\n low: 'secondary',\n};\n\nconst money = (value: number | null | undefined): string =>\n value == null ? '—' : `$${Number(value).toLocaleString(undefined, { maximumFractionDigits: 2 })}`;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\ninterface EditForm {\n title: string;\n description: string;\n type: string;\n priority: string;\n status: string;\n scheduledAt: string;\n estimatedCost: string;\n actualCost: string;\n}\n\nconst toDateInput = (iso: string | null | undefined): string =>\n iso ? new Date(iso).toISOString().slice(0, 10) : '';\n\nexport function OperationsMaintenanceDetailPage() {\n const navigate = useAssetHandlerNavigate();\n const { maintenanceId } = useParams<{ maintenanceId: string }>();\n const { assets, refetchBootstrap } = useAssetHandlerStore();\n\n const { data, loading, refetch } = useQuery<MaintenanceQueryResult>(MAINTENANCE_QUERY, {\n variables: { id: maintenanceId ?? '' },\n skip: !maintenanceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const [showComplete, setShowComplete] = useState(false);\n const [showEdit, setShowEdit] = useState(false);\n const [completeForm, setCompleteForm] = useState({ actualCost: '', notes: '' });\n const [editForm, setEditForm] = useState<EditForm | null>(null);\n const [pendingIndex, setPendingIndex] = useState<number | null>(null);\n\n const assetName = useMemo(() => {\n const byId = new Map(assets.map((a) => [a.id, a.name]));\n return (id: string) => (id ? (byId.get(id) ?? id) : null);\n }, [assets]);\n\n const [completeMaintenance, { loading: completing }] = useMutation(COMPLETE_MAINTENANCE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Maintenance completed', 'The job has been signed off.');\n setShowComplete(false);\n void refetch();\n },\n onError: (error) => notify.error('Could not complete maintenance', error.message),\n });\n\n const [updateMaintenance, { loading: updating }] = useMutation(UPDATE_MAINTENANCE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Maintenance updated', 'Your changes have been saved.');\n setShowEdit(false);\n void refetch();\n },\n onError: (error) => notify.error('Could not update maintenance', error.message),\n });\n\n const [setChecklistItem] = useMutation(SET_CHECKLIST_ITEM_MUTATION, {\n onCompleted: () => {\n setPendingIndex(null);\n void refetch();\n refetchBootstrap();\n },\n onError: (error) => {\n setPendingIndex(null);\n notify.error('Could not update checklist', error.message);\n },\n });\n\n if (loading && !data) {\n return (\n <div className=\"space-y-3 p-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n );\n }\n\n const raw = data?.maintenanceRecord;\n if (!raw) {\n return (\n <NotFoundState\n entityLabel=\"Maintenance record\"\n description=\"This record may have been completed or removed. Return to the operations hub to find it.\"\n backLabel=\"Back to operations\"\n onBack={() => navigate('/operations/operations-hub')}\n />\n );\n }\n\n const record = {\n ...raw,\n status: raw.status.toLowerCase(),\n priority: raw.priority.toLowerCase(),\n type: raw.type.toLowerCase(),\n checklist: (raw.checklist ?? []).map((item) => ({\n task: String(item.task ?? 'Task'),\n completed: Boolean(item.completed),\n })),\n };\n const isClosed = record.status === 'completed' || record.status === 'cancelled';\n const completedTasks = record.checklist.filter((c) => c.completed).length;\n const totalTasks = record.checklist.length;\n const progress = totalTasks === 0 ? 0 : Math.round((completedTasks / totalTasks) * 100);\n const linkedAsset = assetName(record.assetId);\n\n const toggleChecklistItem = (index: number, completed: boolean) => {\n setPendingIndex(index);\n void setChecklistItem({ variables: { recordId: record.id, index, completed } });\n };\n\n const openEdit = () => {\n setEditForm({\n title: raw.title,\n description: raw.description ?? '',\n type: raw.type.toUpperCase(),\n priority: raw.priority.toUpperCase(),\n status: raw.status.toUpperCase(),\n scheduledAt: toDateInput(raw.scheduledAt),\n estimatedCost: raw.estimatedCost != null ? String(raw.estimatedCost) : '',\n actualCost: raw.actualCost != null ? String(raw.actualCost) : '',\n });\n setShowEdit(true);\n };\n\n const setEdit = (field: keyof EditForm, value: string) =>\n setEditForm((prev) => (prev ? { ...prev, [field]: value } : prev));\n\n const submitComplete = () => {\n const parsedCost = Number(completeForm.actualCost);\n void completeMaintenance({\n variables: {\n id: record.id,\n input: {\n actualCost:\n completeForm.actualCost.trim() !== '' && Number.isFinite(parsedCost)\n ? parsedCost\n : null,\n notes: completeForm.notes.trim() || null,\n },\n },\n });\n };\n\n const submitEdit = () => {\n if (!editForm) return;\n if (!editForm.title.trim()) {\n notify.warning('Title required', 'A maintenance record must have a title.');\n return;\n }\n const optionalNumber = (value: string): number | null => {\n const trimmed = value.trim();\n if (trimmed === '') return null;\n const parsed = Number(trimmed);\n return Number.isFinite(parsed) ? parsed : null;\n };\n void updateMaintenance({\n variables: {\n id: record.id,\n input: {\n title: editForm.title.trim(),\n description: editForm.description.trim() || null,\n type: editForm.type,\n priority: editForm.priority,\n status: editForm.status,\n scheduledAt: editForm.scheduledAt\n ? new Date(editForm.scheduledAt).toISOString()\n : null,\n estimatedCost: optionalNumber(editForm.estimatedCost),\n actualCost: optionalNumber(editForm.actualCost),\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-4\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => navigate('/operations/operations-hub')}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-bold\">\n <GradientText>{record.title}</GradientText>\n </h1>\n <Badge variant={priorityColors[record.priority] ?? 'outline'}>\n {record.priority}\n </Badge>\n <Badge variant={record.status === 'completed' ? 'default' : 'secondary'}>\n {record.status.replace(/_/g, ' ')}\n </Badge>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {record.id} &middot; {humanize(record.type)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={openEdit}>\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </Button>\n {!isClosed ? (\n <Button size=\"sm\" onClick={() => setShowComplete(true)}>\n <CheckCircle className=\"mr-2 h-4 w-4\" />\n Complete\n </Button>\n ) : null}\n </div>\n </div>\n\n <PagePurpose>\n Everything needed to carry out and sign off a single maintenance job: the asset, the\n schedule, the step-by-step checklist and live progress. Work through the checklist, log\n notes and mark it complete so the asset&apos;s service history stays accurate.\n </PagePurpose>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <EmphasisPanel className=\"lg:col-span-2\">\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4 px-0 pb-0\">\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary\">Description</h4>\n <p className=\"text-sm mt-1\">{record.description || '—'}</p>\n </div>\n <Separator />\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-text-secondary\">Asset</span>\n {linkedAsset ? (\n <p className=\"font-medium\">\n <button\n type=\"button\"\n className=\"text-action-primary-text hover:underline\"\n onClick={() => navigate('/assets/' + record.assetId)}\n >\n {linkedAsset}\n </button>\n </p>\n ) : (\n <p className=\"font-medium\">—</p>\n )}\n </div>\n <div>\n <span className=\"text-text-secondary\">Type</span>\n <p className=\"font-medium capitalize\">{record.type}</p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Scheduled</span>\n <p className=\"font-medium\">\n {record.scheduledAt\n ? new Date(record.scheduledAt).toLocaleDateString()\n : 'Not scheduled'}\n </p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Completed</span>\n <p className=\"font-medium\">\n {record.completedAt ? new Date(record.completedAt).toLocaleDateString() : '—'}\n </p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Estimated Cost</span>\n <p className=\"font-medium\">{money(record.estimatedCost)}</p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Actual Cost</span>\n <p className=\"font-medium\">{money(record.actualCost)}</p>\n </div>\n </div>\n <Separator />\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary mb-3\">\n Checklist ({completedTasks}/{totalTasks})\n </h4>\n {totalTasks === 0 ? (\n <p className=\"text-sm text-text-secondary\">No checklist items for this job.</p>\n ) : (\n <div className=\"space-y-2\">\n {record.checklist.map((item, i) => (\n <label\n key={i}\n className=\"flex items-center gap-3 rounded border p-2.5 cursor-pointer\"\n >\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 accent-action-primary-bg\"\n checked={item.completed}\n disabled={pendingIndex === i}\n onChange={(e) => toggleChecklistItem(i, e.target.checked)}\n />\n <span\n className={`text-sm ${item.completed ? 'line-through text-text-secondary' : ''}`}\n >\n {item.task}\n </span>\n {pendingIndex === i ? (\n <Loader2 className=\"ml-auto h-3.5 w-3.5 animate-spin text-text-secondary\" />\n ) : null}\n </label>\n ))}\n </div>\n )}\n </div>\n </CardContent>\n </EmphasisPanel>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Progress</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-center mb-4\">\n <p className=\"text-4xl font-bold tabular-nums\">{progress}%</p>\n <p className=\"text-sm text-text-secondary\">Complete</p>\n </div>\n <div className=\"h-3 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </CardContent>\n </Card>\n </div>\n\n <Tabs defaultValue=\"attachments\">\n <TabsList>\n <TabsTrigger value=\"attachments\">Attachments</TabsTrigger>\n <TabsTrigger value=\"discussion\">Discussion</TabsTrigger>\n </TabsList>\n <TabsContent value=\"attachments\" className=\"mt-4\">\n <AttachmentsPanel entityType=\"assethandler-maintenance-record\" entityId={record.id} />\n </TabsContent>\n <TabsContent value=\"discussion\" className=\"mt-4\">\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <CommentsPanel entityType=\"assethandler-maintenance-record\" entityId={record.id} />\n <ActivityTimeline resourceId={record.id} />\n </div>\n </TabsContent>\n </Tabs>\n\n {/* Complete dialog */}\n <ResponsiveDialog open={showComplete} onOpenChange={setShowComplete}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Complete maintenance</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Record the final cost and closing notes. This marks the job as completed.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label>Actual Cost</Label>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n placeholder=\"e.g., 500\"\n value={completeForm.actualCost}\n onChange={(e) =>\n setCompleteForm((prev) => ({ ...prev, actualCost: e.target.value }))\n }\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Notes</Label>\n <Textarea\n placeholder=\"What was done?\"\n value={completeForm.notes}\n onChange={(e) => setCompleteForm((prev) => ({ ...prev, notes: e.target.value }))}\n rows={3}\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setShowComplete(false)} disabled={completing}>\n Cancel\n </Button>\n <Button onClick={submitComplete} disabled={completing}>\n {completing ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {completing ? 'Completing…' : 'Complete maintenance'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit dialog */}\n <ResponsiveDialog open={showEdit} onOpenChange={setShowEdit}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit maintenance</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Update the maintenance job&apos;s details and schedule.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n {editForm ? (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label>Title</Label>\n <Input value={editForm.title} onChange={(e) => setEdit('title', e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Description</Label>\n <Textarea\n value={editForm.description}\n onChange={(e) => setEdit('description', 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={editForm.type} onValueChange={(v) => setEdit('type', v)}>\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={editForm.priority} onValueChange={(v) => setEdit('priority', v)}>\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={editForm.status} onValueChange={(v) => setEdit('status', v)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {MAINTENANCE_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 className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Scheduled Date</Label>\n <Input\n type=\"date\"\n value={editForm.scheduledAt}\n onChange={(e) => setEdit('scheduledAt', e.target.value)}\n />\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 value={editForm.estimatedCost}\n onChange={(e) => setEdit('estimatedCost', e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Actual Cost</Label>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={editForm.actualCost}\n onChange={(e) => setEdit('actualCost', e.target.value)}\n />\n </div>\n </div>\n </div>\n ) : null}\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setShowEdit(false)} disabled={updating}>\n Cancel\n </Button>\n <Button onClick={submitEdit} disabled={updating}>\n {updating ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {updating ? 'Saving…' : 'Save changes'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"OperationsMaintenanceDetailPage.js","names":[],"sources":["../../../src/assethandler/pages/OperationsMaintenanceDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\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 { ArrowLeft, CheckCircle, Pencil, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport { AttachmentsPanel, CommentsPanel, ActivityTimeline, NotFoundState } from '../components';\n\nconst MAINTENANCE_QUERY = gql`\n query AssetHandlerMaintenanceRecord($id: ID!) {\n maintenanceRecord(id: $id) {\n id\n assetId\n title\n description\n type\n status\n priority\n scheduledAt\n completedAt\n estimatedCost\n actualCost\n checklist\n }\n }\n`;\n\n// checklist is a JSON scalar on the wire — model each item loosely and coerce.\ninterface RawChecklistItem {\n task?: unknown;\n completed?: unknown;\n}\n\ninterface MaintenanceRecordDetail {\n id: string;\n assetId: string;\n title: string;\n description?: string | null;\n type: string;\n status: string;\n priority: string;\n scheduledAt?: string | null;\n completedAt?: string | null;\n estimatedCost?: number | null;\n actualCost?: number | null;\n checklist?: RawChecklistItem[] | null;\n}\n\ninterface MaintenanceQueryResult {\n maintenanceRecord: MaintenanceRecordDetail | null;\n}\n\nconst COMPLETE_MAINTENANCE_MUTATION = gql`\n mutation AssetHandlerCompleteMaintenance($id: ID!, $input: CompleteMaintenanceRecordInput!) {\n completeMaintenanceRecord(id: $id, input: $input) {\n id\n status\n completedAt\n }\n }\n`;\n\nconst UPDATE_MAINTENANCE_MUTATION = gql`\n mutation AssetHandlerUpdateMaintenance($id: ID!, $input: UpdateMaintenanceRecordInput!) {\n updateMaintenanceRecord(id: $id, input: $input) {\n id\n }\n }\n`;\n\nconst SET_CHECKLIST_ITEM_MUTATION = gql`\n mutation AssetHandlerSetChecklistItem($recordId: ID!, $index: Int!, $completed: Boolean!) {\n setMaintenanceChecklistItem(recordId: $recordId, index: $index, completed: $completed) {\n id\n checklist\n }\n }\n`;\n\nconst MAINTENANCE_TYPES = [\n 'PREVENTIVE',\n 'CORRECTIVE',\n 'INSPECTION',\n 'CALIBRATION',\n 'EMERGENCY',\n] as const;\nconst MAINTENANCE_STATUSES = ['SCHEDULED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'] as const;\nconst PRIORITIES = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] as const;\n\nconst priorityColors: Record<string, 'destructive' | 'default' | 'secondary' | 'outline'> = {\n critical: 'destructive',\n high: 'destructive',\n medium: 'default',\n low: 'secondary',\n};\n\nconst money = (value: number | null | undefined): string =>\n value == null ? '—' : `$${Number(value).toLocaleString(undefined, { maximumFractionDigits: 2 })}`;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\ninterface EditForm {\n title: string;\n description: string;\n type: string;\n priority: string;\n status: string;\n scheduledAt: string;\n estimatedCost: string;\n actualCost: string;\n}\n\nconst toDateInput = (iso: string | null | undefined): string =>\n iso ? new Date(iso).toISOString().slice(0, 10) : '';\n\nexport function OperationsMaintenanceDetailPage() {\n const navigate = useAssetHandlerNavigate();\n const { maintenanceId } = useParams<{ maintenanceId: string }>();\n const { assets, refetchBootstrap } = useAssetHandlerStore();\n\n const { data, loading, refetch } = useQuery<MaintenanceQueryResult>(MAINTENANCE_QUERY, {\n variables: { id: maintenanceId ?? '' },\n skip: !maintenanceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const [showComplete, setShowComplete] = useState(false);\n const [showEdit, setShowEdit] = useState(false);\n const [completeForm, setCompleteForm] = useState({ actualCost: '', notes: '' });\n const [editForm, setEditForm] = useState<EditForm | null>(null);\n const [pendingIndex, setPendingIndex] = useState<number | null>(null);\n\n const assetName = useMemo(() => {\n const byId = new Map(assets.map((a) => [a.id, a.name]));\n return (id: string) => (id ? (byId.get(id) ?? id) : null);\n }, [assets]);\n\n const [completeMaintenance, { loading: completing }] = useMutation(\n COMPLETE_MAINTENANCE_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Maintenance completed', 'The job has been signed off.');\n setShowComplete(false);\n void refetch();\n },\n onError: (error) => notify.error('Could not complete maintenance', error.message),\n }\n );\n\n const [updateMaintenance, { loading: updating }] = useMutation(UPDATE_MAINTENANCE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Maintenance updated', 'Your changes have been saved.');\n setShowEdit(false);\n void refetch();\n },\n onError: (error) => notify.error('Could not update maintenance', error.message),\n });\n\n const [setChecklistItem] = useMutation(SET_CHECKLIST_ITEM_MUTATION, {\n onCompleted: () => {\n setPendingIndex(null);\n void refetch();\n refetchBootstrap();\n },\n onError: (error) => {\n setPendingIndex(null);\n notify.error('Could not update checklist', error.message);\n },\n });\n\n if (loading && !data) {\n return (\n <div className=\"space-y-3 p-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n );\n }\n\n const raw = data?.maintenanceRecord;\n if (!raw) {\n return (\n <NotFoundState\n entityLabel=\"Maintenance record\"\n description=\"This record may have been completed or removed. Return to the operations hub to find it.\"\n backLabel=\"Back to operations\"\n onBack={() => navigate('/operations/operations-hub')}\n />\n );\n }\n\n const record = {\n ...raw,\n status: raw.status.toLowerCase(),\n priority: raw.priority.toLowerCase(),\n type: raw.type.toLowerCase(),\n checklist: (raw.checklist ?? []).map((item) => ({\n task: String(item.task ?? 'Task'),\n completed: Boolean(item.completed),\n })),\n };\n const isClosed = record.status === 'completed' || record.status === 'cancelled';\n const completedTasks = record.checklist.filter((c) => c.completed).length;\n const totalTasks = record.checklist.length;\n const progress = totalTasks === 0 ? 0 : Math.round((completedTasks / totalTasks) * 100);\n const linkedAsset = assetName(record.assetId);\n\n const toggleChecklistItem = (index: number, completed: boolean) => {\n setPendingIndex(index);\n void setChecklistItem({ variables: { recordId: record.id, index, completed } });\n };\n\n const openEdit = () => {\n setEditForm({\n title: raw.title,\n description: raw.description ?? '',\n type: raw.type.toUpperCase(),\n priority: raw.priority.toUpperCase(),\n status: raw.status.toUpperCase(),\n scheduledAt: toDateInput(raw.scheduledAt),\n estimatedCost: raw.estimatedCost != null ? String(raw.estimatedCost) : '',\n actualCost: raw.actualCost != null ? String(raw.actualCost) : '',\n });\n setShowEdit(true);\n };\n\n const setEdit = (field: keyof EditForm, value: string) =>\n setEditForm((prev) => (prev ? { ...prev, [field]: value } : prev));\n\n const submitComplete = () => {\n const parsedCost = Number(completeForm.actualCost);\n void completeMaintenance({\n variables: {\n id: record.id,\n input: {\n actualCost:\n completeForm.actualCost.trim() !== '' && Number.isFinite(parsedCost)\n ? parsedCost\n : null,\n notes: completeForm.notes.trim() || null,\n },\n },\n });\n };\n\n const submitEdit = () => {\n if (!editForm) return;\n if (!editForm.title.trim()) {\n notify.warning('Title required', 'A maintenance record must have a title.');\n return;\n }\n const optionalNumber = (value: string): number | null => {\n const trimmed = value.trim();\n if (trimmed === '') return null;\n const parsed = Number(trimmed);\n return Number.isFinite(parsed) ? parsed : null;\n };\n void updateMaintenance({\n variables: {\n id: record.id,\n input: {\n title: editForm.title.trim(),\n description: editForm.description.trim() || null,\n type: editForm.type,\n priority: editForm.priority,\n status: editForm.status,\n scheduledAt: editForm.scheduledAt ? new Date(editForm.scheduledAt).toISOString() : null,\n estimatedCost: optionalNumber(editForm.estimatedCost),\n actualCost: optionalNumber(editForm.actualCost),\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-4\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => navigate('/operations/operations-hub')}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-bold\">\n <GradientText>{record.title}</GradientText>\n </h1>\n <Badge variant={priorityColors[record.priority] ?? 'outline'}>\n {record.priority}\n </Badge>\n <Badge variant={record.status === 'completed' ? 'default' : 'secondary'}>\n {record.status.replace(/_/g, ' ')}\n </Badge>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {record.id} &middot; {humanize(record.type)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={openEdit}>\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </Button>\n {!isClosed ? (\n <Button size=\"sm\" onClick={() => setShowComplete(true)}>\n <CheckCircle className=\"mr-2 h-4 w-4\" />\n Complete\n </Button>\n ) : null}\n </div>\n </div>\n\n <PagePurpose>\n Everything needed to carry out and sign off a single maintenance job: the asset, the\n schedule, the step-by-step checklist and live progress. Work through the checklist, log\n notes and mark it complete so the asset&apos;s service history stays accurate.\n </PagePurpose>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <EmphasisPanel className=\"lg:col-span-2\">\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4 px-0 pb-0\">\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary\">Description</h4>\n <p className=\"text-sm mt-1\">{record.description || '—'}</p>\n </div>\n <Separator />\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-text-secondary\">Asset</span>\n {linkedAsset ? (\n <p className=\"font-medium\">\n <button\n type=\"button\"\n className=\"text-action-primary-text hover:underline\"\n onClick={() => navigate('/assets/' + record.assetId)}\n >\n {linkedAsset}\n </button>\n </p>\n ) : (\n <p className=\"font-medium\">—</p>\n )}\n </div>\n <div>\n <span className=\"text-text-secondary\">Type</span>\n <p className=\"font-medium capitalize\">{record.type}</p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Scheduled</span>\n <p className=\"font-medium\">\n {record.scheduledAt\n ? new Date(record.scheduledAt).toLocaleDateString()\n : 'Not scheduled'}\n </p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Completed</span>\n <p className=\"font-medium\">\n {record.completedAt ? new Date(record.completedAt).toLocaleDateString() : '—'}\n </p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Estimated Cost</span>\n <p className=\"font-medium\">{money(record.estimatedCost)}</p>\n </div>\n <div>\n <span className=\"text-text-secondary\">Actual Cost</span>\n <p className=\"font-medium\">{money(record.actualCost)}</p>\n </div>\n </div>\n <Separator />\n <div>\n <h4 className=\"text-sm font-medium text-text-secondary mb-3\">\n Checklist ({completedTasks}/{totalTasks})\n </h4>\n {totalTasks === 0 ? (\n <p className=\"text-sm text-text-secondary\">No checklist items for this job.</p>\n ) : (\n <div className=\"space-y-2\">\n {record.checklist.map((item, i) => (\n <label\n key={i}\n className=\"flex items-center gap-3 rounded border p-2.5 cursor-pointer\"\n >\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 accent-action-primary-bg\"\n checked={item.completed}\n disabled={pendingIndex === i}\n onChange={(e) => toggleChecklistItem(i, e.target.checked)}\n />\n <span\n className={`text-sm ${item.completed ? 'line-through text-text-secondary' : ''}`}\n >\n {item.task}\n </span>\n {pendingIndex === i ? (\n <Loader2 className=\"ml-auto h-3.5 w-3.5 animate-spin text-text-secondary\" />\n ) : null}\n </label>\n ))}\n </div>\n )}\n </div>\n </CardContent>\n </EmphasisPanel>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Progress</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-center mb-4\">\n <p className=\"text-4xl font-bold tabular-nums\">{progress}%</p>\n <p className=\"text-sm text-text-secondary\">Complete</p>\n </div>\n <div className=\"h-3 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </CardContent>\n </Card>\n </div>\n\n <Tabs defaultValue=\"attachments\">\n <TabsList>\n <TabsTrigger value=\"attachments\">Attachments</TabsTrigger>\n <TabsTrigger value=\"discussion\">Discussion</TabsTrigger>\n </TabsList>\n <TabsContent value=\"attachments\" className=\"mt-4\">\n <AttachmentsPanel entityType=\"assethandler-maintenance-record\" entityId={record.id} />\n </TabsContent>\n <TabsContent value=\"discussion\" className=\"mt-4\">\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <CommentsPanel entityType=\"assethandler-maintenance-record\" entityId={record.id} />\n <ActivityTimeline resourceId={record.id} />\n </div>\n </TabsContent>\n </Tabs>\n\n {/* Complete dialog */}\n <ResponsiveDialog open={showComplete} onOpenChange={setShowComplete}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Complete maintenance</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Record the final cost and closing notes. This marks the job as completed.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label>Actual Cost</Label>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n placeholder=\"e.g., 500\"\n value={completeForm.actualCost}\n onChange={(e) =>\n setCompleteForm((prev) => ({ ...prev, actualCost: e.target.value }))\n }\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Notes</Label>\n <Textarea\n placeholder=\"What was done?\"\n value={completeForm.notes}\n onChange={(e) => setCompleteForm((prev) => ({ ...prev, notes: e.target.value }))}\n rows={3}\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setShowComplete(false)} disabled={completing}>\n Cancel\n </Button>\n <Button onClick={submitComplete} disabled={completing}>\n {completing ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {completing ? 'Completing…' : 'Complete maintenance'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit dialog */}\n <ResponsiveDialog open={showEdit} onOpenChange={setShowEdit}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit maintenance</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Update the maintenance job&apos;s details and schedule.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n {editForm ? (\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label>Title</Label>\n <Input value={editForm.title} onChange={(e) => setEdit('title', e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Description</Label>\n <Textarea\n value={editForm.description}\n onChange={(e) => setEdit('description', 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={editForm.type} onValueChange={(v) => setEdit('type', v)}>\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={editForm.priority} onValueChange={(v) => setEdit('priority', v)}>\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={editForm.status} onValueChange={(v) => setEdit('status', v)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {MAINTENANCE_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 className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Scheduled Date</Label>\n <Input\n type=\"date\"\n value={editForm.scheduledAt}\n onChange={(e) => setEdit('scheduledAt', e.target.value)}\n />\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 value={editForm.estimatedCost}\n onChange={(e) => setEdit('estimatedCost', e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Actual Cost</Label>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={editForm.actualCost}\n onChange={(e) => setEdit('actualCost', e.target.value)}\n />\n </div>\n </div>\n </div>\n ) : null}\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setShowEdit(false)} disabled={updating}>\n Cancel\n </Button>\n <Button onClick={submitEdit} disabled={updating}>\n {updating ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {updating ? 'Saving…' : 'Save changes'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwCA,IAAM,KAAoB,CAAG;;;;;;;;;;;;;;;;;GA4CvB,KAAgC,CAAG;;;;;;;;GAUnC,KAA8B,CAAG;;;;;;GAQjC,KAA8B,CAAG;;;;;;;GASjC,KAAoB;CACxB;CACA;CACA;CACA;CACA;CACD,EACK,KAAuB;CAAC;CAAa;CAAe;CAAa;CAAY,EAC7E,KAAa;CAAC;CAAY;CAAQ;CAAU;CAAM,EAElD,KAAsF;CAC1F,UAAU;CACV,MAAM;CACN,QAAQ;CACR,KAAK;CACN,EAEK,KAAS,MACb,KAAS,OAAO,MAAM,IAAI,OAAO,EAAM,CAAC,eAAe,KAAA,GAAW,EAAE,uBAAuB,GAAG,CAAC,IAE3F,KAAY,MAChB,EACG,aAAa,CACb,QAAQ,MAAM,IAAI,CAClB,QAAQ,QAAQ,MAAM,EAAE,aAAa,CAAC,EAarC,MAAe,MACnB,IAAM,IAAI,KAAK,EAAI,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG,GAAG;AAEnD,SAAgB,IAAkC;CAChD,IAAM,IAAW,GAAyB,EACpC,EAAE,qBAAkB,IAAsC,EAC1D,EAAE,WAAQ,yBAAqB,GAAsB,EAErD,EAAE,SAAM,aAAS,eAAY,GAAiC,IAAmB;EACrF,WAAW,EAAE,IAAI,KAAiB,IAAI;EACtC,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEI,CAAC,IAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,IAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAc,KAAmB,EAAS;EAAE,YAAY;EAAI,OAAO;EAAI,CAAC,EACzE,CAAC,GAAU,KAAe,EAA0B,KAAK,EACzD,CAAC,GAAc,KAAmB,EAAwB,KAAK,EAE/D,KAAY,SAAc;EAC9B,IAAM,IAAO,IAAI,IAAI,EAAO,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AACvD,UAAQ,MAAgB,IAAM,EAAK,IAAI,EAAG,IAAI,IAAM;IACnD,CAAC,EAAO,CAAC,EAEN,CAAC,IAAqB,EAAE,SAAS,OAAgB,EACrD,IACA;EACE,gBAAgB,CAAC,wBAAwB;EACzC,mBAAmB;AAGZ,GAFL,EAAO,QAAQ,yBAAyB,+BAA+B,EACvE,EAAgB,GAAM,EACjB,GAAS;;EAEhB,UAAU,MAAU,EAAO,MAAM,kCAAkC,EAAM,QAAQ;EAClF,CACF,EAEK,CAAC,IAAmB,EAAE,SAAS,OAAc,EAAY,IAA6B;EAC1F,gBAAgB,CAAC,wBAAwB;EACzC,mBAAmB;AAGZ,GAFL,EAAO,QAAQ,uBAAuB,gCAAgC,EACtE,EAAY,GAAM,EACb,GAAS;;EAEhB,UAAU,MAAU,EAAO,MAAM,gCAAgC,EAAM,QAAQ;EAChF,CAAC,EAEI,CAAC,MAAoB,EAAY,IAA6B;EAClE,mBAAmB;AAGjB,GAFA,EAAgB,KAAK,EAChB,GAAS,EACd,IAAkB;;EAEpB,UAAU,MAAU;AAElB,GADA,EAAgB,KAAK,EACrB,EAAO,MAAM,8BAA8B,EAAM,QAAQ;;EAE5D,CAAC;AAEF,KAAI,MAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,+DAAgE,EAA7E,EAA6E,CACvF;EACE,CAAA;CAIV,IAAM,IAAM,GAAM;AAClB,KAAI,CAAC,EACH,QACE,kBAAC,IAAD;EACE,aAAY;EACZ,aAAY;EACZ,WAAU;EACV,cAAc,EAAS,6BAA6B;EACpD,CAAA;CAIN,IAAM,IAAS;EACb,GAAG;EACH,QAAQ,EAAI,OAAO,aAAa;EAChC,UAAU,EAAI,SAAS,aAAa;EACpC,MAAM,EAAI,KAAK,aAAa;EAC5B,YAAY,EAAI,aAAa,EAAE,EAAE,KAAK,OAAU;GAC9C,MAAM,OAAO,EAAK,QAAQ,OAAO;GACjC,WAAW,EAAQ,EAAK;GACzB,EAAE;EACJ,EACK,KAAW,EAAO,WAAW,eAAe,EAAO,WAAW,aAC9D,IAAiB,EAAO,UAAU,QAAQ,MAAM,EAAE,UAAU,CAAC,QAC7D,IAAa,EAAO,UAAU,QAC9B,IAAW,MAAe,IAAI,IAAI,KAAK,MAAO,IAAiB,IAAc,IAAI,EACjF,IAAc,GAAU,EAAO,QAAQ,EAEvC,MAAuB,GAAe,MAAuB;AAE5D,EADL,EAAgB,EAAM,EACjB,GAAiB,EAAE,WAAW;GAAE,UAAU,EAAO;GAAI;GAAO;GAAW,EAAE,CAAC;IAG3E,WAAiB;AAWrB,EAVA,EAAY;GACV,OAAO,EAAI;GACX,aAAa,EAAI,eAAe;GAChC,MAAM,EAAI,KAAK,aAAa;GAC5B,UAAU,EAAI,SAAS,aAAa;GACpC,QAAQ,EAAI,OAAO,aAAa;GAChC,aAAa,GAAY,EAAI,YAAY;GACzC,eAAe,EAAI,iBAAiB,OAAmC,KAA5B,OAAO,EAAI,cAAc;GACpE,YAAY,EAAI,cAAc,OAAgC,KAAzB,OAAO,EAAI,WAAW;GAC5D,CAAC,EACF,EAAY,GAAK;IAGb,KAAW,GAAuB,MACtC,GAAa,MAAU,KAAO;EAAE,GAAG;GAAO,IAAQ;EAAO,CAAS;AA+CpE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,SAAQ;MACR,MAAK;MACL,eAAe,EAAS,6BAA6B;gBAErD,kBAAC,IAAD,EAAW,WAAU,WAAY,CAAA;MAC1B,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,MAAD;QAAI,WAAU;kBACZ,kBAAC,IAAD,EAAA,UAAe,EAAO,OAAqB,CAAA;QACxC,CAAA;OACL,kBAAC,GAAD;QAAO,SAAS,GAAe,EAAO,aAAa;kBAChD,EAAO;QACF,CAAA;OACR,kBAAC,GAAD;QAAO,SAAS,EAAO,WAAW,cAAc,YAAY;kBACzD,EAAO,OAAO,QAAQ,MAAM,IAAI;QAC3B,CAAA;OACJ;SACN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAO;OAAG;OAAW,EAAS,EAAO,KAAK;OACzC;QACA,EAAA,CAAA,CACF;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAU,SAAS;gBAA7C,CACE,kBAAC,IAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,OAE5B;SACP,KAKE,OAJF,kBAAC,GAAD;MAAQ,MAAK;MAAK,eAAe,EAAgB,GAAK;gBAAtD,CACE,kBAAC,IAAD,EAAa,WAAU,gBAAiB,CAAA,EAAA,WAEjC;QAEP;OACF;;GAEN,kBAAC,IAAD,EAAA,UAAa,0PAIC,CAAA;GAEd,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,IAAD;KAAe,WAAU;eAAzB,CACE,kBAAC,GAAD;MAAY,WAAU;gBACpB,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA;MACnB,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA0C;QAAgB,CAAA,EACxE,kBAAC,KAAD;QAAG,WAAU;kBAAgB,EAAO,eAAe;QAAQ,CAAA,CACvD,EAAA,CAAA;OACN,kBAAC,GAAD,EAAa,CAAA;OACb,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAsB;UAAY,CAAA,EACjD,IACC,kBAAC,KAAD;UAAG,WAAU;oBACX,kBAAC,UAAD;WACE,MAAK;WACL,WAAU;WACV,eAAe,EAAS,aAAa,EAAO,QAAQ;qBAEnD;WACM,CAAA;UACP,CAAA,GAEJ,kBAAC,KAAD;UAAG,WAAU;oBAAc;UAAK,CAAA,CAE9B,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAsB;UAAW,CAAA,EACjD,kBAAC,KAAD;UAAG,WAAU;oBAA0B,EAAO;UAAS,CAAA,CACnD,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAsB;UAAgB,CAAA,EACtD,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAO,cACJ,IAAI,KAAK,EAAO,YAAY,CAAC,oBAAoB,GACjD;UACF,CAAA,CACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAsB;UAAgB,CAAA,EACtD,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAO,cAAc,IAAI,KAAK,EAAO,YAAY,CAAC,oBAAoB,GAAG;UACxE,CAAA,CACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAsB;UAAqB,CAAA,EAC3D,kBAAC,KAAD;UAAG,WAAU;oBAAe,EAAM,EAAO,cAAc;UAAK,CAAA,CACxD,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAsB;UAAkB,CAAA,EACxD,kBAAC,KAAD;UAAG,WAAU;oBAAe,EAAM,EAAO,WAAW;UAAK,CAAA,CACrD,EAAA,CAAA;SACF;;OACN,kBAAC,GAAD,EAAa,CAAA;OACb,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;kBAAd;SAA6D;SAC/C;SAAe;SAAE;SAAW;SACrC;WACJ,MAAe,IACd,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAAoC,CAAA,GAE/E,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAO,UAAU,KAAK,GAAM,MAC3B,kBAAC,SAAD;SAEE,WAAU;mBAFZ;UAIE,kBAAC,SAAD;WACE,MAAK;WACL,WAAU;WACV,SAAS,EAAK;WACd,UAAU,MAAiB;WAC3B,WAAW,MAAM,GAAoB,GAAG,EAAE,OAAO,QAAQ;WACzD,CAAA;UACF,kBAAC,QAAD;WACE,WAAW,WAAW,EAAK,YAAY,qCAAqC;qBAE3E,EAAK;WACD,CAAA;UACN,MAAiB,IAChB,kBAAC,GAAD,EAAS,WAAU,wDAAyD,CAAA,GAC1E;UACE;WAlBD,EAkBC,CACR;QACE,CAAA,CAEJ,EAAA,CAAA;OACM;QACA;QAEhB,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAW,WAAU;eAAY;KAAoB,CAAA,EAC1C,CAAA,EACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAb,CAAgD,GAAS,IAAK;SAC9D,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAAY,CAAA,CACnD;QACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,OAAO,EAAE,OAAO,GAAG,EAAS,IAAI;MAChC,CAAA;KACE,CAAA,CACM,EAAA,CAAA,CACT,EAAA,CAAA,CACH;;GAEN,kBAAC,IAAD;IAAM,cAAa;cAAnB;KACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MAAa,OAAM;gBAAc;MAAyB,CAAA,EAC1D,kBAAC,GAAD;MAAa,OAAM;gBAAa;MAAwB,CAAA,CAC/C,EAAA,CAAA;KACX,kBAAC,GAAD;MAAa,OAAM;MAAc,WAAU;gBACzC,kBAAC,IAAD;OAAkB,YAAW;OAAkC,UAAU,EAAO;OAAM,CAAA;MAC1E,CAAA;KACd,kBAAC,GAAD;MAAa,OAAM;MAAa,WAAU;gBACxC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,IAAD;QAAe,YAAW;QAAkC,UAAU,EAAO;QAAM,CAAA,EACnF,kBAAC,IAAD,EAAkB,YAAY,EAAO,IAAM,CAAA,CACvC;;MACM,CAAA;KACT;;GAGP,kBAAC,GAAD;IAAkB,MAAM;IAAc,cAAc;cAClD,kBAAC,GAAD,EAAA,UAAA;KACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,wBAA4C,CAAA,EACnE,kBAAC,GAAD,EAAA,UAA6B,6EAEC,CAAA,CACP,EAAA,CAAA;KACzB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,eAAmB,CAAA,EAC1B,kBAAC,GAAD;QACE,MAAK;QACL,KAAI;QACJ,MAAK;QACL,aAAY;QACZ,OAAO,EAAa;QACpB,WAAW,MACT,GAAiB,OAAU;SAAE,GAAG;SAAM,YAAY,EAAE,OAAO;SAAO,EAAE;QAEtE,CAAA,CACE;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,SAAa,CAAA,EACpB,kBAAC,GAAD;QACE,aAAY;QACZ,OAAO,EAAa;QACpB,WAAW,MAAM,GAAiB,OAAU;SAAE,GAAG;SAAM,OAAO,EAAE,OAAO;SAAO,EAAE;QAChF,MAAM;QACN,CAAA,CACE;SACF;;KACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MAAQ,SAAQ;MAAU,eAAe,EAAgB,GAAM;MAAE,UAAU;gBAAY;MAE9E,CAAA,EACT,kBAAC,GAAD;MAAQ,eAjQW;OAC3B,IAAM,IAAa,OAAO,EAAa,WAAW;AAC7C,UAAoB,EACvB,WAAW;QACT,IAAI,EAAO;QACX,OAAO;SACL,YACE,EAAa,WAAW,MAAM,KAAK,MAAM,OAAO,SAAS,EAAW,GAChE,IACA;SACN,OAAO,EAAa,MAAM,MAAM,IAAI;SACrC;QACF,EACF,CAAC;;MAoPuC,UAAU;gBAA3C,CACG,IAAa,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,GAAG,MACjE,IAAa,gBAAgB,uBACvB;QACc,EAAA,CAAA;KACD,EAAA,CAAA;IACT,CAAA;GAGnB,kBAAC,GAAD;IAAkB,MAAM;IAAU,cAAc;cAC9C,kBAAC,GAAD;KAAyB,WAAU;eAAnC;MACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,oBAAwC,CAAA,EAC/D,kBAAC,GAAD,EAAA,UAA6B,sDAEC,CAAA,CACP,EAAA,CAAA;MACxB,IACC,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,SAAa,CAAA,EACpB,kBAAC,GAAD;UAAO,OAAO,EAAS;UAAO,WAAW,MAAM,EAAQ,SAAS,EAAE,OAAO,MAAM;UAAI,CAAA,CAC/E;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,eAAmB,CAAA,EAC1B,kBAAC,GAAD;UACE,OAAO,EAAS;UAChB,WAAW,MAAM,EAAQ,eAAe,EAAE,OAAO,MAAM;UACvD,MAAM;UACN,CAAA,CACE;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,QAAY,CAAA,EACnB,kBAAC,GAAD;YAAQ,OAAO,EAAS;YAAM,gBAAgB,MAAM,EAAQ,QAAQ,EAAE;sBAAtE,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAe,CAAA,EACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,GAAkB,KAAK,MACtB,kBAAC,GAAD;aAAoB,OAAO;uBACxB,EAAS,EAAE;aACD,EAFI,EAEJ,CACb,EACY,CAAA,CACT;cACL;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,YAAgB,CAAA,EACvB,kBAAC,GAAD;YAAQ,OAAO,EAAS;YAAU,gBAAgB,MAAM,EAAQ,YAAY,EAAE;sBAA9E,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAe,CAAA,EACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,GAAW,KAAK,MACf,kBAAC,GAAD;aAAoB,OAAO;uBACxB,EAAS,EAAE;aACD,EAFI,EAEJ,CACb,EACY,CAAA,CACT;cACL;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,UAAc,CAAA,EACrB,kBAAC,GAAD;YAAQ,OAAO,EAAS;YAAQ,gBAAgB,MAAM,EAAQ,UAAU,EAAE;sBAA1E,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAe,CAAA,EACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,GAAqB,KAAK,MACzB,kBAAC,GAAD;aAAoB,OAAO;uBACxB,EAAS,EAAE;aACD,EAFI,EAEJ,CACb,EACY,CAAA,CACT;cACL;;UACF;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,kBAAsB,CAAA,EAC7B,kBAAC,GAAD;YACE,MAAK;YACL,OAAO,EAAS;YAChB,WAAW,MAAM,EAAQ,eAAe,EAAE,OAAO,MAAM;YACvD,CAAA,CACE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,kBAAsB,CAAA,EAC7B,kBAAC,GAAD;YACE,MAAK;YACL,KAAI;YACJ,MAAK;YACL,OAAO,EAAS;YAChB,WAAW,MAAM,EAAQ,iBAAiB,EAAE,OAAO,MAAM;YACzD,CAAA,CACE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,eAAmB,CAAA,EAC1B,kBAAC,GAAD;YACE,MAAK;YACL,KAAI;YACJ,MAAK;YACL,OAAO,EAAS;YAChB,WAAW,MAAM,EAAQ,cAAc,EAAE,OAAO,MAAM;YACtD,CAAA,CACE;;UACF;;QACF;WACJ;MACJ,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAQ,SAAQ;OAAU,eAAe,EAAY,GAAM;OAAE,UAAU;iBAAU;OAExE,CAAA,EACT,kBAAC,GAAD;OAAQ,eAnWO;AACvB,YAAI,CAAC,EAAU;AACf,YAAI,CAAC,EAAS,MAAM,MAAM,EAAE;AAC1B,WAAO,QAAQ,kBAAkB,0CAA0C;AAC3E;;QAEF,IAAM,KAAkB,MAAiC;SACvD,IAAM,IAAU,EAAM,MAAM;AAC5B,aAAI,MAAY,GAAI,QAAO;SAC3B,IAAM,IAAS,OAAO,EAAQ;AAC9B,gBAAO,OAAO,SAAS,EAAO,GAAG,IAAS;;AAEvC,WAAkB,EACrB,WAAW;SACT,IAAI,EAAO;SACX,OAAO;UACL,OAAO,EAAS,MAAM,MAAM;UAC5B,aAAa,EAAS,YAAY,MAAM,IAAI;UAC5C,MAAM,EAAS;UACf,UAAU,EAAS;UACnB,QAAQ,EAAS;UACjB,aAAa,EAAS,cAAc,IAAI,KAAK,EAAS,YAAY,CAAC,aAAa,GAAG;UACnF,eAAe,EAAe,EAAS,cAAc;UACrD,YAAY,EAAe,EAAS,WAAW;UAChD;SACF,EACF,CAAC;;OAyUmC,UAAU;iBAAvC,CACG,IAAW,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,GAAG,MAC/D,IAAW,YAAY,eACjB;SACc,EAAA,CAAA;MACD;;IACT,CAAA;GACf"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProvenancePage.js","names":[],"sources":["../../../src/assethandler/pages/ProvenancePage.tsx"],"sourcesContent":["import { ShieldCheck } from 'lucide-react';\nimport type React from 'react';\nimport {\n Button,\n GradientText,\n IllustratedEmptyState,\n EmphasisPanel,\n PagePurpose,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { Pagination, usePagination } from '../components';\nimport { notify } from '../utils/toast';\n\nconst PROVENANCE_RECORDS_QUERY = gql`\n query AssetHandlerProvenanceRecords {\n provenanceRecords {\n id\n assetId\n hash\n chainNetwork\n txHash\n attestedAt\n attestedBy\n }\n }\n`;\n\nconst RECORD_PROVENANCE_MUTATION = gql`\n mutation AssetHandlerRecordProvenance($input: RecordProvenanceInput!) {\n recordProvenance(input: $input) {\n id\n }\n }\n`;\n\ninterface ProvenanceRecord {\n id: string;\n assetId: string;\n hash: string;\n chainNetwork: string;\n txHash: string | null;\n attestedAt: string;\n attestedBy: string;\n}\n\nconst CHAIN_NETWORKS = [\n 'polygon-amoy',\n 'polygon-mainnet',\n 'ethereum-sepolia',\n 'ethereum-mainnet',\n 'base-sepolia',\n] as const;\n\nexport function ProvenancePage() {\n const assets = useAssetHandlerStore((s) => s.assets);\n const [assetId, setAssetId] = useState('');\n const [chainNetwork, setChainNetwork] =\n useState<(typeof CHAIN_NETWORKS)[number]>('polygon-amoy');\n\n const { data, loading, refetch } = useQuery<{ provenanceRecords: ProvenanceRecord[] }>(\n PROVENANCE_RECORDS_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const [recordProvenance, { loading: recording }] = useMutation(RECORD_PROVENANCE_MUTATION, {\n onCompleted: () => {\n notify.success('Attestation recorded', 'The provenance record has been submitted.');\n void refetch();\n },\n onError: (error) => notify.error('Could not record attestation', error.message),\n });\n\n const records = data?.provenanceRecords ?? [];\n const pagination = usePagination(records, { pageSize: 12 });\n\n const handleSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (!assetId) {\n notify.warning('Asset required', 'Select the asset to attest.');\n return;\n }\n // Content-addressed reference for this attestation snapshot.\n const hash = `${assetId}-${Date.now()}`;\n void recordProvenance({\n variables: {\n input: { assetId, hash, chainNetwork, metadata: { source: 'assethandler-ui' } },\n },\n });\n };\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <ShieldCheck className=\"h-6 w-6\" /> <GradientText>Provenance</GradientText>\n </h1>\n <PagePurpose>\n Prove an asset&apos;s history is tamper-proof. Record cryptographic attestations of key\n lifecycle events to a blockchain network so ownership, custody and condition can be\n independently verified later — essential for high-value, regulated or resold assets.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Record an attestation</h2>\n <form className=\"grid gap-3 md:grid-cols-3\" onSubmit={handleSubmit}>\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.assetTag} — {asset.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Chain Network</Label>\n <Select\n value={chainNetwork}\n onValueChange={(v) => setChainNetwork(v as typeof chainNetwork)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CHAIN_NETWORKS.map((n) => (\n <SelectItem key={n} value={n}>\n {n}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex items-end\">\n <Button className=\"w-full\" disabled={recording} type=\"submit\">\n {recording ? 'Recording…' : 'Record Attestation'}\n </Button>\n </div>\n </form>\n </EmphasisPanel>\n <div className=\"rounded-lg border\">\n <div className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n <span>Asset</span>\n <span>Network</span>\n <span>Hash</span>\n <span>Tx</span>\n <span>Attested At</span>\n </div>\n {loading && records.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && records.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No provenance records yet\"\n description=\"Record an attestation above to start an immutable, verifiable history for your assets.\"\n />\n ) : null}\n {pagination.pageItems.map((record) => (\n <div\n key={record.id}\n className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-sm last:border-b-0\"\n >\n <span>{record.assetId}</span>\n <span>{record.chainNetwork}</span>\n <span className=\"truncate\">{record.hash}</span>\n <span className=\"truncate text-text-secondary\">{record.txHash ?? 'pending'}</span>\n <span className=\"text-text-secondary\">\n {new Date(record.attestedAt).toLocaleString()}\n </span>\n </div>\n ))}\n </div>\n <Pagination\n page={pagination.page}\n pageCount={pagination.pageCount}\n rangeStart={pagination.rangeStart}\n rangeEnd={pagination.rangeEnd}\n totalItems={pagination.totalItems}\n canPrev={pagination.canPrev}\n canNext={pagination.canNext}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"records\"\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProvenancePage.js","names":[],"sources":["../../../src/assethandler/pages/ProvenancePage.tsx"],"sourcesContent":["import { ShieldCheck } from 'lucide-react';\nimport type React from 'react';\nimport {\n Button,\n GradientText,\n IllustratedEmptyState,\n EmphasisPanel,\n PagePurpose,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { Pagination, usePagination } from '../components';\nimport { notify } from '../utils/toast';\n\nconst PROVENANCE_RECORDS_QUERY = gql`\n query AssetHandlerProvenanceRecords {\n provenanceRecords {\n id\n assetId\n hash\n chainNetwork\n txHash\n attestedAt\n attestedBy\n }\n }\n`;\n\nconst RECORD_PROVENANCE_MUTATION = gql`\n mutation AssetHandlerRecordProvenance($input: RecordProvenanceInput!) {\n recordProvenance(input: $input) {\n id\n }\n }\n`;\n\ninterface ProvenanceRecord {\n id: string;\n assetId: string;\n hash: string;\n chainNetwork: string;\n txHash: string | null;\n attestedAt: string;\n attestedBy: string;\n}\n\nconst CHAIN_NETWORKS = [\n 'polygon-amoy',\n 'polygon-mainnet',\n 'ethereum-sepolia',\n 'ethereum-mainnet',\n 'base-sepolia',\n] as const;\n\nexport function ProvenancePage() {\n const assets = useAssetHandlerStore((s) => s.assets);\n const [assetId, setAssetId] = useState('');\n const [chainNetwork, setChainNetwork] = useState<(typeof CHAIN_NETWORKS)[number]>('polygon-amoy');\n\n const { data, loading, refetch } = useQuery<{ provenanceRecords: ProvenanceRecord[] }>(\n PROVENANCE_RECORDS_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const [recordProvenance, { loading: recording }] = useMutation(RECORD_PROVENANCE_MUTATION, {\n onCompleted: () => {\n notify.success('Attestation recorded', 'The provenance record has been submitted.');\n void refetch();\n },\n onError: (error) => notify.error('Could not record attestation', error.message),\n });\n\n const records = data?.provenanceRecords ?? [];\n const pagination = usePagination(records, { pageSize: 12 });\n\n const handleSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (!assetId) {\n notify.warning('Asset required', 'Select the asset to attest.');\n return;\n }\n // Content-addressed reference for this attestation snapshot.\n const hash = `${assetId}-${Date.now()}`;\n void recordProvenance({\n variables: {\n input: { assetId, hash, chainNetwork, metadata: { source: 'assethandler-ui' } },\n },\n });\n };\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <ShieldCheck className=\"h-6 w-6\" /> <GradientText>Provenance</GradientText>\n </h1>\n <PagePurpose>\n Prove an asset&apos;s history is tamper-proof. Record cryptographic attestations of key\n lifecycle events to a blockchain network so ownership, custody and condition can be\n independently verified later — essential for high-value, regulated or resold assets.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Record an attestation</h2>\n <form className=\"grid gap-3 md:grid-cols-3\" onSubmit={handleSubmit}>\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.assetTag} — {asset.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Chain Network</Label>\n <Select\n value={chainNetwork}\n onValueChange={(v) => setChainNetwork(v as typeof chainNetwork)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CHAIN_NETWORKS.map((n) => (\n <SelectItem key={n} value={n}>\n {n}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex items-end\">\n <Button className=\"w-full\" disabled={recording} type=\"submit\">\n {recording ? 'Recording…' : 'Record Attestation'}\n </Button>\n </div>\n </form>\n </EmphasisPanel>\n <div className=\"rounded-lg border\">\n <div className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n <span>Asset</span>\n <span>Network</span>\n <span>Hash</span>\n <span>Tx</span>\n <span>Attested At</span>\n </div>\n {loading && records.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && records.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No provenance records yet\"\n description=\"Record an attestation above to start an immutable, verifiable history for your assets.\"\n />\n ) : null}\n {pagination.pageItems.map((record) => (\n <div\n key={record.id}\n className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-sm last:border-b-0\"\n >\n <span>{record.assetId}</span>\n <span>{record.chainNetwork}</span>\n <span className=\"truncate\">{record.hash}</span>\n <span className=\"truncate text-text-secondary\">{record.txHash ?? 'pending'}</span>\n <span className=\"text-text-secondary\">\n {new Date(record.attestedAt).toLocaleString()}\n </span>\n </div>\n ))}\n </div>\n <Pagination\n page={pagination.page}\n pageCount={pagination.pageCount}\n rangeStart={pagination.rangeStart}\n rangeEnd={pagination.rangeEnd}\n totalItems={pagination.totalItems}\n canPrev={pagination.canPrev}\n canNext={pagination.canNext}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"records\"\n />\n </div>\n );\n}\n"],"mappings":"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"}