Detail ticketu ma rozradovace, ne tri karty pod sebou
Navrh v documentation/25 mel detail jako jednu slozku s rozradovaci, ale v aplikaci zustaly tri karty pod sebou: obsah, prichozi udalosti a log prubehu. Detail byl tri obrazovky dlouhy a to podstatne, tedy obsah pozadavku, se v tom ztracelo. Ted jsou to listy jedne karty: - Pozadavek - obsah a pole na komentar, vychozi - Log prubehu - co delala automatizace a co ji sluzby vratily - Udalosti - co presne prislo zvenku - Vlastni pole - hodnoty poli typu ticketu U kazdeho je pocet, takze je videt, jestli se tam vubec vyplati kliknout. Vlastni pole se nenabizeji, kdyz zadna nejsou - prazdny rozradovac je jen dalsi vec k prehlednuti. Vychozi je vzdycky Pozadavek: to je duvod, proc clovek ticket otevrel. Log a udalosti jsou hloubkova data, na ta se sahá, az kdyz neco nesedi. Vlastni pole se do te doby nedala zobrazit vubec, i kdyz je ticket nesl - to je ten ctvrty rozradovac. Kresli je tatáz tabulka jako obsah ticketu (DataTable v TicketBody.tsx), takze se struktura vsude vypisuje stejne. Rozradovace pouzivaji tentyz vzhled zalozek jako zbytek portalu, ne tvar slozky z navrhu. Druhy styl zalozek na jedne obrazovce je presne ten drift, ktery jsme uklizeli u vstupnich poli. Overeno na bezici instanci: ticket z automatizace ma 5 kroku logu, 2 udalosti a zadna vlastni pole, takze se ctvrty rozradovac skryje. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6bcd7f845c
commit
2a310137a2
@@ -2,6 +2,37 @@
|
||||
|
||||
Nejnovejsi nahore.
|
||||
|
||||
## 2026-09-02 - Detail ticketu ma rozradovace, ne tri karty pod sebou
|
||||
|
||||
Navrh v [25](25-navrh-pristupny-portal.md) mel detail jako jednu slozku
|
||||
s rozradovaci, ale v aplikaci zustaly tri karty pod sebou: obsah, prichozi
|
||||
udalosti a log prubehu. Detail byl tri obrazovky dlouhy a to podstatne, tedy
|
||||
obsah pozadavku, se v tom ztracelo.
|
||||
|
||||
Ted jsou to listy jedne karty:
|
||||
|
||||
| Rozradovac | Co je na nem |
|
||||
| ------------ | ------------------------------------------------- |
|
||||
| Požadavek | obsah a pole na komentar, vychozi |
|
||||
| Log průběhu | co delala automatizace a co ji sluzby vratily |
|
||||
| Události | co presne prislo zvenku |
|
||||
| Vlastní pole | hodnoty poli typu ticketu |
|
||||
|
||||
U kazdeho je pocet, takze je videt, jestli se tam vubec vyplati kliknout.
|
||||
**Vlastni pole se nenabizeji, kdyz zadna nejsou** - prazdny rozradovac je jen
|
||||
dalsi vec k prehlednuti.
|
||||
|
||||
Vychozi je vzdycky Pozadavek: to je duvod, proc clovek ticket otevrel. Log
|
||||
a udalosti jsou hloubkova data, na ta se sahá, az kdyz neco nesedi.
|
||||
|
||||
Vlastni pole se do te doby nedala zobrazit vubec, i kdyz je ticket nesl - to
|
||||
je ten ctvrty rozradovac. Kresli je tatáz tabulka jako obsah ticketu
|
||||
(`DataTable` v `TicketBody.tsx`), takze se struktura vsude vypisuje stejne.
|
||||
|
||||
Rozradovace pouzivaji **tentyz vzhled zalozek jako zbytek portalu**, ne tvar
|
||||
slozky z navrhu. Druhy styl zalozek na jedne obrazovce je presne ten drift,
|
||||
ktery jsme uklizeli u vstupnich poli.
|
||||
|
||||
## 2026-09-02 - Hierarchie firmy: kdo co vidi, a zalozka Firma
|
||||
|
||||
Pohled na celou firmu dostal kazdy, kdo do ni patril: `scopes.push('tenant')`
|
||||
|
||||
@@ -113,7 +113,7 @@ export function TicketBody({ body, sourceRef }: { body: string; sourceRef?: stri
|
||||
* porovnavaji mezi sebou. Vsechno ostatni je dvojice klic a hodnota - u jedne
|
||||
* struktury nikdo nesrovnava sloupce, cte se shora dolu.
|
||||
*/
|
||||
function DataTable({ value }: { value: unknown }) {
|
||||
export function DataTable({ value }: { value: unknown }) {
|
||||
if (Array.isArray(value) && value.length > 0 && value.every(isPlainObject)) {
|
||||
const columns = [...new Set(value.flatMap((row) => Object.keys(row)))];
|
||||
return (
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TicketStatusBadge,
|
||||
} from '@/components/dashboard/StatusBadge';
|
||||
import { TicketActions } from '@/components/dashboard/TicketActions';
|
||||
import { TicketBody } from '@/components/dashboard/TicketBody';
|
||||
import { DataTable, TicketBody } from '@/components/dashboard/TicketBody';
|
||||
import { TicketEvents } from '@/components/dashboard/TicketEvents';
|
||||
import { TicketTrace, TraceSummary } from '@/components/dashboard/TicketTrace';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
@@ -98,6 +98,34 @@ export default function TicketDetail() {
|
||||
*/
|
||||
const [statusDraft, setStatusDraft] = useState<string | null>(null);
|
||||
|
||||
/**
|
||||
* Ktery list detailu je navrchu.
|
||||
*
|
||||
* Vychozi je vzdycky pozadavek: to je duvod, proc clovek ticket otevrel.
|
||||
* Log a udalosti jsou hloubkova data, na ta se sahá, az kdyz neco nesedi.
|
||||
*/
|
||||
const [activeSheet, setSheet] = useState('pozadavek');
|
||||
|
||||
const fieldCount = Object.keys(ticket.data?.fields ?? {}).length;
|
||||
|
||||
/** Rozradovace. Vlastni pole se nenabizeji, kdyz zadna nejsou. */
|
||||
const sheets = [
|
||||
{ key: 'pozadavek', label: 'Požadavek', count: undefined, show: true },
|
||||
{
|
||||
key: 'log',
|
||||
label: 'Log průběhu',
|
||||
count: ticket.data?.trace.length ?? 0,
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
key: 'udalosti',
|
||||
label: 'Události',
|
||||
count: ticket.data?.events.length ?? 0,
|
||||
show: true,
|
||||
},
|
||||
{ key: 'pole', label: 'Vlastní pole', count: fieldCount, show: fieldCount > 0 },
|
||||
].filter((sheet) => sheet.show);
|
||||
|
||||
usePageMeta({ title: `${ticket.data ? ticket.data.id : 'Ticket'}`, area: 'portal' });
|
||||
|
||||
// Po zmene ticketu zmizi stara chyba, at nevisi u uz opraveneho stavu.
|
||||
@@ -194,74 +222,126 @@ export default function TicketDetail() {
|
||||
<TicketActions ticket={ticket.data} onDone={ticket.reload} />
|
||||
|
||||
<div className="grid gap-6 xl:grid-cols-[1fr_20rem]">
|
||||
<div className="space-y-6">
|
||||
<section className="glass rounded-card p-5 sm:p-6">
|
||||
<h2 className="flex items-center gap-2 font-semibold text-white">
|
||||
<FileText className="size-4 text-brand-300" />
|
||||
Obsah požadavku
|
||||
</h2>
|
||||
<div className="mt-3">
|
||||
<TicketBody body={ticket.data.body} sourceRef={ticket.data.sourceRef} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="glass rounded-card p-5 sm:p-6">
|
||||
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<h2 className="flex items-center gap-2 font-semibold text-white">
|
||||
<Inbox className="size-4 text-brand-300" />
|
||||
Příchozí události
|
||||
</h2>
|
||||
<span className="text-xs text-white/35">
|
||||
{ticket.data.events.length}
|
||||
{ticket.data.externalId
|
||||
? ` k externímu ID ${ticket.data.externalId}`
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
<TicketEvents events={ticket.data.events} />
|
||||
</section>
|
||||
|
||||
<section className="glass rounded-card p-5 sm:p-6">
|
||||
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<h2 className="flex items-center gap-2 font-semibold text-white">
|
||||
<ScrollText className="size-4 text-brand-300" />
|
||||
Průběh a log
|
||||
</h2>
|
||||
<TraceSummary entries={ticket.data.trace} />
|
||||
{/*
|
||||
Rozradovace misto tri karet pod sebou. Log prubehu a prichozi
|
||||
udalosti jsou hloubkova data: dulezita, ale ne pokazde. Kdyz
|
||||
stály na strance vsechny naraz, byl detail tri obrazovky dlouhy
|
||||
a to podstatne, tedy obsah pozadavku, se v tom ztracelo.
|
||||
*/}
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{sheets.map((sheet) => (
|
||||
<button
|
||||
key={sheet.key}
|
||||
type="button"
|
||||
onClick={() => setSheet(sheet.key)}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-lg px-3 py-1.5 text-sm font-medium transition-colors',
|
||||
sheet.key === activeSheet
|
||||
? 'bg-brand-500/15 text-brand-100'
|
||||
: 'text-white/50 hover:text-white',
|
||||
)}
|
||||
>
|
||||
{sheet.label}
|
||||
{sheet.count !== undefined && (
|
||||
<span className="rounded-full bg-white/8 px-1.5 text-xs text-white/50">
|
||||
{sheet.count}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<TicketTrace
|
||||
entries={ticket.data.trace}
|
||||
services={catalog.data?.items ?? []}
|
||||
/>
|
||||
<section className="glass rounded-card p-5 sm:p-6">
|
||||
{activeSheet === 'pozadavek' && (
|
||||
<>
|
||||
<h2 className="flex items-center gap-2 font-semibold text-white">
|
||||
<FileText className="size-4 text-brand-300" />
|
||||
Obsah požadavku
|
||||
</h2>
|
||||
<div className="mt-3">
|
||||
<TicketBody body={ticket.data.body} sourceRef={ticket.data.sourceRef} />
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={(event) => void submitComment(event)}
|
||||
className="mt-5 border-t border-ink-600/50 pt-5"
|
||||
>
|
||||
<label
|
||||
htmlFor="ticket-comment"
|
||||
className="text-xs font-semibold tracking-wide text-white/45 uppercase"
|
||||
>
|
||||
Přidat komentář
|
||||
</label>
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
<input
|
||||
id="ticket-comment"
|
||||
value={comment}
|
||||
onChange={(event) => setComment(event.target.value)}
|
||||
placeholder="Co jste zjistili nebo udělali..."
|
||||
className="min-w-0 flex-1 rounded-xl border border-ink-600/70 bg-ink-850/70 px-3.5 py-2.5 text-sm text-white placeholder:text-white/30 focus:border-brand-400/70 focus:outline-none"
|
||||
/>
|
||||
<Button type="submit" size="sm" disabled={busy || comment.trim().length < 2}>
|
||||
<MessageSquarePlus className="size-4" />
|
||||
Zapsat
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-white/35">
|
||||
Komentář se zapíše do stejné časové osy jako běh automatizace.
|
||||
</p>
|
||||
</form>
|
||||
<form
|
||||
onSubmit={(event) => void submitComment(event)}
|
||||
className="mt-5 border-t border-ink-600/50 pt-5"
|
||||
>
|
||||
<label
|
||||
htmlFor="ticket-comment"
|
||||
className="text-xs font-semibold tracking-wide text-white/45 uppercase"
|
||||
>
|
||||
Přidat komentář
|
||||
</label>
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
<input
|
||||
id="ticket-comment"
|
||||
value={comment}
|
||||
onChange={(event) => setComment(event.target.value)}
|
||||
placeholder="Co jste zjistili nebo udělali..."
|
||||
className="min-w-0 flex-1 rounded-xl border border-ink-600/70 bg-ink-850/70 px-3.5 py-2.5 text-sm text-white placeholder:text-white/30 focus:border-brand-400/70 focus:outline-none"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
size="sm"
|
||||
disabled={busy || comment.trim().length < 2}
|
||||
>
|
||||
<MessageSquarePlus className="size-4" />
|
||||
Zapsat
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-white/35">
|
||||
Komentář se zapíše do stejné časové osy jako běh automatizace.
|
||||
</p>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeSheet === 'log' && (
|
||||
<>
|
||||
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<h2 className="flex items-center gap-2 font-semibold text-white">
|
||||
<ScrollText className="size-4 text-brand-300" />
|
||||
Průběh a log
|
||||
</h2>
|
||||
<TraceSummary entries={ticket.data.trace} />
|
||||
</div>
|
||||
<TicketTrace
|
||||
entries={ticket.data.trace}
|
||||
services={catalog.data?.items ?? []}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeSheet === 'udalosti' && (
|
||||
<>
|
||||
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<h2 className="flex items-center gap-2 font-semibold text-white">
|
||||
<Inbox className="size-4 text-brand-300" />
|
||||
Příchozí události
|
||||
</h2>
|
||||
<span className="text-xs text-white/35">
|
||||
{ticket.data.externalId
|
||||
? `k externímu ID ${ticket.data.externalId}`
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
<TicketEvents events={ticket.data.events} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeSheet === 'pole' && (
|
||||
<>
|
||||
<h2 className="flex items-center gap-2 font-semibold text-white">
|
||||
<Inbox className="size-4 text-brand-300" />
|
||||
Vlastní pole typu
|
||||
</h2>
|
||||
<p className="mt-1 mb-3 text-xs text-white/35">
|
||||
Klíče se sčítají, další zpráva o téže věci je jen doplní.
|
||||
</p>
|
||||
<DataTable value={ticket.data.fields} />
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user