enhancement
major
minor
major
minor
major
#29600
TL Views: Anzeige lang laufender Server-Aufträge (Phasen, Fortschritt, unbestimmter Fortschritt, verstrichene Zeit)
Kontext
Anwendungen starten aus der Oberfläche heraus Aufträge, die Sekunden bis Minuten dauern (KI-gestützte Treffersuche, Erzeugung eines Exposés durch einen Agenten, etwa zehn Minuten). Die React-Oberfläche hat dafür nur <progress> mit bekanntem Anteil und die Ladeanzeigen einzelner Controls. Es fehlen ein unbestimmter Fortschritt, eine Phasenanzeige (welche Teilschritte erledigt/aktiv/offen sind), die verstrichene Zeit und ein Muster, wie der Serverauftrag seinen Zustand an die Ansicht meldet.
Erweiterung
Anzeige lang laufender Aufträge in der Ansicht: Zustand, Phasenliste mit aktueller Phase, bestimmter oder unbestimmter Fortschritt, verstrichene Zeit; Ergebnis oder Fehler am Ende. Die Gestaltung (Animation, Symbolik) bleibt App-CSS.
Lösung
Auftrag starten: `<start-job>`
Eine Aktion der Befehlskette (StartJobAction, unterbrechbar wie <confirm>). Sie startet den Auftrag in einem Hintergrund-Thread (SchedulerService, Sub-Session des startenden Fensters installiert), veröffentlicht seinen Zustand als unveränderliche Momentaufnahme (JobState) auf dem in job benannten Kanal und unterbricht die Kette. Endet der Auftrag erfolgreich, läuft die Kette mit seinem Ergebnis weiter (z. B. <write-channel>, <with-transaction>); bei Fehler oder Abbruch wird die Kette abgebrochen (Kompensationen laufen), der Fehler bleibt in der letzten Momentaufnahme sichtbar. Ein Fehler in den Aktionen nach dem Auftrag wird wie ein Kommandofehler im Fenster gemeldet. Jede Momentaufnahme wird in der Sub-Session und unter der Interaktion des Fensters veröffentlicht, also gegen die Anfragen der Sitzung serialisiert; update-interval (Standard 250 ms) drosselt schnell meldende Aufträge, die letzte Meldung und die abschließende Momentaufnahme gehen nie verloren. Der Auftrag läuft außerhalb jeder Transaktion; die Persistierung des Ergebnisses ist Sache der nachfolgenden Aktionen.
Der Rumpf ist entweder eine TL-Script-Funktion (function, Argumente: Auftrags-Monitor, dann die inputs-Kanalwerte, zuletzt der Kettenwert) oder eine Java-Implementierung (<body class="…"/>, Schnittstelle JobBody mit run(JobMonitor, List<Object>)), genau eines von beiden. Phasen werden in der Konfiguration deklariert (<phases><phase name="…"><label>…</label></phase>), damit die Oberfläche sie von Anfang an kennt; der Rumpf markiert den Übergang ($job.jobPhase('draft') bzw. job.beginPhase("draft")), übersprungene Phasen gelten als erledigt, eine nicht deklarierte Phase beendet den Auftrag mit Fehler. Kontext zwischen Phasen fließt über gewöhnliche Variablen.
<start-job cancelable="true" inputs="property" job="exposeJob">
<phases>
<phase name="collect"><label><en>Collecting facts</en><de>Fakten sammeln</de></label></phase>
<phase name="draft"><label><en>Drafting</en><de>Entwurf</de></label></phase>
<phase name="review"><label><en>Reviewing</en><de>Prüfung</de></label></phase>
</phases>
<function><![CDATA[job -> property -> x -> {
$job.jobPhase('collect');
$facts = ...;
$job.jobPhase('draft');
$job.jobIndeterminate();
$draft = ...;
$job.jobPhase('review');
$job.jobProgress(0, $draft.size());
...
}]]></function>
</start-job>
<write-channel name="expose"/>
TL-Script-Funktionen (JobFunctions, Präfix job), meldend am Monitor: jobPhase(job, name), jobPhases(job, liste oder {name: label}) für erst zur Laufzeit bekannte Phasen, jobProgress(job, done, total), jobIndeterminate(job), jobMessage(job, text oder #('…'@en)). Lesend über die Momentaufnahme, für <disabled-if>, <if>, <switch>, <derived-channel>, auch ohne Auftrag (null): jobIsRunning, jobIsFinished, jobStatus ('running'|'completed'|'failed'|'cancelled'), jobResult, jobError.
Abbruch
cancelable="true" zeigt in der Anzeige eine Abbrechen-Schaltfläche. Der Abbruch ist kooperativ, der Rumpf muss aber kein Flag abfragen: Jede Meldung am Monitor und jeder Phasenwechsel wirft bei gesetztem Abbruch die bestehende AbortExecutionException (wie der Legacy-ProgressDialog), die den Rumpf beendet; zusätzlich wird der Arbeits-Thread unterbrochen, sodass sleep(), blockierende Ein-/Ausgabe und HTTP-Aufrufe sofort enden. Die Script-Funktion sleep() behält dazu die Unterbrechung, statt sie zu verschlucken. Ein abgebrochener Auftrag endet unabhängig davon, womit sein Rumpf endet (auch wenn der TL-Script-Interpreter die Ausnahme in eine TopLogicException verpackt), als CANCELLED ohne Fehler; ein nachträglich geliefertes Ergebnis wird verworfen.
== Anzeige: <job-status input="job"> ==
Element JobStatusElement mit dem Control ReactJobStatusControl / React-Komponente TLJobStatus: Zustand, Phasenliste (erledigt/aktiv/offen), Fortschrittsbalken (bestimmt oder unbestimmt), Meldung, verstrichene Zeit, am Ende Ergebnis oder Fehler, bei cancelable die Abbrechen-Schaltfläche (Kommando cancel). Die verstrichene Zeit zählt im Browser ab dem Startzeitpunkt mit Server-Uhrenabgleich (serverNow) und kostet keine Server-Ereignisse; nach dem Ende steht sie still. Texte werden serverseitig aufgelöst (Phasen, Meldung, Fehler über ResKey, Ergebnis über MetaLabelProvider), gemeinsam für <text>, <progress> und <job-status> über ValueLabel. Das Control hängt nicht an der View-Schicht: Es erhält eine JobDisplay-Beschreibung, die das Element aus dem JobState bildet. Gestaltung über BEM-Klassen (tlJobStatus, tlJobStatus--running|completed|failed|cancelled, tlJobStatus__phase--done|active|pending, …) und Theme-Tokens, damit App-CSS sie anpassen kann.
<progress> und ReactProgressControl erhalten einen unbestimmten Modus: ein fraction-Ausdruck, der nichts liefert, bzw. null als Anteil zeigt einen Balken mit laufender Teilfüllung (tlProgress--indeterminate, prefers-reduced-motion beachtet). Der label-Ausdruck von <progress> löst ein I18N-Literal nun als Text auf. Die Balken-Darstellung ist als Komponente ProgressBar von TLProgress exportiert und wird von TLJobStatus wiederverwendet; die Dauer-Formatierung m:ss / h:mm:ss teilen sich Notice-Bar und Auftragsanzeige (duration.ts).
Nicht enthalten
Adapter für das bestehende ProgressInfo der Importer, persistierte Aufträge über Sitzungen hinweg, Fortlaufen eines Auftrags nach Schließen des Fensters.
Demo und Dokumentation
com.top_logic.demo.react, Navigationseintrag „Lang laufender Auftrag“ (WEB-INF/views/demo/long-job-demo.view.xml): dreiphasiger Skript-Auftrag mit bestimmtem und unbestimmtem Fortschritt, Ergebnis auf einem zweiten Kanal, fehlschlagende Variante, Abbruch, einzelner unbestimmter Balken. Abschnitt „Long-running jobs“ in docs/faq/react-view-layer.md. Tests: TestStartJobAction, TestJobFunctions, TestJobStatusElement, TestReactJobStatusControl, TestReactProgressControl, TestProgressElement.