@mmapp/react-compiler 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/README.md +107 -0
  2. package/compile-blueprint-chat.mjs +99 -0
  3. package/compile-blueprint-glass-console.mjs +98 -0
  4. package/compile-chat-defs.mjs +92 -0
  5. package/dist/babel/index.d.mts +3 -0
  6. package/dist/babel/index.d.ts +3 -0
  7. package/dist/babel/index.js +4851 -0
  8. package/dist/babel/index.mjs +7 -0
  9. package/dist/chunk-26U577GB.mjs +3465 -0
  10. package/dist/chunk-2FBDFAX6.mjs +2362 -0
  11. package/dist/chunk-2L4QSMXG.mjs +175 -0
  12. package/dist/chunk-2REDFOER.mjs +931 -0
  13. package/dist/chunk-46YKSHQR.mjs +175 -0
  14. package/dist/chunk-4XHK6FWL.mjs +2058 -0
  15. package/dist/chunk-5M7DKKBC.mjs +215 -0
  16. package/dist/chunk-5VNJ7C6N.mjs +154 -0
  17. package/dist/chunk-6CQOAAMV.mjs +1803 -0
  18. package/dist/chunk-6SEVAAVT.mjs +3516 -0
  19. package/dist/chunk-6YLR5ZDA.mjs +2829 -0
  20. package/dist/chunk-AOGY2GK6.mjs +3292 -0
  21. package/dist/chunk-AXXUXRNA.mjs +1434 -0
  22. package/dist/chunk-CHLVKMQW.mjs +175 -0
  23. package/dist/chunk-CKGOZAB7.mjs +939 -0
  24. package/dist/chunk-D34RAZUX.mjs +2223 -0
  25. package/dist/chunk-EQGA6A6D.mjs +121 -0
  26. package/dist/chunk-EY2CSXYA.mjs +822 -0
  27. package/dist/chunk-FIQ65CDR.mjs +925 -0
  28. package/dist/chunk-FOZXJFAR.mjs +186 -0
  29. package/dist/chunk-FX6URXWN.mjs +186 -0
  30. package/dist/chunk-G7SMOWOL.mjs +828 -0
  31. package/dist/chunk-GGB4G5YY.mjs +175 -0
  32. package/dist/chunk-HLRGCCIL.mjs +4839 -0
  33. package/dist/chunk-HOIUP6IF.mjs +690 -0
  34. package/dist/chunk-I3AU7GRD.mjs +120 -0
  35. package/dist/chunk-ILFGMUVD.mjs +1933 -0
  36. package/dist/chunk-IPTX5MJU.mjs +3223 -0
  37. package/dist/chunk-ITGUSH2Z.mjs +2783 -0
  38. package/dist/chunk-IXHBCAMF.mjs +3306 -0
  39. package/dist/chunk-J7TWJ3TM.mjs +2784 -0
  40. package/dist/chunk-JDPLDGVF.mjs +4810 -0
  41. package/dist/chunk-K53XP2DL.mjs +148 -0
  42. package/dist/chunk-K5HX2SVL.mjs +1902 -0
  43. package/dist/chunk-KFGYOOVS.mjs +214 -0
  44. package/dist/chunk-KFVVOS5N.mjs +925 -0
  45. package/dist/chunk-L2OZ4CDV.mjs +113 -0
  46. package/dist/chunk-MIZV3TAN.mjs +3293 -0
  47. package/dist/chunk-NKKLQE5V.mjs +148 -0
  48. package/dist/chunk-NOW23XFZ.mjs +186 -0
  49. package/dist/chunk-NRXQKQ74.mjs +148 -0
  50. package/dist/chunk-OWI6XWCD.mjs +3375 -0
  51. package/dist/chunk-PRUMNNDI.mjs +3192 -0
  52. package/dist/chunk-QTBD5B3F.mjs +148 -0
  53. package/dist/chunk-SKSDPPNT.mjs +3788 -0
  54. package/dist/chunk-SP2YUS33.mjs +186 -0
  55. package/dist/chunk-SU4E6E7B.mjs +3153 -0
  56. package/dist/chunk-SYUUKW5A.mjs +3379 -0
  57. package/dist/chunk-UL2XZEMA.mjs +3128 -0
  58. package/dist/chunk-XMWUHQVV.mjs +939 -0
  59. package/dist/chunk-XZNEDRGN.mjs +3876 -0
  60. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  61. package/dist/chunk-YFS6JMYO.mjs +3342 -0
  62. package/dist/chunk-Z6AIQ4KL.mjs +113 -0
  63. package/dist/cli/index.d.mts +1 -0
  64. package/dist/cli/index.d.ts +1 -0
  65. package/dist/cli/index.js +11585 -0
  66. package/dist/cli/index.mjs +701 -0
  67. package/dist/codemod/cli.d.mts +1 -0
  68. package/dist/codemod/cli.d.ts +1 -0
  69. package/dist/codemod/cli.js +1104 -0
  70. package/dist/codemod/cli.mjs +157 -0
  71. package/dist/codemod/index.d.mts +148 -0
  72. package/dist/codemod/index.d.ts +148 -0
  73. package/dist/codemod/index.js +981 -0
  74. package/dist/codemod/index.mjs +25 -0
  75. package/dist/dev-server-Bs_sz2DG.d.mts +111 -0
  76. package/dist/dev-server-Bs_sz2DG.d.ts +111 -0
  77. package/dist/dev-server-CjoufJ-u.d.mts +109 -0
  78. package/dist/dev-server-CjoufJ-u.d.ts +109 -0
  79. package/dist/dev-server.d.mts +3 -0
  80. package/dist/dev-server.d.ts +3 -0
  81. package/dist/dev-server.js +7603 -0
  82. package/dist/dev-server.mjs +11 -0
  83. package/dist/envelope-DD7v0v6E.d.mts +265 -0
  84. package/dist/envelope-DD7v0v6E.d.ts +265 -0
  85. package/dist/envelope-vCVjrHlo.d.mts +265 -0
  86. package/dist/envelope-vCVjrHlo.d.ts +265 -0
  87. package/dist/envelope.d.mts +2 -0
  88. package/dist/envelope.d.ts +2 -0
  89. package/dist/envelope.js +5184 -0
  90. package/dist/envelope.mjs +9 -0
  91. package/dist/index-B5gSgvnd.d.mts +44 -0
  92. package/dist/index-B5gSgvnd.d.ts +44 -0
  93. package/dist/index-Bs0MnR54.d.mts +103 -0
  94. package/dist/index-Bs0MnR54.d.ts +103 -0
  95. package/dist/index-DR0nNc_f.d.mts +101 -0
  96. package/dist/index-DR0nNc_f.d.ts +101 -0
  97. package/dist/index-revho_gS.d.mts +104 -0
  98. package/dist/index-revho_gS.d.ts +104 -0
  99. package/dist/index.d.mts +1099 -0
  100. package/dist/index.d.ts +1099 -0
  101. package/dist/index.js +10162 -0
  102. package/dist/index.mjs +372 -0
  103. package/dist/init-IXEE2RCF.mjs +340 -0
  104. package/dist/project-compiler-EGJUTAJU.mjs +10 -0
  105. package/dist/project-compiler-VFR6CSDX.mjs +10 -0
  106. package/dist/project-decompiler-5GY2KSG4.mjs +7 -0
  107. package/dist/pull-A2QUHW4K.mjs +109 -0
  108. package/dist/pull-JBEQWVPE.mjs +109 -0
  109. package/dist/testing/index.d.mts +211 -0
  110. package/dist/testing/index.d.ts +211 -0
  111. package/dist/testing/index.js +5106 -0
  112. package/dist/testing/index.mjs +247 -0
  113. package/dist/vite/index.d.mts +59 -0
  114. package/dist/vite/index.d.ts +59 -0
  115. package/dist/vite/index.js +5023 -0
  116. package/dist/vite/index.mjs +8 -0
  117. package/examples/README.md +72 -0
  118. package/examples/authentication/main.workflow.tsx +139 -0
  119. package/examples/authentication/mm.config.ts +22 -0
  120. package/examples/authentication/models/auth.ts +45 -0
  121. package/examples/authentication/pages/LoginPage.tsx +79 -0
  122. package/examples/authentication/pages/SignupPage.tsx +87 -0
  123. package/examples/counter.workflow.tsx +65 -0
  124. package/examples/dashboard.workflow.tsx +419 -0
  125. package/examples/invoice-approval/actions/invoice.server.ts +72 -0
  126. package/examples/invoice-approval/main.workflow.tsx +168 -0
  127. package/examples/invoice-approval/mm.config.ts +18 -0
  128. package/examples/invoice-approval/models/invoice.ts +46 -0
  129. package/examples/invoice-approval/pages/InvoiceDetailPage.tsx +175 -0
  130. package/examples/invoice-approval/pages/InvoiceFormPage.tsx +198 -0
  131. package/examples/invoice-approval/pages/InvoiceListPage.tsx +141 -0
  132. package/examples/todo-app.workflow.tsx +131 -0
  133. package/examples/uber-app/actions/matching.server.ts +177 -0
  134. package/examples/uber-app/actions/notifications.server.ts +176 -0
  135. package/examples/uber-app/actions/payments.server.ts +184 -0
  136. package/examples/uber-app/actions/pricing.server.ts +176 -0
  137. package/examples/uber-app/app/admin/analytics.tsx +102 -0
  138. package/examples/uber-app/app/admin/fleet.tsx +102 -0
  139. package/examples/uber-app/app/admin/surge-pricing.tsx +95 -0
  140. package/examples/uber-app/app/driver/dashboard.tsx +87 -0
  141. package/examples/uber-app/app/driver/earnings.tsx +101 -0
  142. package/examples/uber-app/app/driver/navigation.tsx +94 -0
  143. package/examples/uber-app/app/driver/ride-acceptance.tsx +103 -0
  144. package/examples/uber-app/app/rider/home.tsx +109 -0
  145. package/examples/uber-app/app/rider/payment-methods.tsx +134 -0
  146. package/examples/uber-app/app/rider/ride-history.tsx +90 -0
  147. package/examples/uber-app/app/rider/ride-tracking.tsx +108 -0
  148. package/examples/uber-app/components/DriverCard.tsx +176 -0
  149. package/examples/uber-app/components/MapView.tsx +216 -0
  150. package/examples/uber-app/components/RatingStars.tsx +227 -0
  151. package/examples/uber-app/components/RideCard.tsx +167 -0
  152. package/examples/uber-app/mm.config.ts +30 -0
  153. package/examples/uber-app/models/location.model.ts +70 -0
  154. package/examples/uber-app/models/payment.model.ts +87 -0
  155. package/examples/uber-app/models/rating.model.ts +54 -0
  156. package/examples/uber-app/models/ride.model.ts +118 -0
  157. package/examples/uber-app/models/user.model.ts +66 -0
  158. package/examples/uber-app/models/vehicle.model.ts +63 -0
  159. package/examples/uber-app/tests/payment.test.tsx +129 -0
  160. package/examples/uber-app/tests/ride-flow.test.tsx +123 -0
  161. package/examples/uber-app/workflows/dispute-resolution.workflow.tsx +205 -0
  162. package/examples/uber-app/workflows/driver-onboarding.workflow.tsx +227 -0
  163. package/examples/uber-app/workflows/payment-processing.workflow.tsx +223 -0
  164. package/examples/uber-app/workflows/ride-request.workflow.tsx +194 -0
  165. package/package.json +77 -0
  166. package/package.json.backup +86 -0
  167. package/scripts/decompile.ts +226 -0
  168. package/scripts/seed-auth.ts +267 -0
  169. package/scripts/seed-uber.ts +248 -0
  170. package/scripts/validate-uber.ts +119 -0
  171. package/seed-blueprint-chat.mjs +444 -0
  172. package/seed-blueprint-glass-console.mjs +445 -0
  173. package/seed-compiled.mjs +318 -0
  174. package/src/RoundTripValidator.ts +400 -0
  175. package/src/__tests__/atom-rendering-coverage.test.ts +680 -0
  176. package/src/__tests__/auth-module-compilation.test.ts +247 -0
  177. package/src/__tests__/auth-template-compilation.test.ts +589 -0
  178. package/src/__tests__/change-extractor.test.ts +142 -0
  179. package/src/__tests__/cli-pull.test.ts +73 -0
  180. package/src/__tests__/cli-test.test.ts +72 -0
  181. package/src/__tests__/component-extractor.test.ts +331 -0
  182. package/src/__tests__/context-extractor.test.ts +145 -0
  183. package/src/__tests__/decompiler.test.ts +718 -0
  184. package/src/__tests__/define-blueprint.test.ts +133 -0
  185. package/src/__tests__/definition-validator.test.ts +519 -0
  186. package/src/__tests__/during-extractor.test.ts +152 -0
  187. package/src/__tests__/effect-extractor.test.ts +107 -0
  188. package/src/__tests__/event-emission.test.ts +127 -0
  189. package/src/__tests__/examples.test.ts +236 -0
  190. package/src/__tests__/full-blueprint-coverage.test.ts +1221 -0
  191. package/src/__tests__/golden-suite.test.ts +403 -0
  192. package/src/__tests__/grammar-island-extractor.test.ts +289 -0
  193. package/src/__tests__/instance-key.test.ts +82 -0
  194. package/src/__tests__/ir-migration.test.ts +255 -0
  195. package/src/__tests__/lock-file.test.ts +117 -0
  196. package/src/__tests__/model-extractor.test.ts +195 -0
  197. package/src/__tests__/model-field-acl.test.ts +237 -0
  198. package/src/__tests__/model-hooks.test.ts +130 -0
  199. package/src/__tests__/model-ref-resolution.test.ts +268 -0
  200. package/src/__tests__/model-roundtrip.test.ts +502 -0
  201. package/src/__tests__/model-runtime.test.ts +112 -0
  202. package/src/__tests__/model-transitions.test.ts +183 -0
  203. package/src/__tests__/nrt-action-trace.test.ts +391 -0
  204. package/src/__tests__/pipeline-hardening.test.ts +413 -0
  205. package/src/__tests__/project-compiler.test.ts +546 -0
  206. package/src/__tests__/project-decompiler.test.ts +343 -0
  207. package/src/__tests__/query-compilation.test.ts +145 -0
  208. package/src/__tests__/round-trip/PLAN.md +158 -0
  209. package/src/__tests__/round-trip/README.md +52 -0
  210. package/src/__tests__/round-trip/RESULTS.md +86 -0
  211. package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +55 -0
  212. package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +11 -0
  213. package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +54 -0
  214. package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +79 -0
  215. package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +12 -0
  216. package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +50 -0
  217. package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +25 -0
  218. package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +11 -0
  219. package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +32 -0
  220. package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +79 -0
  221. package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +10 -0
  222. package/src/__tests__/round-trip/round-trip.test.ts +2598 -0
  223. package/src/__tests__/round-trip-ir.test.ts +300 -0
  224. package/src/__tests__/round-trip.test.ts +1212 -0
  225. package/src/__tests__/route-merging.test.ts +372 -0
  226. package/src/__tests__/router-composition.test.ts +489 -0
  227. package/src/__tests__/router-extractor.test.ts +176 -0
  228. package/src/__tests__/server-action-extractor.test.ts +128 -0
  229. package/src/__tests__/smart-type-inference.test.ts +365 -0
  230. package/src/__tests__/source-envelope.test.ts +284 -0
  231. package/src/__tests__/source-fidelity.test.ts +516 -0
  232. package/src/__tests__/state-extractor.test.ts +115 -0
  233. package/src/__tests__/strict-mode.test.ts +227 -0
  234. package/src/__tests__/transition-effect-extractor.test.ts +119 -0
  235. package/src/__tests__/transition-extractor.test.ts +68 -0
  236. package/src/__tests__/ts-to-expression.test.ts +462 -0
  237. package/src/__tests__/type-generator.test.ts +201 -0
  238. package/src/__tests__/uber-validation.test.ts +502 -0
  239. package/src/action-compiler.ts +361 -0
  240. package/src/babel/emitters/experience-transform.ts +199 -0
  241. package/src/babel/emitters/ir-to-tsx-emitter.ts +110 -0
  242. package/src/babel/emitters/pure-form-emitter.ts +1023 -0
  243. package/src/babel/emitters/runtime-glue-emitter.ts +39 -0
  244. package/src/babel/extractors/change-extractor.ts +199 -0
  245. package/src/babel/extractors/component-extractor.ts +907 -0
  246. package/src/babel/extractors/computed-extractor.ts +262 -0
  247. package/src/babel/extractors/context-extractor.ts +277 -0
  248. package/src/babel/extractors/during-extractor.ts +295 -0
  249. package/src/babel/extractors/effect-extractor.ts +340 -0
  250. package/src/babel/extractors/event-extractor.ts +235 -0
  251. package/src/babel/extractors/grammar-island-extractor.ts +302 -0
  252. package/src/babel/extractors/model-extractor.ts +1018 -0
  253. package/src/babel/extractors/router-extractor.ts +303 -0
  254. package/src/babel/extractors/server-action-extractor.ts +173 -0
  255. package/src/babel/extractors/server-action-hook-extractor.ts +72 -0
  256. package/src/babel/extractors/server-state-extractor.ts +88 -0
  257. package/src/babel/extractors/state-extractor.ts +214 -0
  258. package/src/babel/extractors/transition-effect-extractor.ts +176 -0
  259. package/src/babel/extractors/transition-extractor.ts +143 -0
  260. package/src/babel/index.ts +24 -0
  261. package/src/babel/transpilers/ts-to-expression.ts +674 -0
  262. package/src/babel/visitor.ts +807 -0
  263. package/src/cli/auth.ts +255 -0
  264. package/src/cli/build.ts +288 -0
  265. package/src/cli/deploy.ts +206 -0
  266. package/src/cli/index.ts +328 -0
  267. package/src/cli/init.ts +388 -0
  268. package/src/cli/installer.ts +261 -0
  269. package/src/cli/lock-file.ts +94 -0
  270. package/src/cli/mmrc.ts +22 -0
  271. package/src/cli/pull.ts +172 -0
  272. package/src/cli/registry-client.ts +175 -0
  273. package/src/cli/test.ts +397 -0
  274. package/src/cli/type-generator.ts +243 -0
  275. package/src/codemod/__tests__/forward.test.ts +239 -0
  276. package/src/codemod/__tests__/reverse.test.ts +145 -0
  277. package/src/codemod/__tests__/round-trip.test.ts +137 -0
  278. package/src/codemod/annotation.ts +97 -0
  279. package/src/codemod/classify.ts +197 -0
  280. package/src/codemod/cli.ts +207 -0
  281. package/src/codemod/control-flow.ts +409 -0
  282. package/src/codemod/forward.ts +244 -0
  283. package/src/codemod/import-manager.ts +171 -0
  284. package/src/codemod/index.ts +120 -0
  285. package/src/codemod/reverse.ts +197 -0
  286. package/src/codemod/rules.ts +174 -0
  287. package/src/codemod/state-transform.ts +126 -0
  288. package/src/decompiler/ast-builder.ts +538 -0
  289. package/src/decompiler/config-generator.ts +151 -0
  290. package/src/decompiler/index.ts +315 -0
  291. package/src/decompiler/project-decompiler.ts +1776 -0
  292. package/src/decompiler/project.ts +862 -0
  293. package/src/decompiler/split-strategy.ts +140 -0
  294. package/src/decompiler/state-emitter.ts +1053 -0
  295. package/src/decompiler/sx-emitter.ts +318 -0
  296. package/src/decompiler/workspace-hydrator.ts +189 -0
  297. package/src/dev-server.ts +238 -0
  298. package/src/envelope/fs-tree.ts +217 -0
  299. package/src/envelope/source-envelope.ts +264 -0
  300. package/src/envelope.ts +315 -0
  301. package/src/incremental-compiler.ts +401 -0
  302. package/src/index.ts +99 -0
  303. package/src/model-compiler.ts +277 -0
  304. package/src/project-compiler.ts +1629 -0
  305. package/src/route-extractor.ts +333 -0
  306. package/src/testing/index.ts +32 -0
  307. package/src/testing/snapshot.ts +252 -0
  308. package/src/testing/test-utils.ts +226 -0
  309. package/src/types.ts +68 -0
  310. package/src/vite/index.ts +288 -0
  311. package/test-compile.mjs +142 -0
  312. package/tsconfig.json +25 -0
  313. package/tsup.config.ts +23 -0
  314. package/vitest.config.ts +9 -0
