Dillo example

Yom Kippur Tickets

Household tickets. Scan once, can't be reused.

What this demonstrates

This is the ticket example. Your organization issues tickets. Members read their own. Ushers work the check-in desk — they can scan you in, but they can't edit tickets. Once a ticket is scanned, it's used. Can't be scanned again. Need to reissue? The office can do that. Built for how humans actually are: tickets get lost, plans change, people show up late.

How it works

The permissions are the app. This is the whole data model — schema.ts, the first of the app's three files (its import line, not shown, brings these names in from "create-dillo/schema"):

schema.ts
// The schema embedded in the live worker today (span2/live-schemas filing, re-verified at deploy).
import { anyone, members, nobody, owner, relation, roles, schema, string, table } from '@29court/armadillo/schema';

// Services are public listings. A household is a team: the owner issues tickets,
// and members can read them. Ticket writes are closed, so nobody can edit a
// ticket through the record API. checkIn is the only way a ticket is checked in.
// Shifts belong to the trusted usher desk. Only that desk's owner can write them.
// An usher role can call checkIn and cannot read or write shifts or tickets.
export default schema({
  Service: table({
    name: string().min(1).max(120),
    startsAt: string().datetime(),
  }).permissions({
    view: anyone,
    edit: owner,
  }),

  Household: table({
    householdId: relation.team(),
    name: string().min(1).max(120),
  }).permissions({
    view: members,
    edit: roles('owner'),
  }),

  Member: table({
    householdId: relation.team(),
    name: string().min(1).max(120),
  }).permissions({
    view: members,
    edit: roles('owner'),
  }),

  Ticket: table({
    householdId: relation.team(),
    serviceId: relation('Service'),
    guest: string().min(1).max(120),
    code: string().min(16).max(80),
    checkedInAt: string().datetime().optional().nullable(),
    checkedInBy: relation.user().optional().nullable(),
  }).permissions({
    view: roles('owner', 'member'),
    edit: nobody,
  }),

  Shift: table({
    deskId: relation.team(),
    serviceId: relation('Service'),
    usherId: relation.user(),
  }).permissions({
    view: roles('owner'),
    edit: roles('owner'),
  }),
});

Try it

  1. Open the live demo
  2. View your household tickets
  3. Try the usher scan view
  4. Scan a ticket twice (second scan fails)