Lattice Grid Buy a licence

demo D263

Starter kit: service desk

One ticket stream, a live lane per queue and an open-tickets chart, with no backend

createDataRouter · MockWebSocket

Building…
Loading a live grid…

The configuration

'starter-service-desk': () => ({
  rows: [],
  config: {},
  foot: [
    'one ticket stream, a live lane per queue',
    'a resolved ticket leaves its lane; a new one drops in with no refresh',
    'the mock socket runs it with no backend; one line points it at a real feed',
  ],
  mount: (el: HTMLElement, LG: any) => {
    const cols = [
      { field: 'id', title: 'Ticket', layout: { width: 92 } },
      { field: 'subject', title: 'Subject', layout: { flex: 1, min: 120 } },
      { field: 'priority', title: 'Priority', filter: { type: 'set' } },
      { field: 'agent', title: 'Agent', filter: { type: 'set' } },
      { field: 'waitMins', title: 'Wait', type: 'number', total: 'max' },
    ];
    const strip = document.createElement('div');
    strip.style.cssText = 'display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px';
    el.append(strip);
    const billingEl = panel(strip, 'Billing', 320);
    const technicalEl = panel(strip, 'Technical', 320);
    const onboardingEl = panel(strip, 'Onboarding', 320);
    const chartEl = chartHost(el);
    const mk = (host: HTMLElement) => LG.createGrid(host, {
      rowKey: 'id', theme: 'light', selection: 'single', highlightOnChange: { duration: 400 }, columns: cols,
    });
    const billing = mk(billingEl), technical = mk(technicalEl), onboarding = mk(onboardingEl);
    const metrics = LG.createHeadlessGrid({
      rowKey: 't', columns: [{ field: 't', type: 'number' }, { field: 'open', type: 'number' }],
    });
    let chart: any, socket: any;
    Promise.all([loadDataRouter(), loadCharts(), loadMockSocket()])
      .then(([dr, ch, ms]: any[]) => {
        const rng = ms.rng;
        function* deskFeed(seed: number) {
          const rand = rng(seed);
          const QUEUES = ['billing', 'technical', 'onboarding'];
          const PRIORITIES = ['low', 'normal', 'high', 'urgent'];
          const AGENTS = ['Ada', 'Boris', 'Chen', 'Dara', 'Ege', 'Faye'];
          const SUBJECTS: Record<string, string[]> = {
            billing: ['Refund request', 'Invoice query', 'Card declined', 'Plan change', 'Duplicate charge'],
            technical: ['Login fails', 'Export timeout', 'API 500', 'Slow dashboard', 'Sync stuck'],
            onboarding: ['SSO setup', 'Import data', 'Invite team', 'Configure roles', 'First report'],
          };
          const pick = (list: string[]) => list[Math.floor(rand() * list.length)];
          let seq = 0, t = 0;
          const open: Record<string, string[]> = { billing: [], technical: [], onboarding: [] };
          const ticket = (queue: string) => {
            const id = 'T-' + (1000 + seq++);
            open[queue].push(id);
            return { id, queue, subject: pick(SUBJECTS[queue]), priority: pick(PRIORITIES), status: 'open', agent: pick(AGENTS), waitMins: Math.round(rand() * 90) };
          };
          const totalOpen = () => QUEUES.reduce((n, q) => n + open[q].length, 0);
          const metric = () => ({ id: 'M-' + t, queue: 'metric', t, open: totalOpen() });
          const rows: any[] = [];
          QUEUES.forEach((q) => { for (let i = 0; i < 8; i++) rows.push(ticket(q)); });
          rows.push(metric());
          yield { kind: 'snapshot', rows };
          for (;;) {
            const changes: any[] = [];
            const n = 1 + Math.floor(rand() * 3);
            for (let i = 0; i < n; i++) changes.push({ op: 'upsert', row: ticket(pick(QUEUES)) });
            const q = pick(QUEUES);
            if (rand() < 0.6 && open[q].length) {
              const id = open[q].shift()!;
              changes.push({ op: 'upsert', row: { id, queue: q, subject: 'Resolved', priority: 'normal', status: 'resolved', agent: pick(AGENTS), waitMins: 0 } });
            }
            t++;
            changes.push({ op: 'upsert', row: metric() });
            yield { kind: 'delta', changes };
          }
        }
        const router = dr.createDataRouter({ key: 'queue', rowKey: 'id' });
        router.attach(billing, 'billing');
        router.attach(technical, 'technical');
        router.attach(onboarding, 'onboarding');
        router.attach(metrics, 'metric', { rowKey: 't' });
        chart = ch.createChart({ grid: metrics, container: chartEl, type: 'area', x: 't', y: 'open', title: 'Open tickets across the desk' });
        socket = new ms.MockWebSocket({ feed: deskFeed(5), rate: 1100, jitter: 300 });
        pipe(socket, router);
      })
      .catch((err) => console.error('[starter-service-desk]', err));
    return () => {
      socket?.close?.();
      chart?.destroy?.();
      billing?.destroy?.(); technical?.destroy?.(); onboarding?.destroy?.(); metrics?.destroy?.();
    };
  },
})