Skip to content
File

Blob: src/client/islands/repo-admin/CompactionCard.tsx

typescript169 lines
1import { Check, Clock, Download, Info, Play, Search, Trash2 } from "lucide-react";
2import { Button } from "@/client/components/ui/button";
3import { Card } from "@/client/components/ui/card";
4import { JsonResult } from "./JsonResult";
5import type { AdminState, CompactionData } from "./types";
6 
7export type CompactionCardProps = {
8 compactionData?: CompactionData;
9 compactionStartedAt?: string | null;
10 compactionStatus: string;
11 state: AdminState;
12 pending: Record<string, boolean>;
13 startCompaction: (dryRun: boolean) => Promise<void>;
14 clearCompaction: () => Promise<void>;
15 compactionResult: unknown;
16};
17 
18export function CompactionCard({
19 compactionData,
20 compactionStartedAt,
21 compactionStatus,
22 state,
23 pending,
24 startCompaction,
25 clearCompaction,
26 compactionResult,
27}: CompactionCardProps) {
28 const receiveStartedAt = state.receiveLease
29 ? new Date(state.receiveLease.createdAt).toLocaleString()
30 : null;
31 const queuedCompactionAt = compactionData?.wantedAt
32 ? new Date(compactionData.wantedAt).toLocaleString()
33 : null;
34 
35 return (
36 <Card>
37 <h2 className="mb-4 text-xl font-semibold">Pack Compaction</h2>
38 <p className="mb-4 text-zinc-600 dark:text-zinc-400">
39 Receive and compaction leases are the live background activity signals for this repository.
40 Automatic compaction runs after packs accumulate past the compaction threshold.
41 </p>
42
43 {state.receiveLease ? (
44 <div className="mb-4 rounded-xl bg-amber-50 p-3 dark:bg-amber-900/20">
45 <div className="text-sm">
46 <strong>
47 <Download
48 className="mr-1 inline h-4 w-4 align-[-2px] text-amber-600 dark:text-amber-400"
49 aria-hidden="true"
50 />
51 Receiving:
52 </strong>{" "}
53 receive lease is active
54 {receiveStartedAt ? ` • Started ${receiveStartedAt}` : ""}
55 </div>
56 </div>
57 ) : compactionData?.running ? (
58 <div className="mb-4 rounded-xl bg-accent-50 p-3 dark:bg-accent-900/20">
59 <div className="text-sm">
60 <strong>
61 <Info
62 className="mr-1 inline h-4 w-4 align-[-2px] text-accent-600 dark:text-accent-400"
63 aria-hidden="true"
64 />
65 Running:
66 </strong>{" "}
67 compaction lease is active
68 {compactionStartedAt ? ` • Started ${compactionStartedAt}` : ""}
69 </div>
70 </div>
71 ) : compactionData?.queued ? (
72 <div className="mb-4 rounded-xl bg-accent-50 p-3 text-sm dark:bg-accent-900/20">
73 <strong>
74 <Clock
75 className="mr-1 inline h-4 w-4 align-[-2px] text-accent-600 dark:text-accent-400"
76 aria-hidden="true"
77 />
78 Queued:
79 </strong>{" "}
80 compaction request is recorded in repo metadata
81 </div>
82 ) : (
83 <div className="mb-4 rounded-xl bg-green-50 p-3 text-sm dark:bg-green-900/20">
84 <strong>
85 <Check
86 className="mr-1 inline h-4 w-4 align-[-2px] text-green-600 dark:text-green-400"
87 aria-hidden="true"
88 />
89 Idle:
90 </strong>{" "}
91 no compaction lease is currently active
92 </div>
93 )}
94
95 <div className="grid grid-cols-1 gap-3 text-sm md:grid-cols-2">
96 <div className="rounded-xl border border-zinc-200 p-3 dark:border-zinc-800">
97 <div className="mb-1 text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400">
98 Receive Lease
99 </div>
100 <div className="font-mono">
101 {state.receiveLease
102 ? `active until ${new Date(state.receiveLease.expiresAt).toLocaleString()}`
103 : "idle"}
104 </div>
105 </div>
106 <div className="rounded-xl border border-zinc-200 p-3 dark:border-zinc-800">
107 <div className="mb-1 text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400">
108 Compaction
109 </div>
110 <div className="font-mono">{compactionStatus}</div>
111 {compactionData?.wantedAt ? (
112 <div className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
113 requested {new Date(compactionData.wantedAt).toLocaleString()}
114 </div>
115 ) : null}
116 </div>
117 </div>
118
119 <div className="mt-4 space-y-4">
120 <div className="rounded-xl border border-zinc-200 bg-zinc-50 p-4 text-sm text-zinc-600 dark:border-zinc-800 dark:bg-zinc-900/40 dark:text-zinc-300">
121 Compaction requests queue real background work. The active pack catalog and compaction
122 lease are the source of truth for repository state.
123 </div>
124 <div className="flex flex-wrap gap-3">
125 <Button
126 type="button"
127 onClick={() => void startCompaction(true)}
128 disabled={pending["compaction-dry-run"]}
129 >
130 <Search className="mr-2 inline h-4 w-4 align-[-2px]" aria-hidden="true" />
131 <span className="label">
132 {pending["compaction-dry-run"] ? "Processing..." : "Preview Compaction"}
133 </span>
134 </Button>
135 <Button
136 type="button"
137 onClick={() => void startCompaction(false)}
138 disabled={pending["compaction-start"]}
139 >
140 <Play className="mr-2 inline h-4 w-4 align-[-2px]" aria-hidden="true" />
141 <span className="label">
142 {pending["compaction-start"] ? "Processing..." : "Request Compaction"}
143 </span>
144 </Button>
145 <Button
146 variant="secondary"
147 type="button"
148 onClick={() => void clearCompaction()}
149 disabled={
150 pending["compaction-clear"] || (!compactionData?.queued && !compactionData?.running)
151 }
152 >
153 <Trash2 className="mr-2 inline h-4 w-4 align-[-2px]" aria-hidden="true" />
154 <span className="label">
155 {pending["compaction-clear"] ? "Clearing..." : "Clear Compaction Request"}
156 </span>
157 </Button>
158 </div>
159 {queuedCompactionAt ? (
160 <div className="text-xs text-zinc-500 dark:text-zinc-400">
161 Latest request recorded at {queuedCompactionAt}.
162 </div>
163 ) : null}
164 {compactionResult ? <JsonResult data={compactionResult} /> : null}
165 </div>
166 </Card>
167 );
168}