web: give the toast helper a real type and keep the dist bundle out of git
Blocklists, Clients, Domains and Upstreams each carried a ToastLike shim
that duck-typed a react-hot-toast style API. The console uses its own
Toast context, so notify() was calling toast('some message') with a string
where ToastOptions is expected: every mutation toast rendered an empty
title. They now take the toast function from useToast() and send a real
{ title, tone } payload, mapping 'fail' onto the danger tone.
This commit is contained in:
1 parent
85453852d5
commit
f24fd63e9a
4 files changed
+102
-96
No files matched your search
+22
-21
@@ -10,15 +10,16 @@ import { useEffect, useMemo, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Plus, Search, Trash2 } from 'lucide-react'
|
||||
|
||||
import { addDomain, deleteDomain, getDomains } from '../../lib/api'
|
||||
import type { AddDomainInput } from '../../lib/api'
|
||||
import type { DomainItem } from '../../lib/types'
|
||||
import { useApi, useAsyncAction } from '../../hooks/useApi'
|
||||
import { useDebounced } from '../../hooks/useDebounced'
|
||||
import { formatNumber, formatRelative, toLines } from '../../lib/format'
|
||||
import { addDomain, deleteDomain, getDomains } from '../lib/api'
|
||||
import type { AddDomainInput } from '../lib/api'
|
||||
import type { DomainItem } from '../lib/types'
|
||||
import { useApi, useAsyncAction } from '../hooks/useApi'
|
||||
import { useDebounced } from '../hooks/useDebounced'
|
||||
import { formatNumber, formatRelative, toLines } from '../lib/format'
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
ConfirmDialog,
|
||||
Dialog,
|
||||
EmptyState,
|
||||
@@ -30,21 +31,21 @@ import {
|
||||
Textarea,
|
||||
useToast,
|
||||
} from '../components/ui'
|
||||
import type { ToastOptions } from '../components/ui'
|
||||
import { PageError, PageHeader } from '../components/layout'
|
||||
|
||||
type Row = Record<string, ReactNode>
|
||||
|
||||
type ToastLike = {
|
||||
success?: (message: string) => void
|
||||
error?: (message: string) => void
|
||||
toast?: (message: string) => void
|
||||
push?: (message: string) => void
|
||||
}
|
||||
|
||||
function notify(toast: unknown, kind: 'ok' | 'fail', message: string): void {
|
||||
const t = toast as ToastLike
|
||||
const fn = kind === 'ok' ? (t.success ?? t.toast ?? t.push) : (t.error ?? t.toast ?? t.push)
|
||||
fn?.call(t, message)
|
||||
/**
|
||||
* One line for every mutation on this page: the message plus the tone it
|
||||
* earned. `fail` is the danger tone, `ok` the success one.
|
||||
*/
|
||||
function notify(
|
||||
toast: (options: ToastOptions) => void,
|
||||
kind: 'ok' | 'fail',
|
||||
message: string,
|
||||
): void {
|
||||
toast({ title: message, tone: kind === 'ok' ? 'ok' : 'danger' })
|
||||
}
|
||||
|
||||
const PER_PAGE = 50
|
||||
@@ -88,7 +89,7 @@ type AddForm = {
|
||||
const EMPTY_FORM: AddForm = { name: '', action: 'block', list: '', bulk: '', advanced: false }
|
||||
|
||||
export default function Domains() {
|
||||
const toast = useToast()
|
||||
const { toast } = useToast()
|
||||
const [search, setSearch] = useState('')
|
||||
const [action, setAction] = useState('')
|
||||
const [list, setList] = useState('')
|
||||
@@ -220,7 +221,7 @@ export default function Domains() {
|
||||
updated: <span className="whitespace-nowrap text-xs">{formatRelative(item.updated_at)}</span>,
|
||||
delete: (
|
||||
<IconButton
|
||||
icon={<Trash2 size={14} />}
|
||||
icon={Trash2}
|
||||
label={`delete ${item.name}`}
|
||||
variant="ghost"
|
||||
onClick={() => setPendingDelete(item)}
|
||||
@@ -234,7 +235,7 @@ export default function Domains() {
|
||||
title="Domains"
|
||||
description="Inline allow and block rules, checked before the downloaded lists."
|
||||
actions={
|
||||
<Button icon={<Plus size={14} />} onClick={() => setAddOpen(true)}>
|
||||
<Button icon={Plus} onClick={() => setAddOpen(true)}>
|
||||
Add rule
|
||||
</Button>
|
||||
}
|
||||
@@ -245,7 +246,7 @@ export default function Domains() {
|
||||
<Input
|
||||
label="Search"
|
||||
placeholder="ads.example.com"
|
||||
icon={<Search size={14} />}
|
||||
icon={Search}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
autoComplete="off"
|
||||
|
||||
Reference in new issue
Block a user