mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-06 00:15:32 +00:00
The demo user becomes Nora Lindqvist, engineering lead at Meridian Labs, in the week of a product launch. The fixtures tell one story across mail, contacts, calendar, tasks and files, and exercise the features the screenshots and the public demo should show: - a second, mail-only login (her personal domain) and the shared Support mailbox delegated to the work account, so the account switcher, the shared section and the merged inbox have real content - raw headers the way Stalwart writes them: the sender check fails on the phishing mail, newsletters carry a one-click unsubscribe, one mail asks for a read receipt, and spam scores show in the details - an iMIP invitation with a served .ics, an event waiting for the answer on the calendar, and RSVP patches applied in the demo client - @mentions as mailto links in a received mail, a verification code twelve minutes old, nested folders, named tags on the mail, contact groups, a scheduled message and tasks switched on - every attachment and file backed by real bytes under public/demo, generated by `scripts/demo-assets/generate.mjs`; contact photos are bundled portraits instead of requests to randomuser.me - folder counts computed from the mail instead of hand-kept numbers Demo login seeds the tags, templates and merged-inbox settings once per device and switches the cross-account gate on for the session; "Reset" restores them. `bulwark-demo-simulate=off` in localStorage stops the simulated incoming mail for screenshot runs.
59 lines
2.8 KiB
TypeScript
59 lines
2.8 KiB
TypeScript
import type { FileNode } from '@/lib/jmap/types';
|
|
import { demoDate } from '../demo-utils';
|
|
import { DEMO_ASSETS, type DemoBlobId } from './assets';
|
|
|
|
const FOLDER = {
|
|
documents: 'demo-file-documents',
|
|
beacon: 'demo-file-beacon',
|
|
photos: 'demo-file-photos',
|
|
travel: 'demo-file-travel',
|
|
} as const;
|
|
|
|
function folder(id: string, name: string, parentId: string | null, createdDays: number, modifiedDays: number): FileNode {
|
|
return { id, parentId, name, type: 'd', blobId: null, size: 0, created: demoDate(-createdDays), modified: demoDate(-modifiedDays) };
|
|
}
|
|
|
|
/** A file backed by one of the demo assets; name and size come from the asset. */
|
|
function file(id: string, parentId: string | null, blobId: DemoBlobId, createdDays: number, modifiedDays = createdDays, name?: string): FileNode {
|
|
const asset = DEMO_ASSETS[blobId];
|
|
return { id, parentId, name: name ?? asset.name, type: asset.type, blobId, size: asset.size, created: demoDate(-createdDays), modified: demoDate(-modifiedDays) };
|
|
}
|
|
|
|
export function createDemoFileNodes(): FileNode[] {
|
|
return [
|
|
folder(FOLDER.documents, 'Documents', null, 400, 1),
|
|
folder(FOLDER.beacon, 'Beacon', FOLDER.documents, 60, 1),
|
|
folder(FOLDER.photos, 'Photos', null, 400, 5),
|
|
folder(FOLDER.travel, 'Travel', null, 24, 2),
|
|
|
|
// Documents
|
|
file('demo-file-meeting-notes', FOLDER.documents, 'demo-blob-meeting-notes', 7, 1),
|
|
file('demo-file-todo', FOLDER.documents, 'demo-blob-todo', 3, 0),
|
|
file('demo-file-quarterly-report', FOLDER.documents, 'demo-blob-quarterly-report', 3, 2),
|
|
file('demo-file-service-agreement', FOLDER.documents, 'demo-blob-service-agreement', 9, 1),
|
|
file('demo-file-budget', FOLDER.documents, 'demo-blob-budget', 40, 4),
|
|
|
|
// Documents / Beacon
|
|
file('demo-file-launch-plan', FOLDER.beacon, 'demo-blob-launch-plan', 2),
|
|
file('demo-file-runbook', FOLDER.beacon, 'demo-blob-runbook', 1, 1),
|
|
file('demo-file-soak-metrics', FOLDER.beacon, 'demo-blob-soak-metrics', 1),
|
|
file('demo-file-soak-dashboard', FOLDER.beacon, 'demo-blob-soak-dashboard', 1),
|
|
file('demo-file-launch-deck', FOLDER.beacon, 'demo-blob-launch-deck', 5, 2),
|
|
|
|
// Photos
|
|
file('demo-file-team-photo', FOLDER.photos, 'demo-blob-team-photo', 120),
|
|
file('demo-file-lake', FOLDER.photos, 'demo-blob-vacation', 95),
|
|
file('demo-file-wedding-1', FOLDER.photos, 'demo-blob-wedding-1', 1),
|
|
file('demo-file-wedding-2', FOLDER.photos, 'demo-blob-wedding-2', 1),
|
|
file('demo-file-wedding-3', FOLDER.photos, 'demo-blob-wedding-3', 1),
|
|
|
|
// Travel
|
|
file('demo-file-eticket', FOLDER.travel, 'demo-blob-boarding-pass', 3, 3, 'E-ticket-E7KQ2R.pdf'),
|
|
file('demo-file-hotel', FOLDER.travel, 'demo-blob-hotel-voucher', 3),
|
|
file('demo-file-summit-ticket', FOLDER.travel, 'demo-blob-summit-ticket', 2),
|
|
|
|
// Root
|
|
file('demo-file-prototype', null, 'demo-blob-prototype', 1),
|
|
];
|
|
}
|