mirror of
https://github.com/Nezumi-2711/uptime-monitoring.git
synced 2026-09-22 13:48:31 +00:00
fix: update layout for notification channel dialog
This commit is contained in:
@@ -68,7 +68,7 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open onOpenChange={(open) => !open && !mutation.isPending && onClose()}>
|
<Dialog open onOpenChange={(open) => !open && !mutation.isPending && onClose()}>
|
||||||
<DialogContent className="flex flex-col gap-0 w-[calc(100vw-20px)] sm:w-[min(var(--spacing-dialog-channel),calc(100vw-32px))] max-w-dialog-channel max-h-[calc(100dvh-20px)] sm:max-h-[min(90dvh,760px)] overflow-hidden p-0 rounded-card sm:rounded-xl shadow-[0_24px_70px_rgb(22_62_45/0.15),0_4px_16px_rgb(0_0_0/0.07)] dark:border dark:border-white/10 dark:bg-night-card dark:shadow-[0_24px_64px_-12px_rgba(0,0,0,0.85),0_0_0_1px_rgba(255,255,255,0.06)] *:data-[slot=dialog-close]:top-4 sm:*:data-[slot=dialog-close]:top-4.75 *:data-[slot=dialog-close]:right-3.5 sm:*:data-[slot=dialog-close]:right-5">
|
<DialogContent className="flex flex-col gap-0 w-[calc(100vw-20px)] sm:w-[min(var(--spacing-dialog-channel),calc(100vw-32px))] sm:max-w-dialog-channel max-h-[calc(100dvh-20px)] sm:max-h-[min(90dvh,760px)] overflow-hidden p-0 rounded-card sm:rounded-xl shadow-[0_24px_70px_rgb(22_62_45/0.15),0_4px_16px_rgb(0_0_0/0.07)] dark:border dark:border-white/10 dark:bg-night-card dark:shadow-[0_24px_64px_-12px_rgba(0,0,0,0.85),0_0_0_1px_rgba(255,255,255,0.06)] *:data-[slot=dialog-close]:top-4 sm:*:data-[slot=dialog-close]:top-4.75 *:data-[slot=dialog-close]:right-3.5 sm:*:data-[slot=dialog-close]:right-5">
|
||||||
<DialogHeader className="relative shrink-0 gap-1.25 pt-5.25 pr-13.5 pb-4.5 pl-5 sm:pt-6 sm:pr-16 sm:pb-5 sm:pl-panel-x border-b border-border-dialog-header bg-linear-to-b from-white to-surface-dialog-header dark:border-b-white/8 dark:bg-night-subtle [&_.overline]:mb-1 **:data-[slot=dialog-title]:text-xl **:data-[slot=dialog-title]:leading-tight **:data-[slot=dialog-title]:tracking-dialog-title **:data-[slot=dialog-description]:text-caption **:data-[slot=dialog-description]:leading-normal">
|
<DialogHeader className="relative shrink-0 gap-1.25 pt-5.25 pr-13.5 pb-4.5 pl-5 sm:pt-6 sm:pr-16 sm:pb-5 sm:pl-panel-x border-b border-border-dialog-header bg-linear-to-b from-white to-surface-dialog-header dark:border-b-white/8 dark:bg-night-subtle [&_.overline]:mb-1 **:data-[slot=dialog-title]:text-xl **:data-[slot=dialog-title]:leading-tight **:data-[slot=dialog-title]:tracking-dialog-title **:data-[slot=dialog-description]:text-caption **:data-[slot=dialog-description]:leading-normal">
|
||||||
<p className="overline">Alert destination</p>
|
<p className="overline">Alert destination</p>
|
||||||
<DialogTitle>{editing ? `Edit ${editing.name}` : 'Add notification channel'}</DialogTitle>
|
<DialogTitle>{editing ? `Edit ${editing.name}` : 'Add notification channel'}</DialogTitle>
|
||||||
@@ -78,7 +78,7 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
className="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-4.25 min-h-0 m-0 pt-4.5 px-5 sm:pt-5 sm:px-panel-x pb-0 overflow-y-auto overscroll-contain dialog-scrollbar [&_.field]:gap-1.5 [&_.field]:min-w-0"
|
className="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-4.25 min-h-0 m-0 pt-4.5 px-5 sm:pt-5 sm:px-panel-x pb-0 overflow-y-auto overscroll-contain dialog-scrollbar [&_.field]:gap-1.5 [&_.field]:min-w-0"
|
||||||
onSubmit={submit}
|
onSubmit={submit}
|
||||||
>
|
>
|
||||||
<label className="field" htmlFor="channel-name">
|
<label className="field col-span-1" htmlFor="channel-name">
|
||||||
<span>Name</span>
|
<span>Name</span>
|
||||||
<Input
|
<Input
|
||||||
id="channel-name"
|
id="channel-name"
|
||||||
@@ -89,7 +89,7 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div className="field">
|
<div className="field col-span-1">
|
||||||
<span id="channel-type-label">Provider</span>
|
<span id="channel-type-label">Provider</span>
|
||||||
<Select value={form.type} onValueChange={(type) => setForm({ ...form, type: type as ChannelType, url: '', botToken: '' })}>
|
<Select value={form.type} onValueChange={(type) => setForm({ ...form, type: type as ChannelType, url: '', botToken: '' })}>
|
||||||
<SelectTrigger aria-labelledby="channel-type-label">
|
<SelectTrigger aria-labelledby="channel-type-label">
|
||||||
@@ -105,7 +105,7 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
</div>
|
</div>
|
||||||
{form.type === 'telegram' ? (
|
{form.type === 'telegram' ? (
|
||||||
<>
|
<>
|
||||||
<label className="field" htmlFor="channel-token">
|
<label className="field col-span-1" htmlFor="channel-token">
|
||||||
<span>Bot token</span>
|
<span>Bot token</span>
|
||||||
<Input
|
<Input
|
||||||
id="channel-token"
|
id="channel-token"
|
||||||
@@ -117,7 +117,7 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
required={!editing || editing.type !== form.type}
|
required={!editing || editing.type !== form.type}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="field" htmlFor="channel-chat-id">
|
<label className="field col-span-1" htmlFor="channel-chat-id">
|
||||||
<span>Chat ID</span>
|
<span>Chat ID</span>
|
||||||
<Input
|
<Input
|
||||||
id="channel-chat-id"
|
id="channel-chat-id"
|
||||||
@@ -129,7 +129,7 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
</label>
|
</label>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<label className="field sm:col-span-2" htmlFor="channel-url">
|
<label className="field col-span-1 sm:col-span-2" htmlFor="channel-url">
|
||||||
<span>{form.type === 'webhook' ? 'Webhook URL' : `${form.type === 'slack' ? 'Slack' : 'Discord'} webhook URL`}</span>
|
<span>{form.type === 'webhook' ? 'Webhook URL' : `${form.type === 'slack' ? 'Slack' : 'Discord'} webhook URL`}</span>
|
||||||
<Input
|
<Input
|
||||||
id="channel-url"
|
id="channel-url"
|
||||||
@@ -141,22 +141,22 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
<fieldset className="sm:col-span-2 block min-w-0 m-0 p-0 border-0 pt-4.5 border-t border-border-section dark:border-t-white/8 [&>legend]:float-left [&>legend]:mb-1.75 [&>legend]:text-caption [&>legend]:font-medium [&>legend]:text-ink-label dark:[&>legend]:text-night-body">
|
<fieldset className="col-span-1 sm:col-span-2 min-w-0 m-0 p-0 border-0 pt-4.5 border-t border-border-section dark:border-t-white/8">
|
||||||
<legend>Services</legend>
|
<legend className="block mb-1.75 text-caption font-medium text-ink-label dark:text-night-body">Services</legend>
|
||||||
<DropdownMenuPrimitive.Root>
|
<DropdownMenuPrimitive.Root>
|
||||||
<DropdownMenuPrimitive.Trigger asChild>
|
<DropdownMenuPrimitive.Trigger asChild>
|
||||||
<button
|
<button
|
||||||
className="group clear-both flex w-full min-h-10.5 items-center justify-between gap-4 px-3 py-2 rounded-md border border-border-subtle bg-white text-sm text-left text-ink shadow-[inset_0_1px_2px_rgb(0_0_0/0.025)] cursor-pointer transition-[border-color,box-shadow] duration-120 hover:border-border-input-hover focus-visible:outline-none focus-visible:border-primary-deep focus-visible:shadow-[0_0_0_3px_rgb(36_180_126/0.14)] data-[state=open]:border-primary-deep data-[state=open]:shadow-[0_0_0_3px_rgb(36_180_126/0.14)] disabled:cursor-not-allowed disabled:opacity-55 dark:border-white/12 dark:bg-night-input dark:text-gray-50 dark:shadow-[inset_0_1px_2px_rgba(0,0,0,0.3)] dark:hover:border-white/22 dark:focus-visible:border-primary-deep dark:focus-visible:shadow-[0_0_0_3px_rgba(62,207,142,0.2)] dark:data-[state=open]:border-primary-deep dark:data-[state=open]:shadow-[0_0_0_3px_rgba(62,207,142,0.2)]"
|
className="group flex w-full min-h-10.5 items-center justify-between gap-4 px-3 py-2 rounded-sm border border-border-control bg-white text-body text-left text-ink shadow-[inset_0_1px_2px_rgb(0_0_0/0.025)] cursor-pointer transition-colors duration-120 hover:border-[#aaa] focus-visible:outline-none focus-visible:border-primary-deep focus-visible:shadow-[0_0_0_3px_rgb(36_180_126/0.14)] data-[state=open]:border-primary-deep data-[state=open]:shadow-[0_0_0_3px_rgb(36_180_126/0.14)] disabled:cursor-not-allowed disabled:opacity-50 dark:border-white/12 dark:bg-night-input dark:text-neutral-100 dark:shadow-[inset_0_1px_2px_rgba(0,0,0,0.3)] dark:hover:border-white/22 dark:focus-visible:border-primary-deep dark:focus-visible:shadow-brand-dot-dark dark:data-[state=open]:border-primary-deep dark:data-[state=open]:shadow-brand-dot-dark"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<span className={selected.length === 0 ? 'text-faint' : undefined}>
|
<span className={selected.length === 0 ? 'text-faint dark:text-neutral-400' : undefined}>
|
||||||
{selected.length === 0
|
{selected.length === 0
|
||||||
? 'All services'
|
? 'All services'
|
||||||
: selected.length === 1
|
: selected.length === 1
|
||||||
? selected[0].name
|
? selected[0].name
|
||||||
: `${selected[0].name} +${selected.length - 1} more`}
|
: `${selected[0].name} +${selected.length - 1} more`}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-2.25 text-faint [&>small]:font-mono [&>small]:text-2xs/[1.3] [&>small]:font-medium [&>small]:whitespace-nowrap [&>svg]:size-4 [&>svg]:transition-transform [&>svg]:duration-120 group-data-[state=open]:rotate-180">
|
<span className="flex items-center gap-2.25 text-faint dark:text-neutral-400 [&>small]:font-mono [&>small]:text-2xs/snug-sm [&>small]:font-medium [&>small]:whitespace-nowrap [&>svg]:size-4 [&>svg]:transition-transform [&>svg]:duration-120 group-data-[state=open]:rotate-180">
|
||||||
<small>{selected.length || 'Any'}</small>
|
<small>{selected.length || 'Any'}</small>
|
||||||
<ChevronDown />
|
<ChevronDown />
|
||||||
</span>
|
</span>
|
||||||
@@ -191,14 +191,14 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
Leave empty to notify for every service.
|
Leave empty to notify for every service.
|
||||||
</small>
|
</small>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div className="flex items-start gap-2.75 min-w-0 p-3.5 rounded-lg border border-border-option bg-surface-option transition-colors duration-160 hover:border-border-option-hover hover:bg-surface-option-hover dark:border-white/8 dark:bg-night-subtle dark:hover:border-white/18 dark:hover:bg-night-option-hover **:[[role=switch]]:mt-px [&>label]:min-w-0 [&>label]:cursor-pointer [&_strong]:block [&_strong]:text-caption [&_strong]:font-medium dark:[&_strong]:text-gray-50 [&_small]:block [&_small]:mt-1 [&_small]:text-footnote [&_small]:leading-alert [&_small]:text-muted-text dark:[&_small]:text-gray-400">
|
<div className="col-span-1 flex items-start gap-2.75 min-w-0 p-3.5 rounded-lg border border-border-option bg-surface-option transition-colors duration-160 hover:border-border-option-hover hover:bg-surface-option-hover dark:border-white/8 dark:bg-night-subtle dark:hover:border-white/18 dark:hover:bg-night-option-hover **:[[role=switch]]:mt-px [&>label]:min-w-0 [&>label]:cursor-pointer [&_strong]:block [&_strong]:text-caption [&_strong]:font-medium dark:[&_strong]:text-gray-50 [&_small]:block [&_small]:mt-1 [&_small]:text-footnote [&_small]:leading-alert [&_small]:text-muted-text dark:[&_small]:text-gray-400">
|
||||||
<Switch id="channel-enabled" checked={form.enabled} onCheckedChange={(enabled) => setForm({ ...form, enabled })} />
|
<Switch id="channel-enabled" checked={form.enabled} onCheckedChange={(enabled) => setForm({ ...form, enabled })} />
|
||||||
<label htmlFor="channel-enabled">
|
<label htmlFor="channel-enabled">
|
||||||
<strong>Enable channel</strong>
|
<strong>Enable channel</strong>
|
||||||
<small>Allow automated downtime and recovery alerts.</small>
|
<small>Allow automated downtime and recovery alerts.</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-2.75 min-w-0 p-3.5 rounded-lg border border-border-option bg-surface-option transition-colors duration-160 hover:border-border-option-hover hover:bg-surface-option-hover dark:border-white/8 dark:bg-night-subtle dark:hover:border-white/18 dark:hover:bg-night-option-hover **:[[role=switch]]:mt-px [&>label]:min-w-0 [&>label]:cursor-pointer [&_strong]:block [&_strong]:text-caption [&_strong]:font-medium dark:[&_strong]:text-gray-50 [&_small]:block [&_small]:mt-1 [&_small]:text-footnote [&_small]:leading-alert [&_small]:text-muted-text dark:[&_small]:text-gray-400">
|
<div className="col-span-1 flex items-start gap-2.75 min-w-0 p-3.5 rounded-lg border border-border-option bg-surface-option transition-colors duration-160 hover:border-border-option-hover hover:bg-surface-option-hover dark:border-white/8 dark:bg-night-subtle dark:hover:border-white/18 dark:hover:bg-night-option-hover **:[[role=switch]]:mt-px [&>label]:min-w-0 [&>label]:cursor-pointer [&_strong]:block [&_strong]:text-caption [&_strong]:font-medium dark:[&_strong]:text-gray-50 [&_small]:block [&_small]:mt-1 [&_small]:text-footnote [&_small]:leading-alert [&_small]:text-muted-text dark:[&_small]:text-gray-400">
|
||||||
<Switch
|
<Switch
|
||||||
id="channel-manual"
|
id="channel-manual"
|
||||||
checked={form.notifyManual}
|
checked={form.notifyManual}
|
||||||
@@ -210,12 +210,17 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{form.type === 'webhook' && (
|
{form.type === 'webhook' && (
|
||||||
<div className="sm:col-span-2 p-3 rounded-md border border-border-card bg-surface-soft dark:border-white/8 dark:bg-night-subtle [&>span]:block [&>span]:mb-1.75 [&>span]:text-xs [&>span]:font-semibold dark:[&>span]:text-gray-50 [&>pre]:m-0 [&>pre]:overflow-x-auto [&>pre]:text-footnote [&>pre]:text-muted-text dark:[&>pre]:text-gray-400">
|
<div className="col-span-1 sm:col-span-2 p-3 rounded-md border border-border-card bg-surface-soft dark:border-white/8 dark:bg-night-subtle [&>span]:block [&>span]:mb-1.75 [&>span]:text-xs [&>span]:font-semibold dark:[&>span]:text-gray-50 [&>pre]:m-0 [&>pre]:overflow-x-auto [&>pre]:text-footnote [&>pre]:text-muted-text dark:[&>pre]:text-gray-400">
|
||||||
<span>Raw payload</span>
|
<span>Raw payload</span>
|
||||||
<pre>{`{ "event": "down", "monitor": { … }, "statusCode": 500, "error": "…", "at": "…" }`}</pre>
|
<pre>{`{ "event": "down", "monitor": { … }, "statusCode": 500, "error": "…", "at": "…" }`}</pre>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="form-actions compact-actions sm:col-span-2 sticky bottom-0 z-2 flex justify-end gap-2.25 w-auto -mx-5 sm:-mx-panel-x mt-0.75 px-5 py-3.5 sm:px-panel-x sm:py-3.75 border-t border-border-dialog-footer bg-surface-dialog-footer/97 backdrop-blur-md dark:border-t-white/8 dark:bg-night-card/97 [&_button]:flex-1 sm:[&_button]:flex-initial [&_.primary-button]:min-h-9.5 [&_.primary-button]:px-3.75 [&_.primary-button]:py-1.75 [&_.secondary-button]:min-h-9.5 [&_.secondary-button]:px-3.75 [&_.secondary-button]:py-1.75">
|
{mutation.isError && (
|
||||||
|
<p className="col-span-1 sm:col-span-2 form-error" role="alert">
|
||||||
|
{mutation.error.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="col-span-1 sm:col-span-2 sticky bottom-0 z-2 flex justify-end gap-2.25 w-auto -mx-5 sm:-mx-panel-x mt-0.75 px-5 py-3.5 sm:px-panel-x sm:py-3.75 border-t border-border-dialog-footer bg-surface-dialog-footer/97 backdrop-blur-md dark:border-t-white/8 dark:bg-night-card/97 [&_button]:flex-1 sm:[&_button]:flex-initial [&_.primary-button]:min-h-9.5 [&_.primary-button]:px-3.75 [&_.primary-button]:py-1.75 [&_.secondary-button]:min-h-9.5 [&_.secondary-button]:px-3.75 [&_.secondary-button]:py-1.75">
|
||||||
<Button variant="unstyled" className="secondary-button" type="button" onClick={onClose}>
|
<Button variant="unstyled" className="secondary-button" type="button" onClick={onClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
@@ -223,11 +228,6 @@ export function NotificationChannelDialog({ editing, onClose }: { editing: Notif
|
|||||||
{mutation.isPending ? 'Saving…' : editing ? 'Save changes' : 'Add channel'}
|
{mutation.isPending ? 'Saving…' : editing ? 'Save changes' : 'Add channel'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{mutation.isError && (
|
|
||||||
<p className="form-error sm:col-span-2" role="alert">
|
|
||||||
{mutation.error.message}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</form>
|
</form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
+2
-2
@@ -24,8 +24,8 @@ const customTwMerge = extendTailwindMerge({
|
|||||||
'min-h': ['min-h-metric-h', 'min-h-row-h', 'min-h-skeleton-h', 'min-h-textarea-h', 'min-h-textarea-sm-h'],
|
'min-h': ['min-h-metric-h', 'min-h-row-h', 'min-h-skeleton-h', 'min-h-textarea-h', 'min-h-textarea-sm-h'],
|
||||||
'max-h': ['max-h-skeleton-h'],
|
'max-h': ['max-h-skeleton-h'],
|
||||||
'min-w': ['min-w-dialog-btn'],
|
'min-w': ['min-w-dialog-btn'],
|
||||||
'max-w': ['max-w-dialog-max', 'max-w-select-max'],
|
'max-w': ['max-w-dialog-max', 'max-w-select-max', 'max-w-dialog-channel'],
|
||||||
rounded: ['rounded-card-sm', 'rounded-actions', 'rounded-dialog'],
|
rounded: ['rounded-card-sm', 'rounded-card', 'rounded-actions', 'rounded-dialog'],
|
||||||
tracking: ['tracking-display', 'tracking-title', 'tracking-brand', 'tracking-overline', 'tracking-metric'],
|
tracking: ['tracking-display', 'tracking-title', 'tracking-brand', 'tracking-overline', 'tracking-metric'],
|
||||||
leading: ['leading-title', 'leading-overline', 'leading-alert', 'leading-subtitle'],
|
leading: ['leading-title', 'leading-overline', 'leading-alert', 'leading-subtitle'],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user