Gestione dei servizi · Nuova risorsa

Sistema di progettazione ServiceTitan Anvil.

Rendi concreto Anvil2 con il suo modello di token a tre livelli, valori dei ruoli verificati per temi chiari e scuri e un’interazione completa per l’assegnazione dei servizi, con stati sincronizzati di tecnico, orario e revisione.

Guida OpenDesign7 capitoli38 voci di riferimentoGuida applicativa originale
01

Anteprima dell’interfaccia del sistema di progettazione sistema di design ServiceTitan Anvil

Cambia l’aspetto chiaro/scuro, seleziona il personale disponibile ed esamina un piano locale sincronizzato.

Assegna un tecnico a un intervento di assistenzaApri l’esempio completo
HTML originale con valori di ruolo dei token Hammer 3.5.0. · Riferimento per l’implementazione
02

Riferimento di progettazione del sistema di progettazione sistema di design ServiceTitan Anvil

Esportazioni del tema principale di @servicetitan/hammer-token 3.5.0 e documentazione sulla tematizzazione di Anvil2.

Coppie semantiche per modalità chiara e scura

Fonte
Nome / regolaValore / configurazioneUtilizzo
BackgroundColorDefaultlight #ffffff / dark #1a1a1aSuperficie principale dell'applicazione.
BackgroundColorDefaultHoverlight #f9f9f9 / dark #292929Superficie al passaggio del puntatore.
BackgroundColorPrimarylight #0265dc / dark #70b1ffRiempimento dell’azione primaria.
BackgroundColorPrimaryHoverlight #1d4ca3 / dark #9ecaffRuolo nativo per lo stato hover primario.
BackgroundColorPrimaryFaintlight #fafcff / dark #000b19Superficie selezionata discreta.
ForegroundColorSubduedlight #707070 / dark #c4c4c4Testo di supporto.
ForegroundColorOnPrimarylight #ffffff / dark #1a1a1aTesto abbinato al riempimento primario.
ForegroundColorPrimarylight #0265dc / dark #70b1ffPrimo piano interattivo.
BorderColorSubduedlight #e6e6e6 / dark #404040Bordi poco accentuati.
BorderColorPrimarylight #0265dc / dark #9ecaffBordo interattivo.
StatusColorInfolight #0265dc / dark #70b1ffStato informativo.
StatusColorDangerlight #d62100 / dark #ff5b3dAttenzione immediata.
StatusColorSuccesslight #077e50 / dark #0bd085Revisione locale completata.
StatusColorWarninglight #ffbe00 / dark #ffe278Avvertenza non critica.

Geometria primitiva e valori adattivi

Fonte
Nome / regolaValore / configurazioneUtilizzo
BreakpointSm640pxPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
BreakpointMd768pxPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
BreakpointLg1024pxPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
BreakpointXl1280pxPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
BreakpointXxl1536pxPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
DurationDefault200msPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
DurationInstant0msPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
DurationFast100msPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
DurationSlow300msPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius00remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius10.1875remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius20.375remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius30.5625remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius40.75remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius50.9375remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius61.125remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius71.3125remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.
Radius81.5remPrimitiva pubblicata; gli alias semantici e dei componenti determinano i ruoli effettivamente applicati.

Contratto di override di ThemeProvider

Fonte
Nome / regolaValore / configurazioneUtilizzo
theme.semanticsemantic token overridesModifica un ruolo come BackgroundColorPrimary.
theme.componentcomponent token overridesRestringi la modifica a un ruolo del componente, ad esempio ButtonPrimaryBackgroundColor.
valuedefault/light token valueFornisci il valore per l’aspetto chiaro.
extensions.appearance.dark.valuedark appearance valueSpecifica il valore corrispondente per l’aspetto scuro.
CSS output--a2-<kebab-case-name>ThemeProvider serializza la chiave del token in una variabile CSS.
Primitive cascaderesolved at build timeL’override di una primitiva durante l’esecuzione non ricalcola gli alias semantici.
03

Guida completa del sistema di progettazione sistema di design ServiceTitan Anvil

Una guida pratica per scegliere, applicare e valutare questo sistema in un’interfaccia reale.

DESIGN-servicetitan-anvil.md

Indicazioni complete, un punto di partenza per l’implementazione e link alle fonti.

Scarica la guida

