Servicemanagement · Neue Ressource

ServiceTitan Anvil Designsystem.

Anvil2 konkretisieren – mit seinem Token-Modell aus drei Ebenen, verifizierten Rollenwerten für hell und dunkel sowie einer vollständigen Interaktion zur Servicezuweisung mit synchronisierten Techniker-, Zeit- und Prüfstatusangaben.

OpenDesign-Leitfaden7 Kapitel38 ReferenzeinträgeOriginaler Anwendungsleitfaden
01

ServiceTitan Anvil Designsystem – Interface-Vorschau

Zwischen heller und dunkler Darstellung wechseln, verfügbares Personal auswählen und einen synchronisierten lokalen Plan prüfen.

Eine Technikerin oder einen Techniker einem Serviceeinsatz zuweisenVollständiges Beispiel öffnen
Ursprüngliches HTML mit Rollenwerten aus Hammer Token 3.5.0. · Implementierungsreferenz
02

ServiceTitan Anvil Designsystem – Designreferenz

Kern-Theme-Exporte aus @servicetitan/hammer-token 3.5.0 sowie die Dokumentation zur Theming-Bibliothek Anvil2.

Semantische Paare für helle und dunkle Darstellung

Quelle
Name / RegelWert / KonfigurationVerwendung
BackgroundColorDefaultlight #ffffff / dark #1a1a1aHauptoberfläche der Anwendung.
BackgroundColorDefaultHoverlight #f9f9f9 / dark #292929Hover-Oberfläche.
BackgroundColorPrimarylight #0265dc / dark #70b1ffFlächenfüllung der primären Aktion.
BackgroundColorPrimaryHoverlight #1d4ca3 / dark #9ecaffNative Rolle für den Hover-Zustand des Primärelements.
BackgroundColorPrimaryFaintlight #fafcff / dark #000b19Zurückhaltende ausgewählte Oberfläche.
ForegroundColorSubduedlight #707070 / dark #c4c4c4Unterstützender Text.
ForegroundColorOnPrimarylight #ffffff / dark #1a1a1aText in Kombination mit einer primären Füllfarbe.
ForegroundColorPrimarylight #0265dc / dark #70b1ffInteraktiver Vordergrund.
BorderColorSubduedlight #e6e6e6 / dark #404040Dezente Begrenzungen.
BorderColorPrimarylight #0265dc / dark #9ecaffInteraktive Begrenzung.
StatusColorInfolight #0265dc / dark #70b1ffInformationsstatus.
StatusColorDangerlight #d62100 / dark #ff5b3dSofortiger Handlungsbedarf.
StatusColorSuccesslight #077e50 / dark #0bd085Lokale Prüfung abgeschlossen.
StatusColorWarninglight #ffbe00 / dark #ffe278Unkritischer Hinweis.

Primitive Geometrie- und Responsive-Werte

Quelle
Name / RegelWert / KonfigurationVerwendung
BreakpointSm640pxVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
BreakpointMd768pxVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
BreakpointLg1024pxVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
BreakpointXl1280pxVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
BreakpointXxl1536pxVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
DurationDefault200msVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
DurationInstant0msVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
DurationFast100msVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
DurationSlow300msVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius00remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius10.1875remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius20.375remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius30.5625remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius40.75remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius50.9375remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius61.125remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius71.3125remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.
Radius81.5remVeröffentlichtes Primitiv; semantische und komponentenspezifische Aliase bestimmen die tatsächlichen Anwendungsrollen.

Vertrag für ThemeProvider-Überschreibungen

Quelle
Name / RegelWert / KonfigurationVerwendung
theme.semanticsemantic token overridesEine Rolle wie BackgroundColorPrimary ändern.
theme.componentcomponent token overridesEine Änderung auf eine Komponentenrolle wie ButtonPrimaryBackgroundColor eingrenzen.
valuedefault/light token valueDen Wert für die helle Darstellung angeben.
extensions.appearance.dark.valuedark appearance valueDie passende dunkle Darstellung festlegen.
CSS output--a2-<kebab-case-name>ThemeProvider serialisiert den Token-Schlüssel in eine CSS-Variable.
Primitive cascaderesolved at build timeWird ein Primitiv zur Laufzeit überschrieben, werden semantische Aliase nicht neu berechnet.
03

ServiceTitan Anvil Designsystem – vollständiger Leitfaden

Ein praktischer Leitfaden zur Auswahl, Anwendung und Überprüfung dieses Systems für ein echtes Interface.

DESIGN-servicetitan-anvil.md

Umfassende Anleitung, Einstieg in die Implementierung und Links zu den Quellen.

Leitfaden herunterladen

ServiceTitan Anvil Designsystem – Tokenarchitektur mit drei Ebenen

Primitive unterhalb semantischer Rollen halten

Anvil2 trennt rohe Primitive, semantische Rollen und komponentenspezifische Aliase. Ein Produkt sollte in der Regel die Rolle wählen, die die Bedeutung im Interface beschreibt, statt ein primitives Blau für jedes Steuerelement zu übernehmen. Die Referenztabelle enthält die exakten exportierten Werte und ihre Entsprechungen für den dunklen Modus. So lässt sich eine Theme-Änderung als Wertepaar prüfen.

