|
1 | 1 | "use client"; |
2 | 2 |
|
3 | 3 | import { Button } from "@salt-ds/core"; |
4 | | -import { type LocalActionRecord } from "@/lib/actionLog"; |
| 4 | +import { |
| 5 | + ACTION_TRAIL_STORAGE_KEY, |
| 6 | + isLocalActionRecordArray, |
| 7 | + type LocalActionRecord, |
| 8 | +} from "@/lib/actionLog"; |
5 | 9 | import { useEffect, useMemo, useState } from "react"; |
6 | 10 | import { |
7 | 11 | candidatesByExceptionId as fallbackCandidatesByExceptionId, |
@@ -874,15 +878,32 @@ function buildActionPreview({ |
874 | 878 | } |
875 | 879 |
|
876 | 880 |
|
877 | | -function LocalActionTrail({ records }: { records: LocalActionRecord[] }) { |
| 881 | +function LocalActionTrail({ |
| 882 | + records, |
| 883 | + onClear, |
| 884 | +}: { |
| 885 | + records: LocalActionRecord[]; |
| 886 | + onClear: () => void; |
| 887 | +}) { |
878 | 888 | return ( |
879 | 889 | <div className="mt-4 rounded-2xl border border-slate-200 bg-white p-3"> |
880 | 890 | <div className="flex items-center justify-between gap-3"> |
881 | 891 | <div className="text-xs font-bold uppercase tracking-[0.16em] text-slate-500"> |
882 | 892 | Staged action history |
883 | 893 | </div> |
884 | | - <div className="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-bold text-slate-600"> |
885 | | - {records.length} staged |
| 894 | + <div className="flex items-center gap-2"> |
| 895 | + <div className="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-bold text-slate-600"> |
| 896 | + {records.length} staged |
| 897 | + </div> |
| 898 | + {records.length > 0 ? ( |
| 899 | + <button |
| 900 | + type="button" |
| 901 | + onClick={onClear} |
| 902 | + className="rounded-full border border-slate-200 bg-white px-2.5 py-1 text-xs font-bold text-slate-600 hover:bg-slate-50" |
| 903 | + > |
| 904 | + Clear |
| 905 | + </button> |
| 906 | + ) : null} |
886 | 907 | </div> |
887 | 908 | </div> |
888 | 909 |
|
@@ -935,6 +956,7 @@ export default function BreakResolutionWorkbench() { |
935 | 956 | const [hideStaged, setHideStaged] = useState(false); |
936 | 957 | const [stagedExceptionIds, setStagedExceptionIds] = useState<string[]>([]); |
937 | 958 | const [actionTrail, setActionTrail] = useState<LocalActionRecord[]>([]); |
| 959 | + const [hasLoadedActionTrail, setHasLoadedActionTrail] = useState(false); |
938 | 960 | const [queueFilter, setQueueFilter] = useState<QueueFilter>("All"); |
939 | 961 | const [dataSource, setDataSource] = useState("static fallback"); |
940 | 962 | const [isLoadingData, setIsLoadingData] = useState(true); |
@@ -977,6 +999,41 @@ export default function BreakResolutionWorkbench() { |
977 | 999 | void loadWorkbenchData(); |
978 | 1000 | }, []); |
979 | 1001 |
|
| 1002 | + useEffect(() => { |
| 1003 | + try { |
| 1004 | + const storedActionTrail = window.localStorage.getItem(ACTION_TRAIL_STORAGE_KEY); |
| 1005 | + |
| 1006 | + if (!storedActionTrail) { |
| 1007 | + return; |
| 1008 | + } |
| 1009 | + |
| 1010 | + const parsedActionTrail = JSON.parse(storedActionTrail) as unknown; |
| 1011 | + |
| 1012 | + if (isLocalActionRecordArray(parsedActionTrail)) { |
| 1013 | + setActionTrail(parsedActionTrail); |
| 1014 | + setStagedExceptionIds( |
| 1015 | + Array.from(new Set(parsedActionTrail.map((record) => record.exceptionId))), |
| 1016 | + ); |
| 1017 | + } |
| 1018 | + } catch { |
| 1019 | + setActionTrail([]); |
| 1020 | + setStagedExceptionIds([]); |
| 1021 | + } finally { |
| 1022 | + setHasLoadedActionTrail(true); |
| 1023 | + } |
| 1024 | + }, []); |
| 1025 | + |
| 1026 | + useEffect(() => { |
| 1027 | + if (!hasLoadedActionTrail) { |
| 1028 | + return; |
| 1029 | + } |
| 1030 | + |
| 1031 | + window.localStorage.setItem( |
| 1032 | + ACTION_TRAIL_STORAGE_KEY, |
| 1033 | + JSON.stringify(actionTrail), |
| 1034 | + ); |
| 1035 | + }, [actionTrail, hasLoadedActionTrail]); |
| 1036 | + |
980 | 1037 | const filteredPriorityQueue = useMemo( |
981 | 1038 | () => |
982 | 1039 | filterPriorityQueue( |
@@ -1596,7 +1653,17 @@ export default function BreakResolutionWorkbench() { |
1596 | 1653 | </div> |
1597 | 1654 |
|
1598 | 1655 |
|
1599 | | - <LocalActionTrail records={currentBreakActionTrail} /> |
| 1656 | + <LocalActionTrail |
| 1657 | + records={currentBreakActionTrail} |
| 1658 | + onClear={() => { |
| 1659 | + setActionTrail((current) => |
| 1660 | + current.filter((record) => record.exceptionId !== selectedBreak.exceptionId), |
| 1661 | + ); |
| 1662 | + setStagedExceptionIds((current) => |
| 1663 | + current.filter((exceptionId) => exceptionId !== selectedBreak.exceptionId), |
| 1664 | + ); |
| 1665 | + }} |
| 1666 | + /> |
1600 | 1667 | </aside> |
1601 | 1668 | </section> |
1602 | 1669 |
|
|
0 commit comments