Architettura dei token a tre livelli del sistema di progettazione sistema di design ServiceTitan Anvil

Mantieni le primitive al di sotto dei ruoli semantici

Anvil2 separa le primitive grezze, i ruoli semantici e gli alias specifici dei componenti. In genere, un prodotto dovrebbe scegliere il ruolo che descrive il significato nell’interfaccia, invece di copiare una primitiva blu in ogni controllo. La tabella di riferimento include i valori esportati esatti e i corrispondenti valori per la modalità scura, così è possibile esaminare le due varianti insieme quando si cambia tema.

Da questa guida su OpenDesign

Non aspettarti che gli alias vengano ricalcolati durante l’esecuzione

La guida ufficiale alla tematizzazione specifica che i riferimenti alle primitive vengono risolti durante la compilazione. La modifica di una primitiva tramite un provider durante l’esecuzione non rigenera automaticamente il livello semantico. Per cambiare i colori dell’applicazione, sovrascrivi il ruolo semantico pertinente; per modificare un singolo componente, usa il livello dei componenti. In questo modo si evita un tema modificato solo in parte, con controlli correlati incoerenti.

Aspetto chiaro e scuro del sistema di progettazione sistema di design ServiceTitan Anvil

Abbina le modifiche a testo e sfondo

L’azione primaria usa BackgroundColorPrimary con ForegroundColorOnPrimary. Nell’aspetto chiaro è blu con testo bianco; in quello scuro è blu più chiaro con un’etichetta scura. Cambiare solo il riempimento creerebbe una combinazione incoerente. Lo studio completo HTML usa light-dark() con un selettore color-scheme per mostrare questi valori senza dichiarare di essere ThemeProvider.

Usa le tonalità di stato con un significato esplicito

Anvil2 assegna significati di stato alle famiglie di colori. La revisione locale usa un ruolo di successo, ma specifica comunque con chiarezza cosa è accaduto. Il tecnico non disponibile è indicato con un’etichetta testuale e un pulsante di opzione disabilitato. Nessuna tonalità di verde suggerisce che un intervento sia stato assegnato: l’esempio non si connette mai a un backend di assegnazione.

Assegnazione dei servizi e stati nel sistema di progettazione sistema di design ServiceTitan Anvil

Fai in modo che la disponibilità determini la scelta successiva

Selezionando un tecnico, le opzioni di orario vengono sostituite con la disponibilità fittizia di quella persona. La persona non disponibile rimane visibile, ma non può essere selezionata. Tra i due controlli esiste una dipendenza: quando si cambia il primo, il secondo deve essere reimpostato su un valore valido, anziché mantenere un orario che non corrisponde alla persona scelta.

Separa la pianificazione dall’esecuzione

La revisione dell’assegnazione registra un piano locale e specifica chiaramente che non è stato assegnato alcun intervento. Un flusso di lavoro di produzione richiederebbe controlli delle autorizzazioni, una risposta aggiornata sulla disponibilità e la gestione dei conflitti prima della conferma. L’esempio rende evidente questo limite, offrendo comunque un’interazione completa da provare e adattare.

Densità operativa e leggibilità del sistema di progettazione sistema di design ServiceTitan Anvil

Mantieni il contesto dell’intervento vicino alla selezione

Un riepilogo compatto dell’intervento include attività, durata, zona e priorità. Le opzioni dei tecnici sono disposte in un elenco verticale leggibile e la visita proposta è presentata in un riquadro separato. L’interfaccia usa bordi neutri e colori d’accento discreti, così la selezione e l’azione principale restano facilmente individuabili. Le barre del calendario sono indicate come illustrative; il menu a discesa per l’orario è il riferimento effettivo.

Mantieni l’ordine di lettura sui dispositivi mobili

Integrazione nativa del sistema di progettazione sistema di design ServiceTitan Anvil e alternativa HTML completa

Usa la struttura documentata del provider

ThemeProvider di Anvil2 accetta override dei token semantici e dei componenti, con estensioni per valore e aspetto scuro. La guida include qui sotto un esempio completo del provider, tratto dalla documentazione pubblica. È un riferimento per l’integrazione nativa, distinto dal componente dimostrativo indipendente HTML eseguito localmente.

Non dichiarare un ambiente di esecuzione del pacchetto non verificato

