CI / ci (push) Canceled after 0s
The session built: Appointments page: a single list, newest first. Block Time: edit and delete. Customer CSV export: with barber notes excluded. Guest-link regression test: a test that an appointment id alone is rejected, plus fixes to the stale docs. It built the scheduler, job runner with live progress, backup and restore, and the health alerts. Its own report says a real delete-and-restore on throwaway Docker passed, but I haven’t repeated that. It also says real SMTP and large dumps were not exercised. The manager image needs rebuilding for the backups to work, because it now installs 7zip.
69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useActionState, useState } from "react";
|
|
import { JobProgress } from "../../job-progress";
|
|
import { PassphraseNotice } from "../../passphrase-notice";
|
|
import { deleteBusinessAction, type DeleteBusinessState } from "./actions";
|
|
|
|
const initialState: DeleteBusinessState = { error: null, jobId: null, newPassphrase: null };
|
|
|
|
export function DeleteBusinessForm({ slug }: { slug: string }) {
|
|
const boundAction = deleteBusinessAction.bind(null, slug);
|
|
const [state, formAction, isPending] = useActionState(boundAction, initialState);
|
|
const [confirmation, setConfirmation] = useState("");
|
|
const canDelete = confirmation === slug;
|
|
|
|
if (state.jobId) {
|
|
return (
|
|
<div className="space-y-4">
|
|
{state.newPassphrase && <PassphraseNotice passphrase={state.newPassphrase} />}
|
|
<JobProgress jobId={state.jobId} refreshOnFinish={false}>
|
|
<Link href="/dashboard" className="inline-block rounded-md bg-ink px-4 py-2 text-sm font-semibold text-white">
|
|
Back to businesses
|
|
</Link>
|
|
</JobProgress>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<form action={formAction} className="space-y-4">
|
|
<label className="flex items-start gap-2 text-sm">
|
|
<input type="checkbox" name="finalBackup" defaultChecked className="mt-0.5" />
|
|
<span>
|
|
Take a final backup first <span className="text-muted">(recommended — kept on this server after the business is gone; deleting stops if the backup fails)</span>
|
|
</span>
|
|
</label>
|
|
<div>
|
|
<label htmlFor="confirmation" className="block text-sm font-medium text-muted">
|
|
Type <span className="font-mono font-semibold text-ink">{slug}</span> to confirm
|
|
</label>
|
|
<input
|
|
id="confirmation"
|
|
name="confirmation"
|
|
type="text"
|
|
autoComplete="off"
|
|
autoCorrect="off"
|
|
spellCheck={false}
|
|
value={confirmation}
|
|
onChange={(e) => setConfirmation(e.target.value)}
|
|
className="mt-1 w-full rounded-md border border-line px-3 py-2 font-mono text-sm"
|
|
/>
|
|
</div>
|
|
{state.error && (
|
|
<p role="alert" className="text-sm font-medium text-danger">
|
|
{state.error}
|
|
</p>
|
|
)}
|
|
<button
|
|
type="submit"
|
|
disabled={!canDelete || isPending}
|
|
className="w-full rounded-md bg-danger py-2 text-sm font-semibold text-white disabled:opacity-40"
|
|
>
|
|
{isPending ? "Starting…" : "Delete Business"}
|
|
</button>
|
|
</form>
|
|
);
|
|
}
|