Aus diesem OpenDesign-Leitfaden

Keine Neuberechnung von Aliasen zur Laufzeit erwarten

Laut offizieller Anleitung zum Theming werden Verweise auf Primitive während des Builds aufgelöst. Wird ein Primitiv über einen Provider zur Laufzeit geändert, wird die semantische Ebene nicht automatisch neu generiert. Um die Farben der Anwendung anzupassen, überschreiben Sie die betreffende semantische Rolle; für eine einzelne Komponente verwenden Sie die Komponentenebene. So verhindern Sie ein teilweise geändertes Theme, bei dem zusammengehörige Steuerelemente uneinheitlich bleiben.

ServiceTitan Anvil Designsystem – helle und dunkle Darstellung

Änderungen an Vorder- und Hintergrundfarbe aufeinander abstimmen

Die primäre Aktion verwendet BackgroundColorPrimary mit ForegroundColorOnPrimary. In der hellen Darstellung ist sie blau mit weißem Text, in der dunklen Darstellung hellblau mit dunkler Beschriftung. Würde nur die Füllfarbe wechseln, wäre das Wertepaar nicht stimmig. Die vollständige HTML-Studie verwendet light-dark() mit einem Wechsel des Farbschemas, um diese Werte zu veranschaulichen, ohne vorzugeben, ThemeProvider zu verwenden.

Statusfarben mit klarer Bedeutung verwenden

Anvil2 ordnet den Farbfamilien Statusbedeutungen zu. Bei der lokalen Prüfung wird eine Erfolgsrolle verwendet, zugleich wird genau beschrieben, was passiert ist. Ein nicht verfügbarer Techniker wird textlich gekennzeichnet und hat eine deaktivierte Radio-Schaltfläche. Ein grüner Farbton erweckt nicht den Eindruck, ein Serviceeinsatz sei disponiert worden; das Beispiel ist nicht mit einem Dispositions-Backend verbunden.

ServiceTitan Anvil Designsystem – Servicezuweisung und Status

Verfügbarkeit muss die nächste Auswahl bestimmen

Bei der Auswahl eines Technikers werden die Optionen für die Startzeit durch die fiktive Verfügbarkeit dieser Person ersetzt. Die nicht verfügbare Person bleibt sichtbar, kann aber nicht ausgewählt werden. Da die beiden Steuerelemente voneinander abhängen, muss eine Änderung des ersten das zweite auf einen gültigen Wert zurücksetzen. Eine Zeitangabe, die einer anderen Person zugeordnet ist, darf nicht bestehen bleiben.

Planung und Ausführung voneinander trennen

Die Prüfung der Zuweisung speichert einen lokalen Plan und weist ausdrücklich darauf hin, dass kein Serviceeinsatz disponiert wurde. Für einen produktiven Ablauf wären vor dem Speichern Berechtigungsprüfungen, eine aktuelle Terminplanantwort und eine Behandlung von Konflikten erforderlich. Das Beispiel macht diese Grenze sichtbar und bietet zugleich eine vollständige Interaktion, die sich ausprobieren und anpassen lässt.

ServiceTitan Anvil Designsystem – operative Dichte und Lesbarkeit

Den Auftragskontext direkt bei der Auswahl anzeigen

Eine kompakte Auftragsübersicht enthält Aufgabe, Dauer, Bereich und Priorität. Die Technikeroptionen bilden eine gut lesbare vertikale Liste; der vorgeschlagene Einsatz wird in einem separaten Bereich dargestellt. Neutrale Abgrenzungen und eine zurückhaltende Akzentfarbe sorgen dafür, dass die Auswahl und die Hauptaktion leicht erkennbar bleiben. Die Zeitplanbalken sind als beispielhafte Darstellung gekennzeichnet; maßgeblich ist das tatsächliche Zeitauswahlfeld.

Die Lesereihenfolge auf Mobilgeräten beibehalten

ServiceTitan Anvil Designsystem – native Integration und vollständige Alternative zu HTML

Die dokumentierte Provider-Struktur verwenden

Anvil2s ThemeProvider akzeptiert Überschreibungen für semantische und komponentenspezifische Tokens, einschließlich value- und dark-Erweiterungen. Der Leitfaden enthält weiter unten ein vollständiges Provider-Beispiel aus der öffentlichen Dokumentation. Es dient als Referenz für eine native Integration und unterscheidet sich vom unabhängigen HTML-Beispiel, das lokal ausgeführt wurde.

Keine ungeprüfte Laufzeit des Pakets behaupten

Diese Seite bietet eine funktionierende HTML-Implementierung und verifizierte Token-Daten. Das native Provider-Beispiel ist eine separate Integrationsreferenz und entspricht nicht der Laufzeitumgebung der Vorschau. Wenn Sie React-Komponenten einsetzen, installieren Sie eine kompatible Anvil2-Version, binden Sie die erforderlichen Styles ein und prüfen Sie die Komponenten-APIs, bevor Sie die nativen HTML-Steuerelemente ersetzen.