Questa pagina fornisce un’implementazione HTML funzionante e dati dei token verificati. L’esempio del provider nativo è un riferimento d’integrazione separato e non corrisponde all’ambiente di esecuzione mostrato nell’anteprima. Quando adotti i componenti React, installa una versione di Anvil2 compatibile, includi gli stili richiesti e verifica le API dei componenti prima di sostituire i controlli HTML nativi.

Accessibilità e verifica degli stati del sistema di progettazione sistema di design ServiceTitan Anvil

Usa la semantica nativa dei moduli

I tecnici sono rappresentati da pulsanti di opzione nativi con etichetta, l’orario di inizio è un menu a discesa con etichetta e il controllo dell’aspetto espone aria-pressed. Il messaggio di revisione è un’area di stato. La gestione nativa della tastiera del browser consente di selezionare i pulsanti di opzione; lo stile di messa a fuoco visibile è indipendente da quello di selezione o hover.

Verifica le modifiche dopo la conferma

Cambiare tecnico o orario cancella i messaggi di revisione precedenti. Cambiare aspetto mantiene il tecnico e l’orario selezionati, perché una modifica visiva del tema non dovrebbe cancellare un piano. I test dovrebbero coprire entrambe le transizioni, oltre alla selezione disabilitata e agli overflow sugli schermi stretti, anziché verificare solo che sia possibile fare clic su un pulsante.

Passaggio di consegne all’agente e implementazione del sistema di progettazione sistema di design ServiceTitan Anvil

Inizia dal modello completo degli stati

Salva ed esegui l’esempio HTML completo per esaminarne il comportamento. Le coppie di valori chiaro/scuro derivate dai token sono visibili nella parte superiore di CSS e i dati di disponibilità definiti nell’esempio sono visibili nello script. Un’implementazione nativa di React può mantenere le stesse transizioni di stato sostituendo i controlli con componenti Anvil2 verificati.

  1. Esegui l’esempio completo del sistema di progettazione HTML.
  2. Seleziona il secondo tecnico e verifica che cambino le opzioni dell’orario di inizio.
  3. Esamina il piano, poi cambia l’orario e verifica che i messaggi vengano cancellati.
  4. Cambia aspetto e verifica che la selezione corrente venga mantenuta.

Specifica quali dati richiedono una fonte reale

Chiedi all’agente di mantenere l’elenco degli orari dipendente, il membro dello staff disabilitato, l’invalidazione della revisione e il comportamento dell’aspetto. Sostituisci l’incarico e il membro dello staff fittizi solo quando è disponibile un contratto dati reale. Non trasformare la revisione locale in un messaggio di successo limitandoti a cambiare l’etichetta del pulsante.

  • Verifica la disponibilità prima di un intervento reale.
  • Mantieni separati gli override semantici e quelli dei componenti.
  • Verifica entrambe le combinazioni di primo piano e sfondo, in modalità chiara e scura.

ServiceTitan Anvil Esempi di componenti del sistema di design

Esempio completo di assegnazione locale

Originale HTML/CSS/JavaScript, con aspetti derivati dai token e stato del modulo dipendente.

