Osy#betaRun it on Osyrin: osyrin.com

Kits · Service desk

Composer — writing a reply

Where a person writes a reply to any record — saved replies with typed placeholders filled from the record (an unknown key refused when saved, an unfillable one refused at send), macros that stage the app's own verbs and run them with the send, markdown formatting with a bar and keys that act on the selection, drafts saved per record and per author that survive a reload (and a seam for a draft prepared by someone else), the writer's signature, files pasted, dropped or picked, and 'send and set…' as the app's choice.

Package
Osysharp.Composer
Install
use Osysharp.Composer@0;
Group
Service desk
Tests
30 in the kit’s own suite
Builds on
Identity

Where a person writes a reply — on a ticket, a case, an order, a lead. The kit never knows which: a composer is pointed at a record by its id, and the app says once, in one class, what sending means for that kind of record.

osy

use Osysharp.Composer@0;

policy ComposesReplies => IsAgent;          // who writes here
policy ManagesSavedReplies => IsAdmin;      // who keeps the TEAM's replies and macros

app.Composer = new ComposerSetup { Targets = [new DeskReplies()] };

public class DeskReplies : ComposerTarget {
  public override string Send(ComposerSend s) { ReplyToTicket(s.Item, s.Text, s.Note, …); return ""; }
  public override List<ReplyPlaceholder> Placeholders() { return [new CustomerFirstName { Key = "customer.first_name", … }]; }
  public override string Record { get { return typeof(Ticket).FullName; } }   // a macro's steps: the app's [Action]s on a ticket
  public override string Signature() { … }
}

Composer(ticket.Id, audience: "To Hanna Lindqvist · email", seen: lastTimelinePlace, keymap: DeskComposerKeys());

What it does

Saved repliesthe team's and each writer's own; searched from the bar; {{key}} placeholders the APP declares, each a class that fills itself from the record — an undeclared key is refused when the reply is saved, an unfillable one refused at send
Macrosa reply and steps that are the app's own [Action] functions on the record, with the choices each offers; applying stages them, the send runs them in order as the writer; a step whose choice has gone is said, never skipped
Formattingmarkdown; a bar and ⌘B/⌘I acting on the selection (TextArea(selectionStart:, selectionEnd:)); a second press undoes; Preview
Draftsper record and per author, with files and staged steps; saved every 2 s while changing and on leaving the box; survive a reload; "Draft saved" read live; Discard with Undo
Prepared draftsPrepareDraft(kind, item, text, by) — offered live in the composer, never sent by itself (the seam an assistant writes through)
SignatureComposerTarget.Signature(), under every reply (never a note), with a switch to leave it off
Filespasted, dropped or picked; checked by ComposerTarget.Keep while the writer can still act
Send and set…ComposerTarget.After(item) — the app's choices; ComposerSend.After carries the one picked
Mentions (0.4.0)typing @ in a note offers ComposerTarget.Mentionable(item); a name written out by hand counts too; ComposerSend.Mentions carries their keys; somebody who may not be named carries a Refusal, shown greyed and refused at send with the same sentence
SettingsComposerSettingsSections announces "Saved replies" and "Macros"; the kit serves both pages

Its tests

The kit's test app is a building's maintenance desk, with its own reply rows, statuses and labels, so the kit is shown working outside a helpdesk.

Get it

Add the kit to your app’s manifest:

osy

app MyApp {
  use Osysharp.Composer@0;
}

Then run osy lock: it downloads the release, verifies it by its content address and pins that version in osyrin.lock. osy kits Osysharp.Composer shows what the kit is for, from inside your app.

Reference: osy docs ui-composer-kit.