Kontrakt webhooku: objekt jde vybrat a odmitnuti je videt
Parametr spoustece `data` byl deklarovany jako type string a povinny, zatimco odesilatel ho posila jako objekt a v prvni zprave hovoru ho jeste nema. Kazde volani proto skoncilo na 400 a automatizace hodinu nedelala nic. Za tim byly tri veci, kazda sama o sobe malicherna: - rucne pridany parametr byl vychozi povinny, zatimco parametr odvozeny z ukazkoveho tela nepovinny. Dve ruzna vychozi nastaveni pro tutez vec v jednom formulari. Nove je nepovinny i rucne pridany: povinny znamena "odmitni volani" a do toho nema nikdo spadnout omylem - objekt a seznam neslo vybrat. declarableFieldTypes nabizel jen string, number, boolean a date, a TriggerConfig.tsx mel jeste treti kopii toho seznamu. Deklarovat data jako objekt tedy neslo, i kdyz matchesType objekt umi a operatorsByType pro nej ma operatory - odmitnuti nebylo nikde videt. Skoncilo jako console.warn v logu kontejneru: zadna udalost, zadny beh, nic na detailu automatizace Ten treti bod je ten podstatny. Chybu v kontraktu udela ten, kdo ho psal, ale 400 dostane odesilatel - a ten s tim nic nenadela, casto je to cizi sluzba, ktera volani neopakuje. Majitel automatizace se nedozvi nic a v portalu vypada vsechno v poradku. Detail automatizace proto ukazuje poslednich deset volani: cas, jestli proslo nebo ne, a u odmitnutych duvod. Telo se schvalne neuklada, duvod uz rika, co je spatne, a drzet payloady by znamenalo mit v pameti kopie zakaznickych dat. Seznam je v pameti, restart ho zahodi. Incident se z toho nezaklada a upozorneni se neposila: staci radek, implementator se ozve sam. Vzorova automatizace ma data opravene na object a nepovinne. Do navrhu 25 jsou zapsana rozhodnuti z diskuze: "moje tickety" jsou tickety prirazene mne, v helpdesku ty, ktere jsem zalozil ja, a helpdeskove pozadavky vidi lide podle teze hierarchie jako tickety. Sekce 6 popisuje tuhle zmenu. Overeno na bezici instanci s vlastnim DATA_DIR: telo s data jako objektem projde, prvni zprava hovoru s data null projde, telo bez callSid se dal odmita, a vsechna tri jsou videt v seznamu poslednich volani. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
72da07debe
commit
e8bb4d6e54
@@ -1,8 +1,9 @@
|
||||
# 25 - Navrh: pristupny portal a viditelnost
|
||||
|
||||
**Navrh, ne popis stavu. Nic z toho zatim neni naprogramovane.** Az se cast
|
||||
udela, prepise se do prislusneho souboru dokumentace a odsud zmizi. Stejne
|
||||
pravidlo jako u [09-navrh-rozsireni.md](09-navrh-rozsireni.md).
|
||||
**Navrh, ne popis stavu.** Jedina hotova cast je sekce 6, ostatni zatim
|
||||
naprogramovane neni. Az se cast udela, prepise se do prislusneho souboru
|
||||
dokumentace a odsud zmizi. Stejne pravidlo jako
|
||||
u [09-navrh-rozsireni.md](09-navrh-rozsireni.md).
|
||||
|
||||
Popis soucasneho stavu je v [01-prehled-a-stav.md](01-prehled-a-stav.md), prava
|
||||
a pohledy v [07-firmy-a-prava.md](07-firmy-a-prava.md), widgety
|
||||
@@ -390,6 +391,19 @@ jinak -> moje tickety
|
||||
Posledni radka je rozhodnuti, ne odvozeni: bez ni nema vedouci co rozdelovat,
|
||||
protoze neprirazeny ticket nepatri nikomu.
|
||||
|
||||
**"Moje" znamena prirazene mne.** Nic vic: ne zalozene mnou, ne komentovane
|
||||
mnou. Ticket, ktery clovek zapsal po telefonu a predal dal, uz neni jeho -
|
||||
prave o tom predani slo.
|
||||
|
||||
V **helpdesku** je to naopak: tam jsou "moje" ty pozadavky, ktere jsem **zalozil
|
||||
ja**. Neni to vyjimka z pravidla, je to tentyz pohled z druhe strany. Zadavatel
|
||||
pozadavek nikdy nema prirazeny, protoze ho resi nekdo jiny, a helpdeskove
|
||||
pozadavky se automaticky vazou na hlavni firmu.
|
||||
|
||||
Kdo pozadavky v helpdesku vidi, se ridi **toutez hierarchii** jako u ticketu:
|
||||
firemni priznak, pak skupiny, jinak svoje. Helpdesk tedy nema vlastni pravidlo
|
||||
viditelnosti, jen jinou definici toho, co je "moje".
|
||||
|
||||
### Past: dve identity
|
||||
|
||||
Projekt ma **uzivatele** (kdo se prihlasi) a **resitele** (na koho jde ticket),
|
||||
@@ -474,6 +488,58 @@ nem neda nic overit do hloubky.
|
||||
|
||||
---
|
||||
|
||||
## 6 - Kdyz kontrakt nesedi na data (hotovo)
|
||||
|
||||
Jedina cast, ktera uz naprogramovana **je**. Je tady proto, ze patri ke stejne
|
||||
tride problemu jako zbytek: konfigurace je formalne v poradku a pritom nesedi na
|
||||
skutecna data.
|
||||
|
||||
### Jak to vypadalo
|
||||
|
||||
Parametr spoustece `data` byl deklarovany jako `type: string, required: true`,
|
||||
zatimco odesilatel ho posila jako objekt a v prvni zprave hovoru ho jeste nema.
|
||||
Kazde volani proto skoncilo na 400 a automatizace hodinu nedelala nic.
|
||||
|
||||
Za tim byly tri veci a kazda z nich sama o sobe malicherna:
|
||||
|
||||
1. **Rucne pridany parametr byl vychozi povinny**, zatimco parametr odvozeny
|
||||
z ukazkoveho tela nepovinny. Dve ruzna vychozi nastaveni pro tutez vec
|
||||
v jednom formulari.
|
||||
2. **Objekt a seznam neslo vybrat.** `declarableFieldTypes` je nabizel jen
|
||||
`string`, `number`, `boolean` a `date`, a klient mel jeste svoji treti kopii
|
||||
toho seznamu. Deklarovat `data` jako objekt tedy neslo, i kdyz `matchesType`
|
||||
objekt umi a `operatorsByType` pro nej ma operatory.
|
||||
3. **Odmitnuti nebylo nikde videt.** Skoncilo jako `console.warn` v logu
|
||||
kontejneru: zadna udalost, zadny beh, nic na detailu automatizace.
|
||||
|
||||
Ten treti bod je ten podstatny. Chybu v kontraktu udela ten, kdo ho psal, ale
|
||||
**400 dostane odesilatel** - a ten s tim nic nenadela, casto je to cizi sluzba,
|
||||
ktera volani neopakuje. Majitel automatizace se nedozvi nic a v portalu vypada
|
||||
vsechno v poradku.
|
||||
|
||||
### Co se zmenilo
|
||||
|
||||
- **Novy parametr je nepovinny.** Povinny znamena "odmitni volani" a do toho
|
||||
nema nikdo spadnout omylem. Zaskrtnout to jde vzdycky.
|
||||
- **Objekt a seznam jdou vybrat.** `declarableFieldTypes` je ma a klient si uz
|
||||
nedrzi vlastni kopii, bere sdileny seznam.
|
||||
- **Poslednich deset volani je videt na detailu automatizace.** Cas, jestli
|
||||
proslo nebo ne, a u odmitnutych duvod.
|
||||
|
||||
Telo se **schvalne neuklada**. Duvod odmitnuti uz rika, co je spatne ("Parametr
|
||||
data ma mit typ string"), a drzet payloady by znamenalo mit v pameti kopie
|
||||
zakaznickych dat, aniz by o tom kdokoliv vedel. Seznam volani se drzi v pameti,
|
||||
restart ho zahodi - je to diagnostika posledni hodiny, ne historie.
|
||||
|
||||
### Co se tim nedela
|
||||
|
||||
Nezaklada se incident, neposila se upozorneni. Staci radek: kdyz se
|
||||
implementator ozve, ze mu neco nesedi, je tady videt co. Rozsirit se to da
|
||||
pozdeji, ale kazda dalsi vrstva stoji za to az ve chvili, kdy tenhle seznam
|
||||
prestane stacit.
|
||||
|
||||
---
|
||||
|
||||
## Poradi praci
|
||||
|
||||
1. **Formularove primitivy** a rozdeleni dvou formularu (ticket, helpdesk). Nic
|
||||
@@ -500,34 +566,19 @@ Postgres kdykoliv mezi tim, nezavisle na ostatnim.
|
||||
- **Strankovani seznamu ticketu** zmeni chovani dnesniho klientskeho hledani. Musi
|
||||
jit ruku v ruce se serverovym hledanim, ne pred nim.
|
||||
|
||||
## Rozhodnuto
|
||||
|
||||
- **"Moje tickety" jsou tickety prirazene mne.** V helpdesku ty, ktere jsem
|
||||
zalozil ja. Podrobnosti v sekci 4, cast "Vypocet stropu".
|
||||
- **Helpdeskove pozadavky vidi lide podle teze hierarchie** jako tickety, tedy
|
||||
firemni priznak, pak skupiny, jinak svoje.
|
||||
- **Odmitnuta volani webhooku maji byt videt na automatizaci**, staci radek
|
||||
s casem a duvodem. Uz je to hotove, viz sekce 6.
|
||||
|
||||
## Otevrene otazky
|
||||
|
||||
U kazde jde o rozhodnuti, ktere se z modelu neda odvodit.
|
||||
|
||||
### A - Co znamena "moje tickety" pro strop
|
||||
|
||||
Tri odpovedi, lisi se v tom, kdy clovek o ticket prijde z dohledu:
|
||||
|
||||
| Varianta | Dusledek |
|
||||
| ---------------------------------- | --------------------------------------------------------------- |
|
||||
| jen prirazene mne | pracovnik zalozi ticket po telefonu, preda ho a hned o nem nevi |
|
||||
| prirazene plus zalozene mnou | vidi i to, co poslal dal |
|
||||
| prirazene, zalozene i komentovane | vidi vse, ceho se dotkl |
|
||||
|
||||
Ticket dnes nenese, kdo ho zalozil rucne (`automationId` je jen u automatickych),
|
||||
takze druha a treti varianta znamenaji nove pole na ticketu.
|
||||
|
||||
### B - Helpdesk a strop
|
||||
|
||||
Firma A posle pozadavek firme B. Zadavatel u firmy A ho vidi pres
|
||||
`helpdeskSourceId`, i kdyz ticket vlastni firma B. Zadavatel neni resitel a neni
|
||||
v zadne skupine, takze strop na nej nesedi.
|
||||
|
||||
Nabizi se, ze helpdeskovy pohled ma vlastni pravidlo ("vidim, co moje firma
|
||||
poslala") a strop se na nej nevztahuje. Otazka je, **kdo z firmy A to vidi**:
|
||||
kazdy clen firmy, nebo jen ten, kdo pozadavek poslal? U firmy o peti lidech je
|
||||
odpoved jina nez u firmy o padesati.
|
||||
|
||||
### C - Hledani v udalostech
|
||||
|
||||
Udalosti nesou cele prijate telo webhooku. Hledat v nem znamena hledat v datech,
|
||||
|
||||
@@ -2,6 +2,44 @@
|
||||
|
||||
Nejnovejsi nahore.
|
||||
|
||||
## 2026-09-02 - Kontrakt webhooku: objekt jde vybrat a odmitnuti je videt
|
||||
|
||||
Parametr spoustece `data` byl deklarovany jako `type: string, required: true`,
|
||||
zatimco odesilatel ho posila jako objekt a v prvni zprave hovoru ho jeste nema.
|
||||
Kazde volani proto skoncilo na 400 a automatizace hodinu nedelala nic.
|
||||
|
||||
Za tim byly tri veci, kazda sama o sobe malicherna:
|
||||
|
||||
- **Rucne pridany parametr byl vychozi povinny**, zatimco parametr odvozeny
|
||||
z ukazkoveho tela nepovinny. Dve ruzna vychozi nastaveni pro tutez vec
|
||||
v jednom formulari. Nove je nepovinny i rucne pridany: povinny znamena
|
||||
"odmitni volani" a do toho nema nikdo spadnout omylem.
|
||||
- **Objekt a seznam neslo vybrat.** `declarableFieldTypes` nabizel jen `string`,
|
||||
`number`, `boolean` a `date`, a `TriggerConfig.tsx` mel jeste treti kopii toho
|
||||
seznamu. Deklarovat `data` jako objekt tedy neslo, i kdyz `matchesType` objekt
|
||||
umi a `operatorsByType` pro nej ma operatory. Ted jsou v nabidce oba a klient
|
||||
si vlastni kopii nedrzi.
|
||||
- **Odmitnuti nebylo nikde videt.** Skoncilo jako `console.warn` v logu
|
||||
kontejneru: zadna udalost, zadny beh, nic na detailu automatizace.
|
||||
|
||||
Ten treti bod je ten podstatny. Chybu v kontraktu udela ten, kdo ho psal, ale
|
||||
**400 dostane odesilatel** - a ten s tim nic nenadela, casto je to cizi sluzba,
|
||||
ktera volani neopakuje. Majitel automatizace se nedozvi nic a v portalu vypada
|
||||
vsechno v poradku.
|
||||
|
||||
Detail automatizace proto ukazuje **poslednich deset volani**: cas, jestli
|
||||
proslo nebo ne, a u odmitnutych duvod. Telo se schvalne neuklada, duvod uz rika,
|
||||
co je spatne, a drzet payloady by znamenalo mit v pameti kopie zakaznickych dat.
|
||||
Seznam je v pameti, restart ho zahodi - je to diagnostika posledni hodiny, ne
|
||||
historie. Incident se z toho nezaklada a upozorneni se neposila: staci radek,
|
||||
protoze implementator se ozve sam a tady je videt co.
|
||||
|
||||
Vzorova automatizace ma `data` opravene na `type: 'object', required: false`.
|
||||
|
||||
Overeno na bezici instanci s vlastnim DATA_DIR: telo s `data` jako objektem
|
||||
projde, prvni zprava hovoru s `data: null` projde, telo bez `callSid` se dal
|
||||
odmita - a vsechna tri jsou videt v seznamu poslednich volani.
|
||||
|
||||
## 2026-09-02 - Navrh: pristupny portal a viditelnost
|
||||
|
||||
Novy [25-navrh-pristupny-portal.md](25-navrh-pristupny-portal.md). Je to navrh,
|
||||
|
||||
@@ -170,8 +170,54 @@ export interface Automation {
|
||||
issues: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Jedno volani webhooku, jak dopadlo.
|
||||
*
|
||||
* Odmitnute volani do ted skoncilo jako radek v logu kontejneru, kam se nikdo
|
||||
* nedostane. Odesilatel dostal 400 a vedel o tom, ale ten, kdo kontrakt napsal,
|
||||
* se nedozvedel nic - automatizace svitila zelene a jen do ni nic nechodilo.
|
||||
*
|
||||
* Telo se **schvalne neuklada**. Duvod odmitnuti uz rika, co je spatne
|
||||
* ("Parametr data ma mit typ string"), a drzet payloady by znamenalo mit
|
||||
* v pameti kopie zakaznickych dat bez toho, aby o tom kdokoliv vedel.
|
||||
*/
|
||||
export interface WebhookCall {
|
||||
at: string;
|
||||
/** true = prevzato do fronty, false = odmitnuto pro neplatna data. */
|
||||
ok: boolean;
|
||||
/** Duvody odmitnuti. Prazdne u prevzatych volani. */
|
||||
problems: string[];
|
||||
/** Beh, ktery z volani vznikl. null u odmitnutych a u duplicit. */
|
||||
runId: string | null;
|
||||
}
|
||||
|
||||
/** Kolik poslednich volani se u automatizace drzi. */
|
||||
const MAX_CALLS = 10;
|
||||
|
||||
const calls = new Map<string, WebhookCall[]>();
|
||||
|
||||
/** Zapise, jak dopadlo jedno volani webhooku. Nejnovejsi je prvni. */
|
||||
export function recordWebhookCall(automationId: string, call: WebhookCall): void {
|
||||
const list = calls.get(automationId) ?? [];
|
||||
list.unshift(call);
|
||||
if (list.length > MAX_CALLS) list.length = MAX_CALLS;
|
||||
calls.set(automationId, list);
|
||||
}
|
||||
|
||||
/** Poslednich par volani. Cte se jen pres detail automatizace, ktery hlida firmu. */
|
||||
export function recentWebhookCalls(automationId: string): WebhookCall[] {
|
||||
return calls.get(automationId) ?? [];
|
||||
}
|
||||
|
||||
export interface AutomationDetail extends Automation {
|
||||
flow: AutomationFlow;
|
||||
/**
|
||||
* Poslednich `MAX_CALLS` volani webhooku, nejnovejsi prvni.
|
||||
*
|
||||
* Drzi se v pameti, restart je zahodi. Je to diagnostika posledni hodiny,
|
||||
* ne historie - na tu jsou behy.
|
||||
*/
|
||||
recentCalls: WebhookCall[];
|
||||
/**
|
||||
* Model prichozich dat odvozeny z ukazky u spoustece.
|
||||
*
|
||||
@@ -1001,7 +1047,9 @@ function seedRealAutomations(): void {
|
||||
{ id: 'f_voicebot', name: 'voicebotId', type: 'string', required: true },
|
||||
{ id: 'f_mtjqv4qj_1', name: 'result', type: 'string', required: false, path: 'data.result' },
|
||||
{ id: 'f_mtjqv4zn_2', name: 'rating', type: 'string', required: false, path: 'data.rating' },
|
||||
{ id: 'f_mtjqvws7_3', name: 'data', type: 'string', required: true, path: 'data' },
|
||||
// Objekt a nepovinne: telo ho posila jako strukturu a prvni zprava
|
||||
// hovoru ho jeste nema. Deklarace `string` a povinny odmitala oboji.
|
||||
{ id: 'f_mtjqvws7_3', name: 'data', type: 'object', required: false, path: 'data' },
|
||||
],
|
||||
webhookToken: config.seedWebhookToken || generateWebhookToken(),
|
||||
},
|
||||
@@ -1101,6 +1149,7 @@ function toDetail(stored: StoredAutomation): AutomationDetail {
|
||||
model: stored.flow.trigger?.sample === undefined
|
||||
? []
|
||||
: describeModel(stored.flow.trigger.sample),
|
||||
recentCalls: recentWebhookCalls(stored.id),
|
||||
createdAt: stored.createdAt,
|
||||
updatedAt: stored.updatedAt,
|
||||
};
|
||||
|
||||
+17
-2
@@ -35,8 +35,23 @@ export const fieldTypes: FieldType[] = [
|
||||
'list',
|
||||
];
|
||||
|
||||
/** Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece. */
|
||||
export const declarableFieldTypes: FieldType[] = ['string', 'number', 'boolean', 'date'];
|
||||
/**
|
||||
* Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece.
|
||||
*
|
||||
* Objekt a seznam tady driv nebyly, protoze podminka se nad nimi zeptat skoro
|
||||
* nema na co. Jenze parametr neni jen podklad pro podminku: kdyz odesilatel
|
||||
* posle `data` jako objekt, musi jit deklarovat objekt, jinak ho kontrakt
|
||||
* odmitne. Bez toho zbyval jediny "spravny" postup - nedeklarovat ho vubec
|
||||
* a sahat na nej cestou, coz nikoho nenapadne.
|
||||
*/
|
||||
export const declarableFieldTypes: FieldType[] = [
|
||||
'string',
|
||||
'number',
|
||||
'boolean',
|
||||
'date',
|
||||
'object',
|
||||
'list',
|
||||
];
|
||||
|
||||
/** Ktere operatory maji smysl pro ktery typ. */
|
||||
export const operatorsByType: Record<FieldType, ConditionOperator[]> = {
|
||||
|
||||
+25
-1
@@ -1,6 +1,10 @@
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import { findByWebhookToken, type TriggerField } from '../data/automationStore.js';
|
||||
import {
|
||||
findByWebhookToken,
|
||||
recordWebhookCall,
|
||||
type TriggerField,
|
||||
} from '../data/automationStore.js';
|
||||
import type { FieldType } from '../data/conditions.js';
|
||||
import { findByIntakeToken } from '../data/tenants.js';
|
||||
import { intakeEvent } from '../data/ticketStore.js';
|
||||
@@ -191,6 +195,18 @@ webhookRouter.post('/:token', (req, res) => {
|
||||
|
||||
if (problems.length > 0) {
|
||||
console.warn(`[webhook] ${automation.id}: neplatna data - ${problems.join(' ')}`);
|
||||
/*
|
||||
* Odmitnuti musi byt videt v portalu, ne jen v logu kontejneru. Chybu
|
||||
* v kontraktu udela ten, kdo ho psal, ale 400 dostane odesilatel - a ten
|
||||
* s tim nic nenadela. Bez tohohle zaznamu se majitel automatizace nedozvi,
|
||||
* ze uz hodinu nic nechodi, protoze v portalu vypada vsechno v poradku.
|
||||
*/
|
||||
recordWebhookCall(automation.id, {
|
||||
at: new Date().toISOString(),
|
||||
ok: false,
|
||||
problems,
|
||||
runId: null,
|
||||
});
|
||||
return res.status(400).json({
|
||||
error: 'validation_error',
|
||||
message: problems[0],
|
||||
@@ -224,6 +240,14 @@ webhookRouter.post('/:token', (req, res) => {
|
||||
console.info(
|
||||
`[webhook] ${automation.id}: prijato, zarazeno jako ${item?.id ?? '(duplicita)'}`,
|
||||
);
|
||||
// I prevzata volani, aby v prehledu bylo videt "tohle proslo, tohle ne"
|
||||
// a ne jen seznam chyb bez meritka.
|
||||
recordWebhookCall(automation.id, {
|
||||
at: new Date().toISOString(),
|
||||
ok: true,
|
||||
problems: [],
|
||||
runId: item?.id ?? null,
|
||||
});
|
||||
// 202: prevzato, zpracuje se. Ne 200, ktera by rikala "hotovo".
|
||||
return res.status(202).json({
|
||||
accepted: true,
|
||||
|
||||
@@ -12,15 +12,19 @@ import { useState } from 'react';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { cn } from '@/lib/cn';
|
||||
import { fieldTypeLabels, newFieldId } from '@/lib/flow';
|
||||
import type {
|
||||
Service,
|
||||
FieldType,
|
||||
FlowTrigger,
|
||||
ModelNode,
|
||||
TriggerField,
|
||||
import {
|
||||
declarableFieldTypes,
|
||||
type Service,
|
||||
type FieldType,
|
||||
type FlowTrigger,
|
||||
type ModelNode,
|
||||
type TriggerField,
|
||||
} from '@/types/dashboard';
|
||||
|
||||
const fieldTypes: FieldType[] = ['string', 'number', 'boolean', 'date'];
|
||||
// Nabidka typu se bere ze sdileneho seznamu. Driv tu byla vlastni kopie a ta
|
||||
// se rozesla: objekt a seznam v ni chybely, takze `data` sla deklarovat jen
|
||||
// jako text a kontrakt pak odmital kazde volani.
|
||||
const fieldTypes: readonly FieldType[] = declarableFieldTypes;
|
||||
|
||||
/**
|
||||
* Nastaveni spoustece: registrovana adresa webhooku a deklarace parametru,
|
||||
@@ -101,8 +105,14 @@ function CustomFields({
|
||||
editable: boolean;
|
||||
onChange: (fields: TriggerField[]) => void;
|
||||
}) {
|
||||
/*
|
||||
* Novy parametr je **nepovinny**. Povinny znamena "odmitni volani", a do toho
|
||||
* nema nikdo spadnout omylem - zaskrtnout to jde vzdycky. Odvozeni parametru
|
||||
* z ukazkoveho tela to tak delalo uz driv, rucni pridani ne, a ta nesrovnalost
|
||||
* stala jedno odpoledne hledani, proc webhook prestal chodit.
|
||||
*/
|
||||
function addField() {
|
||||
onChange([...fields, { id: newFieldId(), name: '', type: 'string', required: true }]);
|
||||
onChange([...fields, { id: newFieldId(), name: '', type: 'string', required: false }]);
|
||||
}
|
||||
|
||||
function updateField(id: string, patch: Partial<TriggerField>) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import type {
|
||||
AutomationFlow,
|
||||
ServiceCatalog,
|
||||
TriggerField,
|
||||
WebhookCall,
|
||||
} from '@/types/dashboard';
|
||||
|
||||
/** Kam se ma vlozit dalsi krok - null znamena, ze vyber neni otevreny. */
|
||||
@@ -442,6 +443,10 @@ export default function AutomationDetail() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{flow.trigger?.serviceId === 'webhook' && (
|
||||
<WebhookCalls calls={automation.data?.recentCalls ?? []} />
|
||||
)}
|
||||
|
||||
<p className="flex gap-2 rounded-xl border border-ink-600/60 px-4 py-3 text-xs leading-relaxed text-white/40">
|
||||
<Info className="mt-0.5 size-3.5 shrink-0" />
|
||||
Prototyp: nastavení jednotlivých polí kroku (mapování dat, filtry) zatím
|
||||
@@ -467,6 +472,55 @@ export default function AutomationDetail() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poslednich deset volani webhooku.
|
||||
*
|
||||
* Odmitnute volani do ted skoncilo jako radek v logu kontejneru. Odesilatel
|
||||
* dostal 400 a vedel o tom, ale ten, kdo kontrakt psal, se nedozvedel nic -
|
||||
* automatizace svitila zelene a jen do ni prestalo chodit. Tohle je to nejmensi,
|
||||
* co staci: kdyz se implementator ozve, ze mu neco nesedi, je tady videt co.
|
||||
*
|
||||
* Telo se neuklada, jen duvod odmitnuti. Ten uz rika, co je spatne.
|
||||
*/
|
||||
function WebhookCalls({ calls }: { calls: WebhookCall[] }) {
|
||||
return (
|
||||
<div className="glass rounded-card p-5">
|
||||
<h2 className="font-semibold text-white">Poslední volání</h2>
|
||||
|
||||
{calls.length === 0 ? (
|
||||
<p className="mt-3 text-sm text-white/40">
|
||||
Zatím nic nepřišlo. Objeví se tu deset posledních volání včetně těch,
|
||||
která jsme odmítli.
|
||||
</p>
|
||||
) : (
|
||||
<ul className="mt-3 space-y-2 text-sm">
|
||||
{calls.map((call) => (
|
||||
<li key={`${call.at}-${call.runId ?? 'x'}`} className="flex gap-2">
|
||||
{call.ok ? (
|
||||
<Check className="mt-0.5 size-3.5 shrink-0 text-emerald-400" />
|
||||
) : (
|
||||
<AlertCircle className="mt-0.5 size-3.5 shrink-0 text-rose-400" />
|
||||
)}
|
||||
<div className="min-w-0">
|
||||
<span className="text-white/70">{formatDateTime(call.at)}</span>
|
||||
{call.problems.length > 0 && (
|
||||
<p className="text-xs leading-relaxed text-rose-300/80">
|
||||
{call.problems.join(' ')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
<p className="mt-3 text-xs text-white/35">
|
||||
Drží se v paměti, restart je zahodí. Na historii jsou běhy.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Row({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div className="flex justify-between gap-3">
|
||||
|
||||
@@ -506,8 +506,18 @@ export interface ServiceCatalog {
|
||||
*/
|
||||
export type FieldType = 'string' | 'number' | 'boolean' | 'date' | 'object' | 'list';
|
||||
|
||||
/** Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece. */
|
||||
export const declarableFieldTypes = ['string', 'number', 'boolean', 'date'] as const;
|
||||
/**
|
||||
* Typy, ktere si uzivatel muze zvolit u vlastniho parametru spoustece.
|
||||
* Objekt a seznam jsou mezi nimi kvuli telu, kde `data` prijde jako objekt.
|
||||
*/
|
||||
export const declarableFieldTypes = [
|
||||
'string',
|
||||
'number',
|
||||
'boolean',
|
||||
'date',
|
||||
'object',
|
||||
'list',
|
||||
] as const;
|
||||
|
||||
export type ConditionOperator =
|
||||
| 'eq'
|
||||
@@ -599,10 +609,20 @@ export interface AutomationFlow {
|
||||
steps: FlowStep[];
|
||||
}
|
||||
|
||||
/** Jedno volani webhooku, jak dopadlo. Telo se neuklada, jen duvod odmitnuti. */
|
||||
export interface WebhookCall {
|
||||
at: string;
|
||||
ok: boolean;
|
||||
problems: string[];
|
||||
runId: string | null;
|
||||
}
|
||||
|
||||
export interface AutomationDetail extends Automation {
|
||||
flow: AutomationFlow;
|
||||
/** Model prichozich dat z ukazky u spoustece. Pocita server pri cteni. */
|
||||
model: ModelNode[];
|
||||
/** Poslednich deset volani webhooku, nejnovejsi prvni. Drzi se v pameti. */
|
||||
recentCalls: WebhookCall[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user