feat(admin): delete button on request link
The request-links panel now has a trash button at the far right of each row. Click it, the link is gone — cascadeDelete on intake_link_submissions.link removes the daily counters in the same transaction, and revalidatePath re-renders the panel without the row. Hard delete, no confirmation: matches the existing Pause/Resume pattern (no dialog there either). The icon is a Trash2 from lucide-react; the title attribute is the only label, by design owner-only surface. Phase 9 shape: action takes a scope so revalidation hits the client admin page the panel is mounted on.
This commit is contained in:
parent
3a721df094
commit
6cc7ae01f7
2 changed files with 36 additions and 1 deletions
|
|
@ -114,6 +114,22 @@ export async function setIntakeLinkNotesAction(input: {
|
|||
revalidatePath(adminPathForScope(input.scope));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard-delete a request link. Cascades to `intake_link_submissions` rows
|
||||
* (those use `cascadeDelete: true` on their `link` field, so they go
|
||||
* with it). The token stops working immediately because there's no row
|
||||
* left to resolve against.
|
||||
*/
|
||||
export async function deleteIntakeLinkAction(input: {
|
||||
linkId: string;
|
||||
scope: IntakeLinkScope;
|
||||
}): Promise<void> {
|
||||
await requireOwner();
|
||||
const pb = await getAdminPb();
|
||||
await pb.collection("intake_links").delete(input.linkId);
|
||||
revalidatePath(adminPathForScope(input.scope));
|
||||
}
|
||||
|
||||
function intakeUrlFor(token: string): string {
|
||||
const base = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
|
||||
return `${base}/intake/${token}`;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import { Copy, RefreshCw, Power, PowerOff, Plus } from "lucide-react";
|
||||
import { Copy, RefreshCw, Power, PowerOff, Plus, Trash2 } from "lucide-react";
|
||||
|
||||
import {
|
||||
createIntakeLinkAction,
|
||||
deleteIntakeLinkAction,
|
||||
rotateIntakeTokenAction,
|
||||
setIntakeLinkActiveAction,
|
||||
setIntakeLinkNotesAction,
|
||||
|
|
@ -100,6 +101,15 @@ export function IntakeLinksPanel({
|
|||
});
|
||||
}
|
||||
|
||||
function remove(link: IntakeLinkRecord) {
|
||||
startTransition(async () => {
|
||||
await deleteIntakeLinkAction({
|
||||
linkId: link.id,
|
||||
scope,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const field =
|
||||
"w-full rounded-lg border border-border-subtle bg-surface px-3 py-2 text-xs text-text-secondary";
|
||||
|
||||
|
|
@ -220,6 +230,15 @@ export function IntakeLinksPanel({
|
|||
{link.active ? <PowerOff size={12} strokeWidth={2} /> : <Power size={12} strokeWidth={2} />}
|
||||
{link.active ? "Revoke" : "Re-activate"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={pending}
|
||||
onClick={() => remove(link)}
|
||||
className="inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-2 py-1 text-xs text-text-secondary hover:bg-surface-raised disabled:opacity-50 ml-auto"
|
||||
title="Delete this link permanently"
|
||||
>
|
||||
<Trash2 size={12} strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<NotesEditor link={link} onSave={saveNotes} />
|
||||
|
|
|
|||
Loading…
Reference in a new issue