refactor: optimised UI and UX
- add tag style to status; - add button style to action links of each domain; - add Notiflix for better notification experiences
This commit is contained in:
9 files changed
+181
-49
No files matched your search
@@ -1,37 +1,44 @@
|
||||
"use client"
|
||||
|
||||
import { useActionState } from "react"
|
||||
import { useActionState, useEffect } from "react"
|
||||
import type { ActionState } from "@/lib/action-state"
|
||||
import { notifyFailure, notifySuccess, notifyWarning } from "@/lib/notify"
|
||||
|
||||
export interface ApplyButtonProps {
|
||||
action: (prev: ActionState, formData: FormData) => Promise<ActionState>
|
||||
}
|
||||
|
||||
// Result messages now arrive as toasts instead of text under the button, so the card never
|
||||
// grows and nothing has to be dismissed by hand.
|
||||
const INITIAL: ActionState = { ok: false }
|
||||
|
||||
export function ApplyButton({ action }: ApplyButtonProps) {
|
||||
const [state, formAction, pending] = useActionState<ActionState, FormData>(action, { ok: false })
|
||||
const [state, formAction, pending] = useActionState<ActionState, FormData>(action, INITIAL)
|
||||
|
||||
// Every run of the action returns a fresh object, so this fires exactly once per
|
||||
// submission — and stays quiet on mount, where `state` carries no message yet.
|
||||
useEffect(() => {
|
||||
if (!state.message) return
|
||||
|
||||
// A success that also carries `detail` means the config was written but the restart was
|
||||
// skipped (see applyConfigAction) — that deserves a warning, not a green tick. When the
|
||||
// restart failed outright, the detail is the reason, so it joins the message.
|
||||
if (state.ok) {
|
||||
if (state.detail) notifyWarning(`${state.message}(${state.detail})`)
|
||||
else notifySuccess(state.message)
|
||||
return
|
||||
}
|
||||
notifyFailure(state.detail ? `${state.message}:${state.detail}` : state.message)
|
||||
}, [state])
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<form action={formAction}>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={pending}
|
||||
className="rounded-md bg-zinc-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-zinc-700 disabled:opacity-50 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-white">
|
||||
{pending ? "应用中…" : "应用并重启 dnsmasq"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{state.message ? (
|
||||
<div className="space-y-1 text-sm">
|
||||
<p
|
||||
className={state.ok ? "text-emerald-700 dark:text-emerald-400" : "text-red-600 dark:text-red-400"}>
|
||||
{state.message}
|
||||
</p>
|
||||
{state.detail ? (
|
||||
<p className="text-xs text-zinc-500 dark:text-zinc-400">{state.detail}</p>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<form action={formAction}>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={pending}
|
||||
className="rounded-md bg-zinc-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-zinc-700 disabled:opacity-50 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-white">
|
||||
{pending ? "应用中…" : "应用并重启 dnsmasq"}
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user