|
1 | | -import { screen, inspectScreen, type ScreenDefinition } from "@intent/core" |
2 | | -import { createRouter } from "@intent/router" |
3 | 1 | import { renderRouter } from "@intent/dom" |
4 | | -import type { RouterServices, RoutesFromPaths, RouteContext } from "@intent/router" |
5 | | - |
6 | | -const appPaths = { |
7 | | - home: "/", |
8 | | - "team.details": "/teams/:teamId", |
9 | | - "team.invite": "/teams/:teamId/invite", |
10 | | -} as const |
11 | | - |
12 | | -type AppRoutes = RoutesFromPaths<typeof appPaths> |
13 | | -type AppServices = RouterServices<AppRoutes, { |
14 | | - route: RouteContext<AppRoutes> |
15 | | -}> |
16 | | - |
17 | | -function delay(ms: number) { |
18 | | - return new Promise(resolve => setTimeout(resolve, ms)) |
19 | | -} |
20 | | - |
21 | | -let teamLoadVersion = 0 |
22 | | -const teamVersions: Record<string, number> = {} |
23 | | - |
24 | | -const teams: Record<string, { id: string; name: string; members: string[] }> = { |
25 | | - team_1: { id: "team_1", name: "Alpha", members: [] }, |
26 | | - team_2: { id: "team_2", name: "Beta", members: [] }, |
27 | | - team_3: { id: "team_3", name: "Gamma", members: [] }, |
28 | | -} |
29 | | - |
30 | | -async function loadTeam(teamId: string) { |
31 | | - await delay(80) |
32 | | - const team = teams[teamId] |
33 | | - if (!team) throw new Error(`Team "${teamId}" not found`) |
34 | | - const version = ++teamLoadVersion |
35 | | - teamVersions[teamId] = version |
36 | | - return { ...team, version } |
37 | | -} |
38 | | - |
39 | | -async function inviteMember(teamId: string, email: string) { |
40 | | - await delay(200) |
41 | | - const team = teams[teamId] |
42 | | - if (!team) throw new Error(`Team "${teamId}" not found`) |
43 | | - team.members.push(email) |
44 | | -} |
45 | | - |
46 | | -function updateTeamInfo(teamId: string | undefined) { |
47 | | - const el = document.getElementById("team-info") |
48 | | - if (!el) return |
49 | | - if (!teamId) { |
50 | | - el.textContent = "" |
51 | | - return |
52 | | - } |
53 | | - const team = teams[teamId] |
54 | | - const version = teamVersions[teamId] |
55 | | - if (team) { |
56 | | - const versionStr = version ? `(v${version}) ` : "" |
57 | | - el.textContent = `${versionStr}${team.name} — ${team.members.length} member${team.members.length === 1 ? "" : "s"}` |
58 | | - } |
59 | | -} |
60 | | - |
61 | | -function updateDiagnostics(screenDef: ScreenDefinition<AppServices>) { |
62 | | - const el = document.getElementById("diagnostics") |
63 | | - if (!el) return |
64 | | - const inspected = inspectScreen(screenDef) |
65 | | - if (inspected.diagnostics.length === 0) { |
66 | | - el.textContent = "✓ No diagnostics." |
67 | | - } else { |
68 | | - el.textContent = inspected.diagnostics.map(d => |
69 | | - `[${d.severity}] ${d.code}${d.nodeId ? ` (${d.nodeId})` : ""}: ${d.message}` |
70 | | - ).join("\n") |
71 | | - } |
72 | | -} |
73 | | - |
74 | | -const HomeScreen = screen<AppServices>("Home", $ => { |
75 | | - const openAlpha = $.act("Open Alpha — team_1") |
76 | | - .primary() |
77 | | - .does(({ navigate }) => { |
78 | | - navigate("team.details", { teamId: "team_1" }) |
79 | | - }) |
80 | | - |
81 | | - const openBeta = $.act("Open Beta — team_2") |
82 | | - .does(({ navigate }) => { |
83 | | - navigate("team.details", { teamId: "team_2" }) |
84 | | - }) |
85 | | - |
86 | | - const openGamma = $.act("Open Gamma — team_3") |
87 | | - .does(({ navigate }) => { |
88 | | - navigate("team.details", { teamId: "team_3" }) |
89 | | - }) |
90 | | - |
91 | | - $.surface("main").contains(openAlpha, openBeta, openGamma) |
92 | | -}) |
93 | | - |
94 | | -const TeamDetailScreen = screen<AppServices>("Team Details", $ => { |
95 | | - const team = $.resource("team", { |
96 | | - load: async ({ route }) => { |
97 | | - if (route.name === "team.details") { |
98 | | - return loadTeam(route.params.teamId) |
99 | | - } |
100 | | - throw new Error("Expected team.details route") |
101 | | - }, |
102 | | - }) |
103 | | - |
104 | | - const refresh = $.act("Refresh team") |
105 | | - .does(async ({ route }) => { |
106 | | - await team.reload() |
107 | | - if (route.name === "team.details") { |
108 | | - updateTeamInfo(route.params.teamId) |
109 | | - } |
110 | | - }) |
111 | | - .feedback({ |
112 | | - pending: "Refreshing...", |
113 | | - success: "Team refreshed.", |
114 | | - failure: "Could not refresh.", |
115 | | - }) |
116 | | - |
117 | | - const invite = $.act("Invite member") |
118 | | - .primary() |
119 | | - .when(team.ready, "Team must load first.") |
120 | | - .does(({ navigate, route }) => { |
121 | | - if (route.name === "team.details") { |
122 | | - navigate("team.invite", { teamId: route.params.teamId }) |
123 | | - } |
124 | | - }) |
125 | | - |
126 | | - const back = $.act("Back home") |
127 | | - .does(({ navigate }) => { |
128 | | - navigate("home") |
129 | | - }) |
130 | | - |
131 | | - $.surface("main").contains(refresh, invite, back) |
132 | | -}) |
133 | | - |
134 | | -const InviteScreen = screen<AppServices>("Invite", $ => { |
135 | | - const emailText = $.state.text("email") |
136 | | - const emailAsk = $.ask("Email", emailText) |
137 | | - .asContact("email") |
138 | | - .required() |
139 | | - .private() |
140 | | - |
141 | | - const sendInvite = $.act("Send invite") |
142 | | - .primary() |
143 | | - .when(emailAsk.valid, "Enter a valid email address.") |
144 | | - .does(async ({ navigate, route }) => { |
145 | | - if (route.name === "team.invite") { |
146 | | - await inviteMember(route.params.teamId, emailText.value) |
147 | | - updateTeamInfo(route.params.teamId) |
148 | | - navigate("team.details", { teamId: route.params.teamId }) |
149 | | - } |
150 | | - }) |
151 | | - .feedback({ |
152 | | - pending: "Sending invite...", |
153 | | - success: "Invite sent.", |
154 | | - failure: "Could not send invite.", |
155 | | - }) |
156 | | - |
157 | | - $.flow("invite") |
158 | | - .startsWith(emailAsk) |
159 | | - .then(sendInvite) |
160 | | - |
161 | | - $.surface("main").contains(emailAsk, sendInvite) |
162 | | -}) |
163 | | - |
164 | | -const NotFoundScreen = screen<AppServices>("Not Found", $ => { |
165 | | - $.surface("main").contains() |
166 | | -}) |
167 | | - |
168 | | -const router = createRouter<AppServices>() |
169 | | - .route("home", appPaths.home, HomeScreen) |
170 | | - .route("team.details", appPaths["team.details"], TeamDetailScreen) |
171 | | - .route("team.invite", appPaths["team.invite"], InviteScreen) |
| 2 | +import { router } from "./router.js" |
| 3 | +import { NotFoundScreen } from "./screens.js" |
| 4 | +import { updateTeamInfo, updateDiagnostics } from "./demo-panels.js" |
172 | 5 |
|
173 | 6 | const root = document.getElementById("root")! |
174 | 7 | renderRouter(router, { |
|
0 commit comments