Lots of changes to the API and webhooks
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
This commit is contained in:
parent
20552a34ff
commit
636b8cf789
@ -9,6 +9,7 @@ import { useDriveList } from "@/lib/api/hooks/use-drive-queries"
|
|||||||
import type { ApiTokenDriveScope } from "@/lib/api/types"
|
import type { ApiTokenDriveScope } from "@/lib/api/types"
|
||||||
import { displayFileName } from "@/lib/drive/display-file-name"
|
import { displayFileName } from "@/lib/drive/display-file-name"
|
||||||
import { normalizeDriveFolderPath } from "@/lib/drive/drive-sidebar-tree"
|
import { normalizeDriveFolderPath } from "@/lib/drive/drive-sidebar-tree"
|
||||||
|
import { AutomationBorderedFieldset } from "@/components/gmail/settings/automation/automation-bordered-fieldset"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
export function ApiTokenDriveScopeEditor({
|
export function ApiTokenDriveScopeEditor({
|
||||||
@ -61,10 +62,10 @@ export function ApiTokenDriveScopeEditor({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<fieldset className={cn("space-y-3 rounded-md border border-border p-3", className)}>
|
<AutomationBorderedFieldset
|
||||||
<legend className="px-1 text-sm font-medium text-foreground">
|
className={className}
|
||||||
Périmètre Drive — dossiers
|
legend="Périmètre Drive — dossiers"
|
||||||
</legend>
|
>
|
||||||
<label className="flex items-start gap-2">
|
<label className="flex items-start gap-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={scope.all_folders}
|
checked={scope.all_folders}
|
||||||
@ -159,6 +160,6 @@ export function ApiTokenDriveScopeEditor({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</fieldset>
|
</AutomationBorderedFieldset>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Checkbox } from "@/components/ui/checkbox"
|
|||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { useMailAccounts } from "@/lib/api/hooks/use-mail-queries"
|
import { useMailAccounts } from "@/lib/api/hooks/use-mail-queries"
|
||||||
import type { ApiTokenMailScope } from "@/lib/api/types"
|
import type { ApiTokenMailScope } from "@/lib/api/types"
|
||||||
import { cn } from "@/lib/utils"
|
import { AutomationBorderedFieldset } from "@/components/gmail/settings/automation/automation-bordered-fieldset"
|
||||||
|
|
||||||
export function ApiTokenMailScopeEditor({
|
export function ApiTokenMailScopeEditor({
|
||||||
scope,
|
scope,
|
||||||
@ -29,10 +29,10 @@ export function ApiTokenMailScopeEditor({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<fieldset className={cn("space-y-3 rounded-md border border-border p-3", className)}>
|
<AutomationBorderedFieldset
|
||||||
<legend className="px-1 text-sm font-medium text-foreground">
|
className={className}
|
||||||
Périmètre mail — comptes
|
legend="Périmètre mail — comptes"
|
||||||
</legend>
|
>
|
||||||
<label className="flex items-start gap-2">
|
<label className="flex items-start gap-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={scope.all_accounts}
|
checked={scope.all_accounts}
|
||||||
@ -83,6 +83,6 @@ export function ApiTokenMailScopeEditor({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</fieldset>
|
</AutomationBorderedFieldset>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import type { ReactNode } from "react"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export function AutomationBorderedFieldset({
|
||||||
|
legend,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
legendClassName,
|
||||||
|
contentClassName,
|
||||||
|
}: {
|
||||||
|
legend: ReactNode
|
||||||
|
children: ReactNode
|
||||||
|
className?: string
|
||||||
|
legendClassName?: string
|
||||||
|
contentClassName?: string
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<fieldset
|
||||||
|
className={cn("m-0 min-w-0 rounded-md border border-border p-0", className)}
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
className={cn(
|
||||||
|
"ml-2.5 w-auto max-w-[calc(100%-1.25rem)] px-1 text-sm font-medium leading-none text-foreground",
|
||||||
|
legendClassName
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{legend}
|
||||||
|
</legend>
|
||||||
|
<div className={cn("space-y-3 px-3 pb-3 pt-2", contentClassName)}>{children}</div>
|
||||||
|
</fieldset>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { LayoutGrid } from "lucide-react"
|
||||||
|
import type { AutomationDomain } from "@/lib/mail-automation/domains"
|
||||||
|
import { AUTOMATION_DOMAIN_LABELS } from "@/lib/mail-automation/domains"
|
||||||
|
import { suitePublicAsset } from "@/lib/suite/suite-public-asset"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export const AUTOMATION_DOMAIN_MARK_SRC: Record<AutomationDomain, string> = {
|
||||||
|
mail: suitePublicAsset("/brand/ultimail-header-icon.png"),
|
||||||
|
drive: suitePublicAsset("/ultidrive-mark.svg"),
|
||||||
|
contacts: suitePublicAsset("/contacts-mark.svg"),
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AutomationDomainMark({
|
||||||
|
domain,
|
||||||
|
className,
|
||||||
|
alt,
|
||||||
|
}: {
|
||||||
|
domain: AutomationDomain
|
||||||
|
className?: string
|
||||||
|
alt?: string
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={AUTOMATION_DOMAIN_MARK_SRC[domain]}
|
||||||
|
alt={alt ?? AUTOMATION_DOMAIN_LABELS[domain]}
|
||||||
|
className={cn("shrink-0 object-contain", className)}
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AutomationDomainMarks({
|
||||||
|
domains,
|
||||||
|
className,
|
||||||
|
markClassName = "size-5",
|
||||||
|
}: {
|
||||||
|
domains: AutomationDomain[]
|
||||||
|
className?: string
|
||||||
|
markClassName?: string
|
||||||
|
}) {
|
||||||
|
if (domains.length === 0) return null
|
||||||
|
return (
|
||||||
|
<div className={cn("flex shrink-0 items-center gap-1", className)}>
|
||||||
|
{domains.map((domain) => (
|
||||||
|
<AutomationDomainMark
|
||||||
|
key={domain}
|
||||||
|
domain={domain}
|
||||||
|
className={markClassName}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AutomationDomainFilterTab({
|
||||||
|
active,
|
||||||
|
onClick,
|
||||||
|
domain,
|
||||||
|
label,
|
||||||
|
}: {
|
||||||
|
active: boolean
|
||||||
|
onClick: () => void
|
||||||
|
domain: AutomationDomain | "all"
|
||||||
|
label: string
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onClick}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-[11px] transition-colors",
|
||||||
|
active
|
||||||
|
? "border-primary bg-primary/10 text-primary"
|
||||||
|
: "border-border bg-background text-muted-foreground hover:bg-muted"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{domain === "all" ? (
|
||||||
|
<LayoutGrid className="size-3.5 shrink-0 opacity-80" aria-hidden />
|
||||||
|
) : (
|
||||||
|
<AutomationDomainMark domain={domain} className="size-3.5" alt="" />
|
||||||
|
)}
|
||||||
|
<span>{label}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { AutomationBorderedFieldset } from "@/components/gmail/settings/automation/automation-bordered-fieldset"
|
||||||
import { useContactBooks } from "@/lib/api/hooks/use-contact-queries"
|
import { useContactBooks } from "@/lib/api/hooks/use-contact-queries"
|
||||||
import type { WebhookContactsScope } from "@/lib/mail-automation/webhook-config"
|
import type { WebhookContactsScope } from "@/lib/mail-automation/webhook-config"
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
export function WebhookContactsScopeEditor({
|
export function WebhookContactsScopeEditor({
|
||||||
scope,
|
scope,
|
||||||
@ -34,10 +34,10 @@ export function WebhookContactsScopeEditor({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<fieldset className={cn("space-y-3 rounded-md border border-border p-3", className)}>
|
<AutomationBorderedFieldset
|
||||||
<legend className="px-1 text-sm font-medium text-foreground">
|
className={className}
|
||||||
Périmètre contacts — carnets
|
legend="Périmètre contacts — carnets"
|
||||||
</legend>
|
>
|
||||||
<label className="flex items-start gap-2">
|
<label className="flex items-start gap-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={scope.all_books}
|
checked={scope.all_books}
|
||||||
@ -81,6 +81,6 @@ export function WebhookContactsScopeEditor({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</fieldset>
|
</AutomationBorderedFieldset>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import {
|
|||||||
AUTOMATION_DOMAIN_LABELS,
|
AUTOMATION_DOMAIN_LABELS,
|
||||||
type AutomationDomain,
|
type AutomationDomain,
|
||||||
} from "@/lib/mail-automation/domains"
|
} from "@/lib/mail-automation/domains"
|
||||||
|
import { AutomationBorderedFieldset } from "@/components/gmail/settings/automation/automation-bordered-fieldset"
|
||||||
|
import { AutomationDomainMark } from "@/components/gmail/settings/automation/automation-domain-mark"
|
||||||
import type { TriggerType } from "@/lib/mail-automation/types"
|
import type { TriggerType } from "@/lib/mail-automation/types"
|
||||||
import {
|
import {
|
||||||
hasContactsWebhookEvents,
|
hasContactsWebhookEvents,
|
||||||
@ -58,14 +60,21 @@ export function WebhookEventScopeEditor({
|
|||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Le webhook part uniquement pour les événements cochés, dans le périmètre défini ci-dessous.
|
Le webhook part uniquement pour les événements cochés, dans le périmètre défini ci-dessous.
|
||||||
</p>
|
</p>
|
||||||
|
<div className="space-y-3">
|
||||||
{DOMAINS.map((domain) => {
|
{DOMAINS.map((domain) => {
|
||||||
const options = WEBHOOK_EVENT_OPTIONS.filter((o) => o.domain === domain)
|
const options = WEBHOOK_EVENT_OPTIONS.filter((o) => o.domain === domain)
|
||||||
return (
|
return (
|
||||||
<fieldset key={domain} className="rounded-md border border-border p-3">
|
<AutomationBorderedFieldset
|
||||||
<legend className="px-1 text-xs font-medium text-foreground">
|
key={domain}
|
||||||
|
legend={
|
||||||
|
<>
|
||||||
|
<AutomationDomainMark domain={domain} className="size-3.5" alt="" />
|
||||||
{AUTOMATION_DOMAIN_LABELS[domain]}
|
{AUTOMATION_DOMAIN_LABELS[domain]}
|
||||||
</legend>
|
</>
|
||||||
<ul className="mt-2 space-y-1.5">
|
}
|
||||||
|
legendClassName="flex items-center gap-1.5 text-xs"
|
||||||
|
>
|
||||||
|
<ul className="space-y-1.5">
|
||||||
{options.map((opt) => (
|
{options.map((opt) => (
|
||||||
<li key={opt.value}>
|
<li key={opt.value}>
|
||||||
<label className="flex cursor-pointer items-center gap-2 text-sm">
|
<label className="flex cursor-pointer items-center gap-2 text-sm">
|
||||||
@ -78,10 +87,11 @@ export function WebhookEventScopeEditor({
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</fieldset>
|
</AutomationBorderedFieldset>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ApiTokenMailScopeEditor
|
<ApiTokenMailScopeEditor
|
||||||
enabled={hasMailWebhookEvents(eventTypes)}
|
enabled={hasMailWebhookEvents(eventTypes)}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
AUTOMATION_DOMAIN_LABELS,
|
AUTOMATION_DOMAIN_LABELS,
|
||||||
type AutomationDomain,
|
type AutomationDomain,
|
||||||
} from "@/lib/mail-automation/domains"
|
} from "@/lib/mail-automation/domains"
|
||||||
|
import { AutomationDomainFilterTab } from "@/components/gmail/settings/automation/automation-domain-mark"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
const DOMAIN_TABS: Array<AutomationDomain | "all"> = ["all", "mail", "drive", "contacts"]
|
const DOMAIN_TABS: Array<AutomationDomain | "all"> = ["all", "mail", "drive", "contacts"]
|
||||||
@ -79,19 +80,13 @@ export function WebhookTemplateVariablesPanel() {
|
|||||||
</p>
|
</p>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{DOMAIN_TABS.map((tab) => (
|
{DOMAIN_TABS.map((tab) => (
|
||||||
<button
|
<AutomationDomainFilterTab
|
||||||
key={tab}
|
key={tab}
|
||||||
type="button"
|
domain={tab}
|
||||||
|
label={tab === "all" ? "Tout" : AUTOMATION_DOMAIN_LABELS[tab]}
|
||||||
|
active={filter === tab}
|
||||||
onClick={() => setFilter(tab)}
|
onClick={() => setFilter(tab)}
|
||||||
className={cn(
|
/>
|
||||||
"rounded-md border px-2 py-0.5 text-[11px] transition-colors",
|
|
||||||
filter === tab
|
|
||||||
? "border-primary bg-primary/10 text-primary"
|
|
||||||
: "border-border bg-background text-muted-foreground hover:bg-muted"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{tab === "all" ? "Tout" : AUTOMATION_DOMAIN_LABELS[tab]}
|
|
||||||
</button>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,6 +22,11 @@ import { SettingsSyncBanner } from "@/components/gmail/settings/settings-sync-ba
|
|||||||
import { WebhookTemplateVariablesPanel } from "@/components/gmail/settings/automation/webhook-template-variables-panel"
|
import { WebhookTemplateVariablesPanel } from "@/components/gmail/settings/automation/webhook-template-variables-panel"
|
||||||
import { AutomationTabMasonry } from "@/components/gmail/settings/automation/automation-tab-masonry"
|
import { AutomationTabMasonry } from "@/components/gmail/settings/automation/automation-tab-masonry"
|
||||||
import { WebhookEventScopeEditor } from "@/components/gmail/settings/automation/webhook-event-scope-editor"
|
import { WebhookEventScopeEditor } from "@/components/gmail/settings/automation/webhook-event-scope-editor"
|
||||||
|
import {
|
||||||
|
AutomationDomainFilterTab,
|
||||||
|
AutomationDomainMark,
|
||||||
|
AutomationDomainMarks,
|
||||||
|
} from "@/components/gmail/settings/automation/automation-domain-mark"
|
||||||
import { WEBHOOK_DEFAULT_TEMPLATES } from "@/lib/mail-automation/webhook-template-variables"
|
import { WEBHOOK_DEFAULT_TEMPLATES } from "@/lib/mail-automation/webhook-template-variables"
|
||||||
import {
|
import {
|
||||||
AUTOMATION_DOMAIN_LABELS,
|
AUTOMATION_DOMAIN_LABELS,
|
||||||
@ -30,12 +35,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
createDefaultWebhookForm,
|
createDefaultWebhookForm,
|
||||||
defaultEventTypesForDomain,
|
defaultEventTypesForDomain,
|
||||||
|
eventDomainsFromTypes,
|
||||||
type WebhookFormState,
|
type WebhookFormState,
|
||||||
} from "@/lib/mail-automation/webhook-config"
|
} from "@/lib/mail-automation/webhook-config"
|
||||||
import { TRIGGER_LABELS } from "@/lib/mail-automation/domains"
|
import { TRIGGER_LABELS } from "@/lib/mail-automation/domains"
|
||||||
import type { ApiWebhook } from "@/lib/api/types"
|
import type { ApiWebhook } from "@/lib/api/types"
|
||||||
import type { TriggerType } from "@/lib/mail-automation/types"
|
import type { TriggerType } from "@/lib/mail-automation/types"
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
const CREATE_DOMAINS: AutomationDomain[] = ["mail", "drive", "contacts"]
|
||||||
|
|
||||||
function webhookToForm(hook: ApiWebhook): WebhookFormState {
|
function webhookToForm(hook: ApiWebhook): WebhookFormState {
|
||||||
return {
|
return {
|
||||||
@ -57,6 +64,11 @@ function summarizeWebhook(hook: ApiWebhook): string {
|
|||||||
return labels.join(", ") + extra
|
return labels.join(", ") + extra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function webhookDomains(hook: ApiWebhook): AutomationDomain[] {
|
||||||
|
const types = (hook.event_types ?? []) as TriggerType[]
|
||||||
|
return eventDomainsFromTypes(types.length > 0 ? types : ["message_received"])
|
||||||
|
}
|
||||||
|
|
||||||
export function WebhooksPanel() {
|
export function WebhooksPanel() {
|
||||||
const { ready, authenticated } = useAuthReady()
|
const { ready, authenticated } = useAuthReady()
|
||||||
const { data: webhooks = [], isFetching, isError, refetch, isPending } = useMailWebhooks()
|
const { data: webhooks = [], isFetching, isError, refetch, isPending } = useMailWebhooks()
|
||||||
@ -86,6 +98,8 @@ export function WebhooksPanel() {
|
|||||||
function openEdit(hook: ApiWebhook) {
|
function openEdit(hook: ApiWebhook) {
|
||||||
setEditingId(hook.id)
|
setEditingId(hook.id)
|
||||||
setForm(webhookToForm(hook))
|
setForm(webhookToForm(hook))
|
||||||
|
const domains = webhookDomains(hook)
|
||||||
|
setPresetDomain(domains[0] ?? "mail")
|
||||||
setEditorOpen(true)
|
setEditorOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,42 +133,70 @@ export function WebhooksPanel() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsSyncBanner isFetching={isFetching} isError={isError} onRetry={() => refetch()} />
|
<SettingsSyncBanner isFetching={isFetching} isError={isError} onRetry={() => refetch()} />
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<AutomationTabMasonry columns={2}>
|
||||||
{(["mail", "drive", "contacts"] as AutomationDomain[]).map((domain) => (
|
<div className="space-y-4">
|
||||||
<Button key={domain} type="button" size="sm" variant="outline" onClick={() => openCreate(domain)}>
|
<div className="space-y-2">
|
||||||
Webhook {AUTOMATION_DOMAIN_LABELS[domain]}
|
<p className="text-sm font-medium">Webhooks</p>
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
{CREATE_DOMAINS.map((domain) => (
|
||||||
|
<Button
|
||||||
|
key={domain}
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-9 w-full justify-start gap-2 px-3"
|
||||||
|
onClick={() => openCreate(domain)}
|
||||||
|
>
|
||||||
|
<AutomationDomainMark domain={domain} className="size-5" alt="" />
|
||||||
|
<span>Nouveau webhook {AUTOMATION_DOMAIN_LABELS[domain]}</span>
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<AutomationTabMasonry columns={2}>
|
|
||||||
<WebhookTemplateVariablesPanel />
|
|
||||||
|
|
||||||
{showInitialLoad ? (
|
{showInitialLoad ? (
|
||||||
<p className="text-sm text-muted-foreground">Chargement…</p>
|
<p className="text-sm text-muted-foreground">Chargement…</p>
|
||||||
) : webhooks.length === 0 ? (
|
) : webhooks.length === 0 ? (
|
||||||
<p className="text-sm text-muted-foreground">Aucun webhook.</p>
|
<p className="text-sm text-muted-foreground">Aucun webhook configuré.</p>
|
||||||
) : (
|
) : (
|
||||||
<ul className="divide-y divide-border rounded-lg border border-border">
|
<ul className="divide-y divide-border rounded-lg border border-border">
|
||||||
{webhooks.map((hook) => (
|
{webhooks.map((hook) => {
|
||||||
<li key={hook.id} className="flex items-start justify-between gap-2 px-3 py-3">
|
const domains = webhookDomains(hook)
|
||||||
<div className="min-w-0">
|
return (
|
||||||
|
<li key={hook.id} className="flex items-start gap-2 px-3 py-3">
|
||||||
|
<AutomationDomainMarks
|
||||||
|
domains={domains}
|
||||||
|
className="pt-0.5"
|
||||||
|
markClassName="size-5"
|
||||||
|
/>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
<p className="text-sm font-medium">{hook.name}</p>
|
<p className="text-sm font-medium">{hook.name}</p>
|
||||||
<p className="truncate text-xs text-muted-foreground">{hook.url}</p>
|
<p className="truncate text-xs text-muted-foreground">{hook.url}</p>
|
||||||
<p className="mt-0.5 text-[11px] text-muted-foreground">{summarizeWebhook(hook)}</p>
|
<p className="mt-0.5 text-[11px] text-muted-foreground">
|
||||||
|
{summarizeWebhook(hook)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex shrink-0 gap-1">
|
<div className="flex shrink-0 gap-1">
|
||||||
<Button type="button" variant="ghost" size="icon" onClick={() => openEdit(hook)}>
|
<Button type="button" variant="ghost" size="icon" onClick={() => openEdit(hook)}>
|
||||||
<Pencil className="size-4" />
|
<Pencil className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" variant="ghost" size="icon" onClick={() => deleteWebhook.mutate(hook.id)}>
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => deleteWebhook.mutate(hook.id)}
|
||||||
|
>
|
||||||
<Trash2 className="size-4" />
|
<Trash2 className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<WebhookTemplateVariablesPanel />
|
||||||
</AutomationTabMasonry>
|
</AutomationTabMasonry>
|
||||||
|
|
||||||
<Dialog open={editorOpen} onOpenChange={setEditorOpen}>
|
<Dialog open={editorOpen} onOpenChange={setEditorOpen}>
|
||||||
@ -164,11 +206,15 @@ export function WebhooksPanel() {
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex-1 space-y-4 overflow-y-auto p-4">
|
<div className="flex-1 space-y-4 overflow-y-auto p-4">
|
||||||
{!editingId ? (
|
{!editingId ? (
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label className="text-xs text-muted-foreground">Modèle de départ</Label>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{(["mail", "drive", "contacts"] as AutomationDomain[]).map((domain) => (
|
{CREATE_DOMAINS.map((domain) => (
|
||||||
<button
|
<AutomationDomainFilterTab
|
||||||
key={domain}
|
key={domain}
|
||||||
type="button"
|
domain={domain}
|
||||||
|
label={AUTOMATION_DOMAIN_LABELS[domain]}
|
||||||
|
active={presetDomain === domain}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPresetDomain(domain)
|
setPresetDomain(domain)
|
||||||
setForm((f) => ({
|
setForm((f) => ({
|
||||||
@ -177,17 +223,10 @@ export function WebhooksPanel() {
|
|||||||
template: WEBHOOK_DEFAULT_TEMPLATES[domain],
|
template: WEBHOOK_DEFAULT_TEMPLATES[domain],
|
||||||
}))
|
}))
|
||||||
}}
|
}}
|
||||||
className={cn(
|
/>
|
||||||
"rounded-md border px-2 py-0.5 text-[11px] transition-colors",
|
|
||||||
presetDomain === domain
|
|
||||||
? "border-primary bg-primary/10 text-primary"
|
|
||||||
: "border-border bg-background text-muted-foreground hover:bg-muted"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Modèle {AUTOMATION_DOMAIN_LABELS[domain]}
|
|
||||||
</button>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user