ServiceTitan Anvil Designsystem – Barrierefreiheit und Statusprüfung

Native Formularsemantik verwenden

Techniker werden durch native Radio-Schaltflächen mit Beschriftung dargestellt, die Startzeit durch ein beschriftetes Auswahlfeld und die Darstellungseinstellung durch aria-pressed. Die Prüfmeldung ist eine Statusregion. Für die Radio-Auswahl wird das native Tastaturverhalten des Browsers genutzt; die sichtbare Fokusgestaltung bleibt unabhängig von Auswahl- und Hover-Stilen.

Änderungen nach der Bestätigung prüfen

Wird der Techniker oder die Zeit geändert, werden alte Prüfmeldungen gelöscht. Ein Wechsel der Darstellung behält den ausgewählten Techniker und die Zeit bei, denn eine visuelle Theme-Änderung sollte einen Plan nicht verwerfen. Tests sollten beide Übergänge, eine deaktivierte Auswahl und das Verhalten bei schmalen Bildschirmen abdecken, statt nur zu prüfen, ob sich eine Schaltfläche anklicken lässt.

ServiceTitan Anvil Designsystem – Übergabe an den AI-Agenten und Implementierung

Mit dem vollständigen Zustandsmodell beginnen

Speichern und starten Sie das vollständige HTML-Beispiel, um das Verhalten zu prüfen. Die aus Tokens abgeleiteten Paare für den hellen und dunklen Modus sind oben im CSS sichtbar; die festgelegten Verfügbarkeitsdaten finden Sie im Skript. Eine native React-Implementierung kann dieselben Zustandsübergänge beibehalten und dabei geprüfte Anvil2-Steuerelemente verwenden.

  1. Das vollständige HTML-Beispiel ausführen.
  2. Den zweiten Techniker auswählen und prüfen, ob sich die Optionen für die Startzeit ändern.
  3. Plan prüfen, dann die Uhrzeit ändern und sicherstellen, dass die Rückmeldung gelöscht wird.
  4. Darstellung wechseln und prüfen, ob die aktuelle Auswahl erhalten bleibt.

Angeben, wofür echte Daten erforderlich sind

Den Agenten anweisen, die abhängige Zeitliste, das deaktivierte Teammitglied, die Ungültigkeit der Prüfung und das Verhalten der Darstellung beizubehalten. Den fiktiven Auftrag und das Teammitglied erst ersetzen, wenn ein echter Datenvertrag verfügbar ist. Die lokale Prüfung nicht allein durch eine Änderung der Schaltflächenbeschriftung als Erfolg ausgeben.

  • Verfügbarkeit vor einer echten Disposition prüfen.
  • Semantische und komponentenspezifische Überschreibungen getrennt halten.
  • Vorder- und Hintergrundkombinationen für helle und dunkle Darstellung testen.

ServiceTitan Anvil-Komponentenbeispiele des Designsystems

Vollständiges Beispiel für eine lokale Zuweisung

Originales HTML/CSS/JavaScript mit tokenbasierten Darstellungen und abhängigem Formularstatus.

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>

Code-Referenz

  • Als index.html speichern; moderne Browser unterstützen light-dark(). Dies ist nicht die native Anvil2-Laufzeitumgebung.

Referenz zur ThemeProvider-Integration

Dokumentierte Form semantischer Überschreibungen in einem vollständigen React-Eintrag. Mit der installierten Anvil2-Version abgleichen.

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 />);

Code-Referenz

  • Diese Provider-Referenz wurde anhand der offiziellen Dokumentation geprüft; das oben ausgeführte Beispiel ist davon unabhängig und verwendet HTML.
04

Einrichtung des ServiceTitan Anvil-Designsystem-Agenten

Ausgangspunkt für die Implementierung

Für das ausgeführte Beispiel ist kein Anvil2-Paket erforderlich. Das separate ThemeProvider-Beispiel setzt eine kompatible native Installation voraus.

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

Ein Prompt für dein Projekt

Agenten-Prompt
Eine Oberfläche zur Zuweisung von Serviceaufträgen mit semantischen Rollen von Anvil2 erstellen. Die Vordergrundkombinationen für helle und dunkle Darstellung, die vom Techniker abhängigen Zeitauswahlen, nicht verfügbare und daher deaktivierte Teammitglieder sowie die Ungültigkeit der Prüfung aus dem vollständigen Beispiel beibehalten. Explizit zwischen dem originalen HTML und einer verifizierten React-Installation von Anvil2 wählen. Eine Disposition niemals ohne Antwort vom Backend als erfolgt ausgeben.
05

Quellen und Geltungsbereich des ServiceTitan Anvil-Designsystems

Ein OpenDesign-Leitfaden zu ServiceTitan Anvil2. Tokenwerte wurden aus den öffentlichen Hammer-Tokens 3.5.0 extrahiert. Das ausgeführte Beispiel verwendet originales HTML, keine React-Laufzeitumgebung von Anvil2; alle Techniker und Verfügbarkeiten sind fiktiv.