html
<!doctype html>
<html lang="en">
  <meta charset="utf-8" /><meta
    name="viewport"
    content="width=device-width,initial-scale=1"
  /><title>Dispatch assignment study</title
  ><style>
    :root {
      color-scheme: light;
      --surface: light-dark(#fff, #1a1a1a);
      --hover: light-dark(#f9f9f9, #292929);
      --text: light-dark(#040404, #fff);
      --muted: light-dark(#707070, #c4c4c4);
      --line: light-dark(#e6e6e6, #404040);
      --action: light-dark(#0265dc, #70b1ff);
      --on-action: light-dark(#fff, #1a1a1a);
      --faint: light-dark(#fafcff, #000b19);
      --success: light-dark(#077e50, #0bd085);
    }
    * {
      box-sizing: border-box;
    }
    body {
      margin: 0;
      padding: 24px;
      background: var(--surface);
      color: var(--text);
      font:
        16px/1.5 system-ui,
        sans-serif;
    }
    h1 {
      font-size: 28px;
      margin: 8px 0;
    }
    h2 {
      font-size: 20px;
      margin: 0 0 16px;
    }
    p {
      margin: 8px 0 16px;
    }
    .small {
      font-size: 13px;
      color: var(--muted);
    }
    header {
      display: flex;
      justify-content: space-between;
      gap: 24px;
      border-bottom: 1px solid var(--line);
      padding-bottom: 24px;
    }
    button,
    select {
      font: inherit;
      color: inherit;
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: 4px;
      padding: 10px 16px;
      min-height: 44px;
    }
    button {
      cursor: pointer;
    }
    button:hover {
      background: var(--hover);
    }
    button:focus-visible,
    select:focus-visible,
    input:focus-visible {
      outline: 3px solid var(--action);
      outline-offset: 3px;
    }
    main {
      display: grid;
      grid-template-columns: 1.4fr 1fr;
      gap: 24px;
      margin-top: 24px;
    }
    .board {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 24px;
    }
    .job {
      padding: 16px;
      background: var(--faint);
      border-left: 4px solid var(--action);
      margin-bottom: 24px;
    }
    .job strong {
      display: block;
    }
    .people {
      display: grid;
      gap: 12px;
    }
    .person {
      display: grid;
      grid-template-columns: 24px 1fr auto;
      gap: 12px;
      padding: 16px;
      border: 1px solid var(--line);
      border-radius: 4px;
      align-items: center;
    }
    .person:has(:checked) {
      border-color: var(--action);
      background: var(--faint);
    }
    input {
      accent-color: var(--action);
      width: 18px;
      height: 18px;
    }
    .person small {
      display: block;
      color: var(--muted);
    }
    .person:has(:disabled) {
      background: var(--hover);
    }
    .timeline {
      display: flex;
      gap: 4px;
      margin: 20px 0;
    }
    .timeline span {
      height: 40px;
      flex: 1;
      background: var(--line);
      border-radius: 4px;
    }
    .timeline .open {
      background: var(--action);
    }
    .timeline-label {
      display: flex;
      justify-content: space-between;
      font-size: 13px;
      color: var(--muted);
    }
    label[for="time"] {
      display: block;
      margin: 16px 0 8px;
    }
    .primary {
      background: var(--action);
      color: var(--on-action);
      border-color: var(--action);
      margin-top: 24px;
    }
    .primary:hover {
      filter: brightness(0.95);
      background: var(--action);
    }
    #feedback {
      color: var(--success);
      padding-top: 16px;
    }
    footer {
      border-top: 1px solid var(--line);
      padding-top: 16px;
      margin-top: 24px;
      font-size: 13px;
      color: var(--muted);
    }
    @media (max-width: 767px) {
      body {
        padding: 16px;
      }
      main {
        grid-template-columns: 1fr;
      }
      .board {
        padding: 16px;
      }
      header {
        display: block;
      }
      header button {
        margin-top: 8px;
      }
      .person {
        grid-template-columns: 24px 1fr;
      }
      .person > span:last-child {
        grid-column: 2;
      }
    }
  </style>
  <body>
    <header>
      <div>
        <div class="small">SERVICE OPERATIONS / LOCAL PLANNING STUDY</div>
        <h1>Assign a service visit</h1>
        <p>Match a sample job with an available technician.</p>
      </div>
      <div>
        <button id="theme" aria-pressed="false">Dark appearance</button>
      </div>
    </header>
    <main>
      <section class="board">
        <h2>Job and technician</h2>
        <div class="job">
          <strong>Sample job #204 · Heating inspection</strong
          ><span>90 minutes · North district · Routine priority</span>
        </div>
        <div class="people" id="people"></div>
      </section>
      <aside class="board">
        <h2>Proposed visit</h2>
        <p id="technician"></p>
        <label for="time">Available start time</label
        ><select id="time"></select>
        <div class="timeline" aria-hidden="true">
          <span></span><span class="open"></span><span class="open"></span
          ><span></span><span></span><span></span>
        </div>
        <div class="timeline-label">
          <span>08:00</span><span>Illustrative day</span><span>17:00</span>
        </div>
        <p class="small">
          The time list is the source of available choices. The bars are a
          decorative schedule summary.
        </p>
        <button id="assign" class="primary">Review assignment</button>
        <p id="feedback" role="status"></p>
      </aside>
    </main>
    <footer>
      Original HTML using Anvil2 Hammer token 3.5.0 light/dark roles. Fictional
      technicians and schedules; no dispatch service connected.
    </footer>
    <script>
      const staff = [
        {
          id: "alex",
          name: "Alex Morgan",
          skill: "Heating specialist",
          slots: ["09:00", "11:00"],
        },
        {
          id: "sam",
          name: "Sam Rivera",
          skill: "General maintenance",
          slots: ["13:00", "15:00"],
        },
        { id: "lee", name: "Lee Chen", skill: "Unavailable today", slots: [] },
      ];
      let selected = "alex";
      const $ = (id) => document.getElementById(id);
      function update() {
        const p = staff.find((s) => s.id === selected);
        $("technician").textContent = p.name + " · " + p.skill;
        $("time").replaceChildren(...p.slots.map((t) => new Option(t, t)));
        $("feedback").textContent = "";
      }
      $("people").replaceChildren(
        ...staff.map((p) => {
          const l = document.createElement("label");
          l.className = "person";
          const input = document.createElement("input");
          input.type = "radio";
          input.name = "technician";
          input.checked = p.id === selected;
          input.disabled = p.slots.length === 0;
          input.onchange = () => {
            selected = p.id;
            update();
          };
          const text = document.createElement("span");
          text.innerHTML = `<strong>${p.name}</strong><small>${p.skill}</small>`;
          const availability = document.createElement("span");
          availability.textContent = p.slots.length
            ? p.slots.length + " start times"
            : "Unavailable";
          l.append(input, text, availability);
          return l;
        }),
      );
      $("time").onchange = () => {
        $("feedback").textContent = "";
      };
      $("assign").onclick = () => {
        $("feedback").textContent =
          "Local plan: " +
          staff.find((s) => s.id === selected).name +
          " at " +
          $("time").value +
          ". No visit has been dispatched.";
      };
      $("theme").onclick = () => {
        let dark = $("theme").getAttribute("aria-pressed") !== "true";
        $("theme").setAttribute("aria-pressed", dark);
        document.documentElement.style.colorScheme = dark ? "dark" : "light";
      };
      update();
    </script>
  </body>
</html>

Riferimento del codice

  • Salva come index.html; i browser moderni supportano light-dark(). Non si tratta del runtime nativo di Anvil2.

Riferimento per l’integrazione ThemeProvider

La struttura dell’override semantico è documentata in una voce completa di React. Convalidala rispetto alla versione di Anvil2 installata.

jsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { ThemeProvider } from '@servicetitan/anvil2';

const theme = {
  semantic: {
    BackgroundColorPrimary: {
      value: '#7c3aed',
      extensions: { appearance: { dark: { value: '#a78bfa' } } },
    },
  },
};

function App() {
  return (
    <ThemeProvider theme={theme}>
      <main>
        <h1>Theme integration check</h1>
        <div style={{ background: 'var(--a2-background-color-primary)', padding: 24 }}>
          <span style={{ color: 'var(--a2-foreground-color-on-primary)' }}>
            Primary role with the provider’s matching foreground role
          </span>
        </div>
      </main>
    </ThemeProvider>
  );
}
createRoot(document.getElementById('root')).render(<App />);

Riferimento del codice

  • Questo riferimento del provider è stato verificato sulla documentazione ufficiale; l’esempio in esecuzione qui sopra è indipendente HTML.
04

ServiceTitan Anvil Configurazione dell’agente AI del sistema di design

Punto di partenza per l’implementazione

Per l’esempio eseguito non è necessario alcun pacchetto Anvil2. L’esempio separato ThemeProvider richiede un’installazione nativa compatibile.

sh
# Save the complete HTML example as index.html
python3 -m http.server 8080
# Open http://localhost:8080

Un prompt per il tuo progetto

Prompt per l’agente
Crea un’interfaccia per l’assegnazione dei servizi usando i ruoli semantici di Anvil2. Mantieni le coppie di colori in primo piano per chiaro e scuro, le opzioni orarie in base al tecnico, il personale non disponibile disabilitato e l’invalidazione della revisione, come nell’esempio completo. Scegli esplicitamente tra l’installazione originale HTML e un’installazione di Anvil2 verificata React. Non dichiarare mai un’assegnazione senza una risposta dal backend.
05

Fonti e ambito del sistema di design ServiceTitan Anvil

Una guida OpenDesign ad Anvil2 ServiceTitan. I valori dei token sono estratti da Hammer token 3.5.0, disponibile al pubblico. L’esempio eseguibile usa HTML originale, non un runtime React di Anvil2; tutti i tecnici e le disponibilità sono fittizi.