@@ -0,0 +1,419 @@
1
+ /**
2
+ * @workflow slug="dashboard" version="1.0.0" category="blueprint"
3
+ * @description Real-world dashboard blueprint with multi-route layout, role-based access,
4
+ * data source bindings, state management, and conditional rendering based on state.
5
+ */
6
+ import { useState } from 'react';
7
+ import {
8
+ useTransition,
9
+ useOnEnter,
10
+ useOnExit,
11
+ useOnChange,
12
+ useWhileIn,
13
+ useOnEvent,
14
+ useQuery,
15
+ useMutation,
16
+ useRole,
17
+ } from '@mindmatrix/react';
18
+
19
+ export function Dashboard() {
20
+ // --- Fields ---
21
+ const [activeRoute, setActiveRoute] = useState('/dashboard');
22
+ const [sidebarOpen, setSidebarOpen] = useState(true);
23
+ const [searchQuery, setSearchQuery] = useState('');
24
+ const [notificationCount, setNotificationCount] = useState(0);
25
+ const [selectedRecordId, setSelectedRecordId] = useState('');
26
+ const [theme, setTheme] = useState('light');
27
+ const [refreshInterval, setRefreshInterval] = useState(30000);
28
+ const [lastRefreshed, setLastRefreshed] = useState('');
29
+
30
+ // --- Data Sources ---
31
+
32
+ // Main metrics for the dashboard overview
33
+ const { data: metrics } = useQuery('dashboard-metric', {
34
+ limit: 20,
35
+ orderBy: 'priority',
36
+ filter: { visible: true },
37
+ });
38
+
39
+ // Recent activity feed
40
+ const { data: activities } = useQuery('activity-log', {
41
+ limit: 50,
42
+ orderBy: 'created_at',
43
+ order: 'desc',
44
+ filter: { archived: false },
45
+ });
46
+
47
+ // Workflow definitions for the data browser
48
+ const { data: definitions } = useQuery('workflow-definition', {
49
+ limit: 100,
50
+ filter: { category: 'data' },
51
+ orderBy: 'name',
52
+ });
53
+
54
+ // Notifications
55
+ const { data: notifications } = useQuery('notification', {
56
+ limit: 10,
57
+ filter: { read: false },
58
+ orderBy: 'created_at',
59
+ order: 'desc',
60
+ });
61
+
62
+ // User settings
63
+ const { data: userSettings } = useQuery('user-setting', {
64
+ limit: 1,
65
+ filter: { scope: 'dashboard' },
66
+ });
67
+
68
+ const settingsMutation = useMutation('user-setting');
69
+ const activityMutation = useMutation('activity-log');
70
+
71
+ // --- Roles ---
72
+ const isAdmin = useRole('admin');
73
+ const isViewer = useRole('viewer');
74
+ const isEditor = useRole('editor');
75
+
76
+ // --- States & Hooks ---
77
+
78
+ // When the dashboard initializes, clear stale state
79
+ useOnEnter('active', () => {
80
+ setNotificationCount(0);
81
+ setLastRefreshed('');
82
+ });
83
+
84
+ // When entering settings, record the visit
85
+ useOnEnter('settings', () => {
86
+ setSearchQuery('');
87
+ });
88
+
89
+ // When leaving active state, persist preferences
90
+ useOnExit('active', () => {
91
+ setSelectedRecordId('');
92
+ });
93
+
94
+ // Auto-refresh metrics while dashboard is active
95
+ useWhileIn('active', 30000, () => {
96
+ setLastRefreshed('now');
97
+ });
98
+
99
+ // Track route changes for analytics
100
+ useOnChange('activeRoute', () => {
101
+ setSearchQuery('');
102
+ });
103
+
104
+ // Track notification count changes
105
+ useOnChange('notificationCount', () => {
106
+ // Could trigger toast or badge update
107
+ });
108
+
109
+ // Listen for real-time updates from the backend
110
+ useOnEvent('data:updated', () => {
111
+ setLastRefreshed('now');
112
+ });
113
+
114
+ useOnEvent('notification:received', () => {
115
+ setNotificationCount(notificationCount + 1);
116
+ });
117
+
118
+ // --- Transitions ---
119
+
120
+ // Lifecycle transitions
121
+ useTransition('initialize', { from: 'draft', to: 'active' });
122
+ useTransition('suspend', { from: 'active', to: 'suspended' });
123
+ useTransition('resume', { from: 'suspended', to: 'active' });
124
+ useTransition('deactivate', { from: 'active', to: 'archived' });
125
+
126
+ // Route transitions (self-transitions that change activeRoute)
127
+ useTransition('navigate', { from: 'active', to: 'active' });
128
+
129
+ // Settings mode
130
+ useTransition('open-settings', { from: 'active', to: 'settings' });
131
+ useTransition('close-settings', { from: 'settings', to: 'active' });
132
+
133
+ // --- Derived state ---
134
+ const isOnDashboard = activeRoute === '/dashboard';
135
+ const isOnSettings = activeRoute === '/settings';
136
+ const isOnData = activeRoute === '/data';
137
+ const hasNotifications = notificationCount > 0;
138
+
139
+ // --- UI ---
140
+
141
+ return (
142
+ <Stack className="dashboard-app" sx={{ height: '100vh', direction: 'row' }}>
143
+ {/* ---- Sidebar Navigation ---- */}
144
+ {sidebarOpen && (
145
+ <Stack sx={{ width: '240px', bg: 'gray.900', color: 'white', p: 4, gap: 2 }}>
146
+ <Heading sx={{ fontSize: 'lg', fontWeight: 'bold', mb: 4 }}>
147
+ MindMatrix
148
+ </Heading>
149
+
150
+ {/* Nav items */}
151
+ <Button
152
+ onClick={() => setActiveRoute('/dashboard')}
153
+ sx={{ variant: isOnDashboard ? 'solid' : 'ghost', colorScheme: 'blue', justifyContent: 'start' }}
154
+ >
155
+ Dashboard
156
+ </Button>
157
+
158
+ <Button
159
+ onClick={() => setActiveRoute('/data')}
160
+ sx={{ variant: isOnData ? 'solid' : 'ghost', colorScheme: 'blue', justifyContent: 'start' }}
161
+ >
162
+ Data Browser
163
+ </Button>
164
+
165
+ <Button
166
+ onClick={() => setActiveRoute('/settings')}
167
+ sx={{ variant: isOnSettings ? 'solid' : 'ghost', colorScheme: 'blue', justifyContent: 'start' }}
168
+ >
169
+ Settings
170
+ </Button>
171
+
172
+ <Spacer />
173
+
174
+ {/* Notification badge */}
175
+ {hasNotifications && (
176
+ <Badge sx={{ colorScheme: 'red' }}>
177
+ {notificationCount} new
178
+ </Badge>
179
+ )}
180
+
181
+ {/* Admin-only: system status */}
182
+ {isAdmin && (
183
+ <Card sx={{ bg: 'gray.800', p: 2 }}>
184
+ <Text sx={{ fontSize: 'xs', color: 'gray.400' }}>System Status</Text>
185
+ <Text sx={{ fontSize: 'sm', color: 'green.400' }}>All services operational</Text>
186
+ </Card>
187
+ )}
188
+ </Stack>
189
+ )}
190
+
191
+ {/* ---- Main Content Area ---- */}
192
+ <Stack sx={{ flex: 1, overflow: 'auto' }}>
193
+ {/* Top bar */}
194
+ <Stack sx={{ direction: 'row', alignItems: 'center', p: 4, borderBottom: '1px solid', borderColor: 'gray.200', gap: 4 }}>
195
+ <Button onClick={() => setSidebarOpen(!sidebarOpen)} sx={{ variant: 'ghost', size: 'sm' }}>
196
+ Menu
197
+ </Button>
198
+
199
+ <TextInput
200
+ bind="searchQuery"
201
+ placeholder="Search..."
202
+ sx={{ flex: 1, maxWidth: '400px' }}
203
+ />
204
+
205
+ <Spacer />
206
+
207
+ <Text sx={{ fontSize: 'sm', color: 'gray.500' }}>
208
+ Last refreshed: {lastRefreshed}
209
+ </Text>
210
+
211
+ {isAdmin && (
212
+ <Button onClick={() => setRefreshInterval(15000)} sx={{ variant: 'outline', size: 'sm' }}>
213
+ Turbo Refresh
214
+ </Button>
215
+ )}
216
+ </Stack>
217
+
218
+ {/* ---- Route: /dashboard ---- */}
219
+ {isOnDashboard && (
220
+ <Stack sx={{ p: 6, gap: 6 }}>
221
+ <Heading sx={{ fontSize: '2xl', fontWeight: 'bold' }}>
222
+ Dashboard Overview
223
+ </Heading>
224
+
225
+ {/* Metrics grid */}
226
+ <Grid sx={{ columns: 4, gap: 4 }}>
227
+ <Each items={metrics}>
228
+ {(metric: any) => (
229
+ <MetricCard
230
+ label={metric.name}
231
+ value={metric.value}
232
+ format={metric.format}
233
+ trend={metric.trend}
234
+ sparkline={true}
235
+ sx={{ bg: 'white', shadow: 'sm', rounded: 'lg', p: 4 }}
236
+ />
237
+ )}
238
+ </Each>
239
+ </Grid>
240
+
241
+ {/* Activity + Notifications side by side */}
242
+ <Stack sx={{ direction: 'row', gap: 6 }}>
243
+ {/* Recent activity */}
244
+ <Stack sx={{ flex: 2, gap: 3 }}>
245
+ <Heading sx={{ fontSize: 'lg', fontWeight: 'semibold' }}>
246
+ Recent Activity
247
+ </Heading>
248
+ <ScrollArea sx={{ maxHeight: '400px' }}>
249
+ <Each items={activities}>
250
+ {(activity: any) => (
251
+ <Stack sx={{ direction: 'row', gap: 3, py: 2, borderBottom: '1px solid', borderColor: 'gray.100' }}>
252
+ <Badge variant={activity.type === 'error' ? 'error' : 'default'}>
253
+ {activity.type}
254
+ </Badge>
255
+ <Stack sx={{ flex: 1 }}>
256
+ <Text sx={{ fontWeight: 'medium' }}>{activity.title}</Text>
257
+ <Text sx={{ fontSize: 'sm', color: 'gray.500' }}>{activity.description}</Text>
258
+ </Stack>
259
+ {isEditor && (
260
+ <Button sx={{ variant: 'ghost', size: 'sm' }}>
261
+ View
262
+ </Button>
263
+ )}
264
+ </Stack>
265
+ )}
266
+ </Each>
267
+ </ScrollArea>
268
+ </Stack>
269
+
270
+ {/* Notifications panel */}
271
+ <Stack sx={{ flex: 1, gap: 3 }}>
272
+ <Heading sx={{ fontSize: 'lg', fontWeight: 'semibold' }}>
273
+ Notifications
274
+ </Heading>
275
+ <Each items={notifications}>
276
+ {(notification: any) => (
277
+ <Card sx={{ p: 3, mb: 2 }}>
278
+ <Text sx={{ fontWeight: 'medium' }}>{notification.title}</Text>
279
+ <Text sx={{ fontSize: 'sm', color: 'gray.500' }}>{notification.message}</Text>
280
+ </Card>
281
+ )}
282
+ </Each>
283
+ {!notifications && (
284
+ <Text sx={{ color: 'gray.400', textAlign: 'center', py: 4 }}>
285
+ No new notifications
286
+ </Text>
287
+ )}
288
+ </Stack>
289
+ </Stack>
290
+
291
+ {/* Admin-only: system charts */}
292
+ {isAdmin && (
293
+ <Stack sx={{ gap: 4 }}>
294
+ <Heading sx={{ fontSize: 'lg', fontWeight: 'semibold' }}>
295
+ System Analytics
296
+ </Heading>
297
+ <Stack sx={{ direction: 'row', gap: 4 }}>
298
+ <Chart type="line" bind="metrics" xField="date" yField="value" sx={{ flex: 1, height: '300px' }} />
299
+ <Chart type="bar" bind="activities" xField="type" yField="count" sx={{ flex: 1, height: '300px' }} />
300
+ </Stack>
301
+ </Stack>
302
+ )}
303
+ </Stack>
304
+ )}
305
+
306
+ {/* ---- Route: /data ---- */}
307
+ {isOnData && (
308
+ <Stack sx={{ p: 6, gap: 4 }}>
309
+ <Stack sx={{ direction: 'row', alignItems: 'center', gap: 4 }}>
310
+ <Heading sx={{ fontSize: '2xl', fontWeight: 'bold' }}>
311
+ Data Browser
312
+ </Heading>
313
+ {isEditor && (
314
+ <Button sx={{ colorScheme: 'blue' }}>
315
+ New Definition
316
+ </Button>
317
+ )}
318
+ </Stack>
319
+
320
+ {/* Definition list with server grid */}
321
+ <ServerGrid
322
+ bind="definitions"
323
+ endpoint="/api/v1/workflow/definitions"
324
+ columns={[
325
+ { field: 'name', header: 'Name', sortable: true },
326
+ { field: 'category', header: 'Category', sortable: true },
327
+ { field: 'version', header: 'Version', sortable: false },
328
+ { field: 'updated_at', header: 'Last Updated', sortable: true },
329
+ ]}
330
+ pageSize={25}
331
+ />
332
+
333
+ {/* Viewer restriction notice */}
334
+ {isViewer && !isEditor && (
335
+ <Card sx={{ bg: 'yellow.50', p: 4 }}>
336
+ <Text sx={{ color: 'yellow.800' }}>
337
+ You have read-only access. Contact an admin to request edit permissions.
338
+ </Text>
339
+ </Card>
340
+ )}
341
+ </Stack>
342
+ )}
343
+
344
+ {/* ---- Route: /settings ---- */}
345
+ {isOnSettings && (
346
+ <Stack sx={{ p: 6, gap: 6, maxWidth: '800px' }}>
347
+ <Heading sx={{ fontSize: '2xl', fontWeight: 'bold' }}>
348
+ Settings
349
+ </Heading>
350
+
351
+ {/* General settings */}
352
+ <Section title="General" description="Configure your dashboard preferences">
353
+ <Stack sx={{ gap: 4 }}>
354
+ <Stack sx={{ direction: 'row', alignItems: 'center', gap: 4 }}>
355
+ <Text sx={{ width: '150px', fontWeight: 'medium' }}>Theme</Text>
356
+ <Select
357
+ bind="theme"
358
+ options={['light', 'dark', 'system']}
359
+ />
360
+ </Stack>
361
+
362
+ <Stack sx={{ direction: 'row', alignItems: 'center', gap: 4 }}>
363
+ <Text sx={{ width: '150px', fontWeight: 'medium' }}>Refresh Rate</Text>
364
+ <Select
365
+ bind="refreshInterval"
366
+ options={['15000', '30000', '60000', '300000']}
367
+ />
368
+ </Stack>
369
+ </Stack>
370
+ </Section>
371
+
372
+ {/* Admin-only settings */}
373
+ {isAdmin && (
374
+ <Section title="Administration" description="System-level configuration (admin only)">
375
+ <Stack sx={{ gap: 4 }}>
376
+ <Card sx={{ p: 4 }}>
377
+ <Stack sx={{ gap: 3 }}>
378
+ <Text sx={{ fontWeight: 'medium' }}>User Management</Text>
379
+ <Text sx={{ fontSize: 'sm', color: 'gray.500' }}>
380
+ Manage users, roles, and permissions for this workspace.
381
+ </Text>
382
+ <Button sx={{ colorScheme: 'blue', variant: 'outline' }}>
383
+ Manage Users
384
+ </Button>
385
+ </Stack>
386
+ </Card>
387
+
388
+ <Card sx={{ p: 4 }}>
389
+ <Stack sx={{ gap: 3 }}>
390
+ <Text sx={{ fontWeight: 'medium' }}>Data Retention</Text>
391
+ <Text sx={{ fontSize: 'sm', color: 'gray.500' }}>
392
+ Configure how long activity logs and archived records are retained.
393
+ </Text>
394
+ <Button sx={{ colorScheme: 'red', variant: 'outline' }}>
395
+ Configure Retention
396
+ </Button>
397
+ </Stack>
398
+ </Card>
399
+ </Stack>
400
+ </Section>
401
+ )}
402
+
403
+ {/* Save button */}
404
+ {isEditor && (
405
+ <Stack sx={{ direction: 'row', justifyContent: 'end', gap: 2 }}>
406
+ <Button onClick={() => setActiveRoute('/dashboard')} sx={{ variant: 'outline' }}>
407
+ Cancel
408
+ </Button>
409
+ <Button sx={{ colorScheme: 'blue' }}>
410
+ Save Settings
411
+ </Button>
412
+ </Stack>
413
+ )}
414
+ </Stack>
415
+ )}
416
+ </Stack>
417
+ </Stack>
418
+ );
419
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Server actions — backend handlers for invoice approval transitions.
3
+ *
4
+ * These functions run server-side during state transitions.
5
+ * Each receives a TransitionContext with instance data and utilities.
6
+ */
7
+
8
+ import type { TransitionContext } from '@mindmatrix/react';
9
+
10
+ /** Send approval notification to the invoice submitter. */
11
+ export async function sendApprovalNotification(ctx: TransitionContext): Promise<void> {
12
+ const { instance, env } = ctx;
13
+ await env.notify({
14
+ message: `Invoice #${instance.id} has been approved`,
15
+ instanceId: instance.id,
16
+ });
17
+
18
+ // Also emit an event for audit trail
19
+ await env.emit('invoice:approved', {
20
+ instanceId: instance.id,
21
+ approvedBy: ctx.actor?.id,
22
+ approvedAt: new Date().toISOString(),
23
+ });
24
+ }
25
+
26
+ /** Send rejection notification with reason. */
27
+ export async function sendRejectionNotification(ctx: TransitionContext): Promise<void> {
28
+ const { instance, env } = ctx;
29
+ const reason = instance.state_data?.rejectionReason ?? 'No reason provided';
30
+
31
+ await env.notify({
32
+ message: `Invoice #${instance.id} was rejected: ${reason}`,
33
+ instanceId: instance.id,
34
+ });
35
+
36
+ await env.emit('invoice:rejected', {
37
+ instanceId: instance.id,
38
+ rejectedBy: ctx.actor?.id,
39
+ reason,
40
+ });
41
+ }
42
+
43
+ /** Validate invoice data before submission. */
44
+ export async function validateInvoice(ctx: TransitionContext): Promise<void> {
45
+ const { instance } = ctx;
46
+ const data = instance.state_data;
47
+
48
+ if (!data?.amount || data.amount <= 0) {
49
+ throw new Error('Invoice amount must be greater than zero');
50
+ }
51
+ if (!data?.vendor) {
52
+ throw new Error('Vendor name is required');
53
+ }
54
+ if (!data?.date) {
55
+ throw new Error('Invoice date is required');
56
+ }
57
+ }
58
+
59
+ /** Send webhook to accounting system on approval. */
60
+ export async function syncToAccounting(ctx: TransitionContext): Promise<void> {
61
+ const { instance } = ctx;
62
+ await fetch('https://api.example.com/accounting/invoices', {
63
+ method: 'POST',
64
+ headers: { 'Content-Type': 'application/json' },
65
+ body: JSON.stringify({
66
+ externalId: instance.id,
67
+ amount: instance.state_data?.amount,
68
+ vendor: instance.state_data?.vendor,
69
+ approvedAt: new Date().toISOString(),
70
+ }),
71
+ });
72
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @workflow slug="invoice-approval" version="1.0.0" category="blueprint"
3
+ * @description Invoice approval workflow with multi-page views, data model, and server actions.
4
+ *
5
+ * States: draft → submitted → approved | rejected
6
+ * Pages: InvoiceList, InvoiceDetail, InvoiceForm
7
+ * Model: Invoice (amount, vendor, date, status, notes)
8
+ * Server Action: sendNotification (on approval/rejection)
9
+ */
10
+ import {
11
+ Stack,
12
+ Row,
13
+ Heading,
14
+ Text,
15
+ Button,
16
+ Show,
17
+ useOnEnter,
18
+ useOnExit,
19
+ useTransition,
20
+ useRole,
21
+ } from '@mindmatrix/react';
22
+ import { useState } from 'react';
23
+ import type { Invoice } from './models/invoice';
24
+ import { sendApprovalNotification, sendRejectionNotification } from './actions/invoice.server';
25
+ import { InvoiceListPage } from './pages/InvoiceListPage';
26
+ import { InvoiceDetailPage } from './pages/InvoiceDetailPage';
27
+ import { InvoiceFormPage } from './pages/InvoiceFormPage';
28
+
29
+ export default function InvoiceApproval() {
30
+ // --- Fields ---
31
+ const [activeRoute, setActiveRoute] = useState('/invoices');
32
+ const [selectedInvoiceId, setSelectedInvoiceId] = useState('');
33
+ const [filterStatus, setFilterStatus] = useState('all');
34
+ const [totalPending, setTotalPending] = useState(0);
35
+ const [lastAction, setLastAction] = useState('');
36
+
37
+ // --- Roles ---
38
+ const isApprover = useRole('approver');
39
+ const isSubmitter = useRole('submitter');
40
+
41
+ // --- State Effects ---
42
+ useOnEnter('draft', () => {
43
+ setLastAction('created');
44
+ });
45
+
46
+ useOnEnter('submitted', () => {
47
+ setLastAction('submitted');
48
+ setTotalPending(totalPending + 1);
49
+ });
50
+
51
+ useOnEnter('approved', () => {
52
+ setLastAction('approved');
53
+ setTotalPending(Math.max(0, totalPending - 1));
54
+ });
55
+
56
+ useOnEnter('rejected', () => {
57
+ setLastAction('rejected');
58
+ setTotalPending(Math.max(0, totalPending - 1));
59
+ });
60
+
61
+ useOnExit('draft', () => {
62
+ // Clear form state when leaving draft
63
+ setSelectedInvoiceId('');
64
+ });
65
+
66
+ // --- Transitions ---
67
+ const submit = useTransition('submit', {
68
+ from: 'draft',
69
+ to: 'submitted',
70
+ requiredFields: ['amount', 'vendor', 'date'],
71
+ roles: ['submitter'],
72
+ });
73
+
74
+ const approve = useTransition('approve', {
75
+ from: 'submitted',
76
+ to: 'approved',
77
+ roles: ['approver'],
78
+ });
79
+
80
+ const reject = useTransition('reject', {
81
+ from: 'submitted',
82
+ to: 'rejected',
83
+ roles: ['approver'],
84
+ });
85
+
86
+ const revise = useTransition('revise', {
87
+ from: 'rejected',
88
+ to: 'draft',
89
+ roles: ['submitter'],
90
+ });
91
+
92
+ // --- Navigation ---
93
+ const isOnList = activeRoute === '/invoices';
94
+ const isOnDetail = activeRoute === '/invoices/detail';
95
+ const isOnForm = activeRoute === '/invoices/new';
96
+
97
+ return (
98
+ <Stack sx={{ minHeight: '100vh', bg: 'gray.50' }}>
99
+ {/* Header */}
100
+ <Row sx={{ alignItems: 'center', justifyContent: 'space-between', p: 16, bg: 'white', borderBottom: '1px solid', borderColor: 'gray.200' }}>
101
+ <Row sx={{ alignItems: 'center', gap: 12 }}>
102
+ <Heading sx={{ fontSize: 20, fontWeight: 'bold' }}>Invoice Approval</Heading>
103
+ <Show when={totalPending > 0}>
104
+ <Text sx={{ bg: 'orange.100', color: 'orange.800', px: 8, py: 2, rounded: 'full', fontSize: 12 }}>
105
+ {totalPending} pending
106
+ </Text>
107
+ </Show>
108
+ </Row>
109
+ <Row sx={{ gap: 8 }}>
110
+ <Button
111
+ onClick={() => setActiveRoute('/invoices')}
112
+ sx={{ variant: isOnList ? 'solid' : 'ghost' }}
113
+ >
114
+ All Invoices
115
+ </Button>
116
+ <Show when={isSubmitter}>
117
+ <Button
118
+ onClick={() => setActiveRoute('/invoices/new')}
119
+ sx={{ variant: isOnForm ? 'solid' : 'ghost', colorScheme: 'blue' }}
120
+ >
121
+ New Invoice
122
+ </Button>
123
+ </Show>
124
+ </Row>
125
+ </Row>
126
+
127
+ {/* Route Pages */}
128
+ <Show when={isOnList}>
129
+ <InvoiceListPage
130
+ filterStatus={filterStatus}
131
+ onFilterChange={setFilterStatus}
132
+ onSelect={(id: string) => {
133
+ setSelectedInvoiceId(id);
134
+ setActiveRoute('/invoices/detail');
135
+ }}
136
+ />
137
+ </Show>
138
+
139
+ <Show when={isOnDetail}>
140
+ <InvoiceDetailPage
141
+ invoiceId={selectedInvoiceId}
142
+ isApprover={isApprover}
143
+ onApprove={() => approve.fire()}
144
+ onReject={() => reject.fire()}
145
+ onRevise={() => revise.fire()}
146
+ onBack={() => setActiveRoute('/invoices')}
147
+ />
148
+ </Show>
149
+
150
+ <Show when={isOnForm}>
151
+ <InvoiceFormPage
152
+ onSubmit={() => {
153
+ submit.fire();
154
+ setActiveRoute('/invoices');
155
+ }}
156
+ onCancel={() => setActiveRoute('/invoices')}
157
+ />
158
+ </Show>
159
+
160
+ {/* Status bar */}
161
+ <Row sx={{ p: 8, bg: 'gray.100', justifyContent: 'end', gap: 8, mt: 'auto' }}>
162
+ <Text sx={{ fontSize: 11, color: 'gray.500' }}>
163
+ Last action: {lastAction || 'none'}
164
+ </Text>
165
+ </Row>
166
+ </Stack>
167
+ );
168
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Blueprint configuration for "Invoice Approval".
3
+ */
4
+
5
+ import { defineBlueprint } from '@mindmatrix/react';
6
+
7
+ export default defineBlueprint({
8
+ slug: 'invoice-approval',
9
+ name: 'Invoice Approval',
10
+ version: '1.0.0',
11
+ category: 'blueprint',
12
+ description: 'Invoice approval workflow with multi-page views, data model, and server actions.',
13
+ roles: [
14
+ { name: 'submitter', permissions: ['view', 'edit', 'transition'] },
15
+ { name: 'approver', permissions: ['view', 'transition'] },
16
+ { name: 'admin', permissions: ['view', 'edit', 'transition', 'delete'] },
17
+ ],
18
+ });