@askrjs/cli 0.0.2 → 0.0.4

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 (310) hide show
  1. package/README.md +46 -7
  2. package/dist/add.d.ts +16 -0
  3. package/dist/add.js +558 -1
  4. package/dist/cli.d.ts +5 -0
  5. package/dist/cli.js +43 -16
  6. package/dist/create.d.ts +5 -0
  7. package/dist/create.js +104 -26
  8. package/dist/directory-swap-DWoHtx7C.js +35 -0
  9. package/dist/discovery-Difb7Y4G.js +323 -0
  10. package/dist/generate.d.ts +5 -0
  11. package/dist/generate.js +461 -0
  12. package/dist/is-direct-execution-Cdlr-ZUl.js +0 -2
  13. package/dist/openapi.d.ts +25 -0
  14. package/dist/openapi.js +49 -16
  15. package/dist/planner-VAj7qlxr.js +285 -0
  16. package/dist/range-YUs9eimn.js +125 -0
  17. package/dist/registry-CyrRHRG5.js +209 -0
  18. package/dist/skills/askr-dashboard-charts/SKILL.md +61 -44
  19. package/dist/skills/askr-dashboard-charts/agents/openai.yaml +2 -2
  20. package/dist/skills/askr-ssr-ssg/SKILL.md +3 -1
  21. package/dist/{skills-Bh0mEhIx.js → skills-B7CbWur9.js} +89 -26
  22. package/dist/skills.d.ts +18 -0
  23. package/dist/skills.js +1 -1
  24. package/dist/specification-DXnDOC-0.js +49 -0
  25. package/dist/ssg-config.d.ts +41 -0
  26. package/dist/ssg-config.js +1 -0
  27. package/dist/ssg.d.ts +61 -0
  28. package/dist/ssg.js +360 -24
  29. package/dist/templates/full-stack/AGENTS.md +0 -1
  30. package/dist/templates/full-stack/README.md +0 -1
  31. package/dist/templates/full-stack/index.html +0 -1
  32. package/dist/templates/full-stack/package.json +5 -3
  33. package/dist/templates/full-stack/server.ts +5 -9
  34. package/dist/templates/full-stack/src/action-authorizations.ts +2 -2
  35. package/dist/templates/full-stack/src/actions/create-message.ts +5 -5
  36. package/dist/templates/full-stack/src/i18n.ts +4 -4
  37. package/dist/templates/full-stack/src/main.tsx +4 -5
  38. package/dist/templates/full-stack/src/pages/home.tsx +5 -5
  39. package/dist/templates/full-stack/src/pages/layout.tsx +15 -5
  40. package/dist/templates/full-stack/src/pages/not-found.tsx +5 -2
  41. package/dist/templates/full-stack/src/routes.tsx +11 -12
  42. package/dist/templates/full-stack/src/schemas.ts +1 -2
  43. package/dist/templates/full-stack/src/server/action-registry.ts +5 -10
  44. package/dist/templates/full-stack/src/server/actions/create-message.ts +4 -4
  45. package/dist/templates/full-stack/src/server/app.ts +67 -57
  46. package/dist/templates/full-stack/src/server/dependencies.ts +10 -10
  47. package/dist/templates/full-stack/src/server/entry-server.ts +3 -3
  48. package/dist/templates/full-stack/src/telemetry.ts +1 -2
  49. package/dist/templates/full-stack/tests/actions/create-message.test.ts +7 -8
  50. package/dist/templates/full-stack/tsconfig.json +1 -2
  51. package/dist/templates/full-stack/vite.config.ts +5 -6
  52. package/dist/templates/spa/AGENTS.md +1 -1
  53. package/dist/templates/spa/package.json +3 -4
  54. package/dist/templates/spa/src/adapters/operations-client.ts +7 -2
  55. package/dist/templates/spa/src/pages/app/_routes.tsx +2 -0
  56. package/dist/templates/spa/src/pages/app/admin-home.tsx +21 -3
  57. package/dist/templates/spa/src/pages/public/_routes.tsx +2 -0
  58. package/dist/templates/spa/src/styles.css +1 -1
  59. package/dist/templates/spa/tsconfig.json +2 -1
  60. package/dist/templates/spa/vitest.config.ts +0 -4
  61. package/dist/templates/ssg/README.md +1 -1
  62. package/dist/templates/ssg/package.json +1 -1
  63. package/dist/templates/ssg/ssg.config.ts +7 -0
  64. package/dist/templates/ssg/tests/ssg-config.test.ts +5 -3
  65. package/dist/templates/ssg/tsconfig.json +2 -1
  66. package/dist/templates/ssg/vitest.config.ts +0 -4
  67. package/dist/templates/ssr/package.json +2 -2
  68. package/dist/templates/ssr/server.ts +4 -10
  69. package/dist/templates/ssr/src/entry-server.tsx +6 -4
  70. package/dist/templates/startkit/package.json +2 -2
  71. package/dist/templates/startkit/src/lib/mock-data.ts +17 -39
  72. package/dist/templates/startkit/tsconfig.json +2 -1
  73. package/dist/update.d.ts +41 -0
  74. package/dist/update.js +282 -2
  75. package/dist/writer-D8qe_7ud.js +226 -0
  76. package/package.json +23 -22
  77. package/dist/add-DqhqRzBQ.js +0 -555
  78. package/dist/add-DqhqRzBQ.js.map +0 -1
  79. package/dist/cli.js.map +0 -1
  80. package/dist/create.js.map +0 -1
  81. package/dist/is-direct-execution-Cdlr-ZUl.js.map +0 -1
  82. package/dist/openapi.js.map +0 -1
  83. package/dist/skills-Bh0mEhIx.js.map +0 -1
  84. package/dist/ssg.js.map +0 -1
  85. package/dist/templates/full-stack/src/vite-server.d.ts +0 -6
  86. package/dist/templates/spa/src/styles.d.ts +0 -1
  87. package/dist/templates/ssg/src/vite-env.d.ts +0 -5
  88. package/dist/templates/ssr/src/vite-server.d.ts +0 -7
  89. package/dist/templates/startkit/src/vite-env.d.ts +0 -5
  90. package/dist/update-DyW1na5V.js +0 -1140
  91. package/dist/update-DyW1na5V.js.map +0 -1
  92. package/skills/askr-accessibility/SKILL.md +0 -68
  93. package/skills/askr-accessibility/agents/openai.yaml +0 -4
  94. package/skills/askr-agent-execution/SKILL.md +0 -85
  95. package/skills/askr-agent-execution/agents/openai.yaml +0 -4
  96. package/skills/askr-agent-workflows/SKILL.md +0 -57
  97. package/skills/askr-agent-workflows/agents/openai.yaml +0 -4
  98. package/skills/askr-api-integration/SKILL.md +0 -79
  99. package/skills/askr-api-integration/agents/openai.yaml +0 -4
  100. package/skills/askr-app-builder/SKILL.md +0 -99
  101. package/skills/askr-app-builder/agents/openai.yaml +0 -4
  102. package/skills/askr-auth-access/SKILL.md +0 -74
  103. package/skills/askr-auth-access/agents/openai.yaml +0 -4
  104. package/skills/askr-cli-vite/SKILL.md +0 -76
  105. package/skills/askr-cli-vite/agents/openai.yaml +0 -4
  106. package/skills/askr-dashboard-charts/SKILL.md +0 -82
  107. package/skills/askr-dashboard-charts/agents/openai.yaml +0 -4
  108. package/skills/askr-design-system/SKILL.md +0 -86
  109. package/skills/askr-design-system/agents/openai.yaml +0 -4
  110. package/skills/askr-env-config/SKILL.md +0 -74
  111. package/skills/askr-env-config/agents/openai.yaml +0 -4
  112. package/skills/askr-error-loading-empty/SKILL.md +0 -89
  113. package/skills/askr-error-loading-empty/agents/openai.yaml +0 -4
  114. package/skills/askr-file-upload-artifacts/SKILL.md +0 -77
  115. package/skills/askr-file-upload-artifacts/agents/openai.yaml +0 -4
  116. package/skills/askr-forms-tables-crud/SKILL.md +0 -98
  117. package/skills/askr-forms-tables-crud/agents/openai.yaml +0 -4
  118. package/skills/askr-mental-model/SKILL.md +0 -109
  119. package/skills/askr-mental-model/agents/openai.yaml +0 -4
  120. package/skills/askr-migration-react/SKILL.md +0 -81
  121. package/skills/askr-migration-react/agents/openai.yaml +0 -4
  122. package/skills/askr-observability-debugging/SKILL.md +0 -83
  123. package/skills/askr-observability-debugging/agents/openai.yaml +0 -4
  124. package/skills/askr-project-structure/SKILL.md +0 -79
  125. package/skills/askr-project-structure/agents/openai.yaml +0 -4
  126. package/skills/askr-query-mutation/SKILL.md +0 -115
  127. package/skills/askr-query-mutation/agents/openai.yaml +0 -4
  128. package/skills/askr-realtime-streaming/SKILL.md +0 -76
  129. package/skills/askr-realtime-streaming/agents/openai.yaml +0 -4
  130. package/skills/askr-resources-data/SKILL.md +0 -100
  131. package/skills/askr-resources-data/agents/openai.yaml +0 -4
  132. package/skills/askr-routing-layouts/SKILL.md +0 -107
  133. package/skills/askr-routing-layouts/agents/openai.yaml +0 -4
  134. package/skills/askr-runtime-reactivity/SKILL.md +0 -92
  135. package/skills/askr-runtime-reactivity/agents/openai.yaml +0 -4
  136. package/skills/askr-ssr-ssg/SKILL.md +0 -76
  137. package/skills/askr-ssr-ssg/agents/openai.yaml +0 -4
  138. package/skills/askr-testing-determinism/SKILL.md +0 -75
  139. package/skills/askr-testing-determinism/agents/openai.yaml +0 -4
  140. package/skills/askr-theming/SKILL.md +0 -86
  141. package/skills/askr-theming/agents/openai.yaml +0 -4
  142. package/skills/askr-ui-composition/SKILL.md +0 -92
  143. package/skills/askr-ui-composition/agents/openai.yaml +0 -4
  144. package/templates/full-stack/AGENTS.md +0 -12
  145. package/templates/full-stack/README.md +0 -18
  146. package/templates/full-stack/gitignore.template +0 -4
  147. package/templates/full-stack/index.html +0 -14
  148. package/templates/full-stack/package.json +0 -37
  149. package/templates/full-stack/server.ts +0 -13
  150. package/templates/full-stack/src/action-authorizations.ts +0 -10
  151. package/templates/full-stack/src/actions/create-message.ts +0 -10
  152. package/templates/full-stack/src/i18n.ts +0 -8
  153. package/templates/full-stack/src/main.tsx +0 -12
  154. package/templates/full-stack/src/pages/home.tsx +0 -25
  155. package/templates/full-stack/src/pages/layout.tsx +0 -33
  156. package/templates/full-stack/src/pages/not-found.tsx +0 -4
  157. package/templates/full-stack/src/routes.tsx +0 -20
  158. package/templates/full-stack/src/schemas.ts +0 -11
  159. package/templates/full-stack/src/server/action-registry.ts +0 -12
  160. package/templates/full-stack/src/server/actions/create-message.ts +0 -14
  161. package/templates/full-stack/src/server/app.ts +0 -66
  162. package/templates/full-stack/src/server/dependencies.ts +0 -55
  163. package/templates/full-stack/src/server/entry-server.ts +0 -8
  164. package/templates/full-stack/src/telemetry.ts +0 -4
  165. package/templates/full-stack/src/vite-server.d.ts +0 -6
  166. package/templates/full-stack/tests/actions/create-message.test.ts +0 -13
  167. package/templates/full-stack/tsconfig.json +0 -16
  168. package/templates/full-stack/vite.config.ts +0 -11
  169. package/templates/spa/AGENTS.md +0 -60
  170. package/templates/spa/README.md +0 -96
  171. package/templates/spa/gitignore.template +0 -16
  172. package/templates/spa/index.html +0 -13
  173. package/templates/spa/package.json +0 -35
  174. package/templates/spa/src/adapters/operations-client.ts +0 -90
  175. package/templates/spa/src/components/shared/metric-card.tsx +0 -27
  176. package/templates/spa/src/components/shared/status-badge.tsx +0 -24
  177. package/templates/spa/src/features/operations/operations.query.ts +0 -9
  178. package/templates/spa/src/main.tsx +0 -10
  179. package/templates/spa/src/pages/_layout.tsx +0 -9
  180. package/templates/spa/src/pages/_routes.tsx +0 -27
  181. package/templates/spa/src/pages/app/_layout.tsx +0 -94
  182. package/templates/spa/src/pages/app/_routes.tsx +0 -10
  183. package/templates/spa/src/pages/app/admin-home.tsx +0 -152
  184. package/templates/spa/src/pages/app/agent-runs.tsx +0 -93
  185. package/templates/spa/src/pages/app/settings.tsx +0 -91
  186. package/templates/spa/src/pages/auth/_layout.tsx +0 -38
  187. package/templates/spa/src/pages/auth/_routes.tsx +0 -6
  188. package/templates/spa/src/pages/auth/login.tsx +0 -63
  189. package/templates/spa/src/pages/not-found.tsx +0 -22
  190. package/templates/spa/src/pages/public/_layout.tsx +0 -47
  191. package/templates/spa/src/pages/public/_routes.tsx +0 -6
  192. package/templates/spa/src/pages/public/home.tsx +0 -143
  193. package/templates/spa/src/shared/format.ts +0 -12
  194. package/templates/spa/src/shared/navigation.ts +0 -15
  195. package/templates/spa/src/styles/components.css +0 -49
  196. package/templates/spa/src/styles/layout.css +0 -192
  197. package/templates/spa/src/styles/reset.css +0 -44
  198. package/templates/spa/src/styles/theme.css +0 -57
  199. package/templates/spa/src/styles/tokens.css +0 -20
  200. package/templates/spa/src/styles.css +0 -7
  201. package/templates/spa/src/styles.d.ts +0 -1
  202. package/templates/spa/tests/app.test.tsx +0 -37
  203. package/templates/spa/tests/components/shared.test.tsx +0 -12
  204. package/templates/spa/tests/resources.test.ts +0 -22
  205. package/templates/spa/tsconfig.json +0 -20
  206. package/templates/spa/tsconfig.node.json +0 -10
  207. package/templates/spa/vite.config.ts +0 -24
  208. package/templates/spa/vitest.config.ts +0 -17
  209. package/templates/ssg/AGENTS.md +0 -55
  210. package/templates/ssg/README.md +0 -39
  211. package/templates/ssg/gitignore.template +0 -17
  212. package/templates/ssg/index.html +0 -13
  213. package/templates/ssg/package.json +0 -35
  214. package/templates/ssg/src/app.tsx +0 -14
  215. package/templates/ssg/src/components/badge.tsx +0 -3
  216. package/templates/ssg/src/components/counter.tsx +0 -30
  217. package/templates/ssg/src/components/site-shell.tsx +0 -111
  218. package/templates/ssg/src/jsx.d.ts +0 -23
  219. package/templates/ssg/src/main.tsx +0 -8
  220. package/templates/ssg/src/pages/about.tsx +0 -82
  221. package/templates/ssg/src/pages/content.tsx +0 -63
  222. package/templates/ssg/src/pages/example.tsx +0 -97
  223. package/templates/ssg/src/pages/home.tsx +0 -51
  224. package/templates/ssg/src/resources/user.ts +0 -15
  225. package/templates/ssg/src/routes.tsx +0 -15
  226. package/templates/ssg/src/styles.css +0 -89
  227. package/templates/ssg/src/vite-env.d.ts +0 -5
  228. package/templates/ssg/ssg.config.ts +0 -34
  229. package/templates/ssg/tests/app.test.tsx +0 -19
  230. package/templates/ssg/tests/components/counter.test.tsx +0 -18
  231. package/templates/ssg/tests/resources.test.ts +0 -11
  232. package/templates/ssg/tests/ssg-config.test.ts +0 -11
  233. package/templates/ssg/tsconfig.json +0 -20
  234. package/templates/ssg/tsconfig.node.json +0 -10
  235. package/templates/ssg/vite.config.ts +0 -24
  236. package/templates/ssg/vitest.config.ts +0 -17
  237. package/templates/ssr/AGENTS.md +0 -28
  238. package/templates/ssr/README.md +0 -38
  239. package/templates/ssr/gitignore.template +0 -16
  240. package/templates/ssr/index.html +0 -13
  241. package/templates/ssr/package.json +0 -34
  242. package/templates/ssr/server.ts +0 -16
  243. package/templates/ssr/src/app.tsx +0 -21
  244. package/templates/ssr/src/components/counter.tsx +0 -26
  245. package/templates/ssr/src/entry-server.tsx +0 -10
  246. package/templates/ssr/src/main.tsx +0 -11
  247. package/templates/ssr/src/pages/about.tsx +0 -75
  248. package/templates/ssr/src/pages/example.tsx +0 -113
  249. package/templates/ssr/src/pages/home.tsx +0 -56
  250. package/templates/ssr/src/resources/user.ts +0 -15
  251. package/templates/ssr/src/routes.tsx +0 -13
  252. package/templates/ssr/src/styles.css +0 -148
  253. package/templates/ssr/src/vite-server.d.ts +0 -7
  254. package/templates/ssr/tests/app.test.tsx +0 -24
  255. package/templates/ssr/tests/components/counter.test.tsx +0 -13
  256. package/templates/ssr/tests/resources.test.ts +0 -11
  257. package/templates/ssr/tsconfig.json +0 -22
  258. package/templates/ssr/tsconfig.node.json +0 -10
  259. package/templates/ssr/tsconfig.server.json +0 -15
  260. package/templates/ssr/vite.config.ts +0 -25
  261. package/templates/ssr/vitest.config.ts +0 -13
  262. package/templates/startkit/AGENTS.md +0 -77
  263. package/templates/startkit/README.md +0 -192
  264. package/templates/startkit/gitignore.template +0 -16
  265. package/templates/startkit/index.html +0 -13
  266. package/templates/startkit/package.json +0 -35
  267. package/templates/startkit/src/components/app-header.tsx +0 -93
  268. package/templates/startkit/src/components/app-sidebar.tsx +0 -95
  269. package/templates/startkit/src/components/data-table.tsx +0 -80
  270. package/templates/startkit/src/components/empty-state.tsx +0 -13
  271. package/templates/startkit/src/components/page-header.tsx +0 -23
  272. package/templates/startkit/src/components/stat-card.tsx +0 -24
  273. package/templates/startkit/src/features/accounts/account-filters.tsx +0 -61
  274. package/templates/startkit/src/features/accounts/account-table.tsx +0 -79
  275. package/templates/startkit/src/lib/format.ts +0 -31
  276. package/templates/startkit/src/lib/mock-data.ts +0 -441
  277. package/templates/startkit/src/lib/routes.ts +0 -116
  278. package/templates/startkit/src/main.tsx +0 -12
  279. package/templates/startkit/src/pages/_layout.tsx +0 -56
  280. package/templates/startkit/src/pages/auth/_layout.tsx +0 -7
  281. package/templates/startkit/src/pages/auth/login.tsx +0 -129
  282. package/templates/startkit/src/pages/home.tsx +0 -83
  283. package/templates/startkit/src/pages/not-found.tsx +0 -24
  284. package/templates/startkit/src/pages/workspace/_layout.tsx +0 -17
  285. package/templates/startkit/src/pages/workspace/accounts/index.tsx +0 -217
  286. package/templates/startkit/src/pages/workspace/dashboard.tsx +0 -118
  287. package/templates/startkit/src/pages/workspace/settings.tsx +0 -218
  288. package/templates/startkit/src/router.tsx +0 -7
  289. package/templates/startkit/src/routes/auth-config.ts +0 -38
  290. package/templates/startkit/src/routes/auth.ts +0 -8
  291. package/templates/startkit/src/routes/index.ts +0 -28
  292. package/templates/startkit/src/routes/public.ts +0 -8
  293. package/templates/startkit/src/routes/workspace/accounts.ts +0 -8
  294. package/templates/startkit/src/routes/workspace/index.ts +0 -14
  295. package/templates/startkit/src/styles/components.css +0 -396
  296. package/templates/startkit/src/styles/layout.css +0 -192
  297. package/templates/startkit/src/styles/reset.css +0 -38
  298. package/templates/startkit/src/styles/theme.css +0 -76
  299. package/templates/startkit/src/styles/tokens.css +0 -67
  300. package/templates/startkit/src/styles.css +0 -7
  301. package/templates/startkit/src/toast.ts +0 -55
  302. package/templates/startkit/src/utils/join-classes.ts +0 -4
  303. package/templates/startkit/src/vite-env.d.ts +0 -5
  304. package/templates/startkit/tests/app.test.tsx +0 -32
  305. package/templates/startkit/tests/preferences.test.ts +0 -43
  306. package/templates/startkit/tests/resources.test.ts +0 -82
  307. package/templates/startkit/tsconfig.json +0 -20
  308. package/templates/startkit/tsconfig.node.json +0 -10
  309. package/templates/startkit/vite.config.ts +0 -32
  310. package/templates/startkit/vitest.config.ts +0 -13
@@ -1,24 +0,0 @@
1
- import type { JSXElement } from '@askrjs/askr/foundations';
2
- import type { IconProps } from '@askrjs/lucide';
3
-
4
- type IconComponent = (props: IconProps) => JSXElement;
5
-
6
- export default function StatCard(props: {
7
- label: string;
8
- value: string;
9
- trend: string;
10
- icon: IconComponent;
11
- }) {
12
- const Icon = props.icon;
13
-
14
- return (
15
- <article class="panel stat-card">
16
- <div class="stat-card-head">
17
- <p>{props.label}</p>
18
- <Icon size={16} aria-hidden="true" />
19
- </div>
20
- <strong>{props.value}</strong>
21
- <p class="trend">{props.trend}</p>
22
- </article>
23
- );
24
- }
@@ -1,61 +0,0 @@
1
- import { Input } from '@askrjs/ui/input';
2
- import { SearchIcon } from '@askrjs/lucide';
3
- import {
4
- Select,
5
- SelectContent,
6
- SelectItem,
7
- SelectPortal,
8
- SelectTrigger,
9
- SelectValue,
10
- } from '@askrjs/ui/select';
11
- import { Button } from '@askrjs/ui/button';
12
- import type { AccountStatus } from '../../lib/mock-data';
13
-
14
- export default function AccountFilters(props: {
15
- query: string;
16
- status: AccountStatus | 'all';
17
- onQueryChange: (next: string) => void;
18
- onStatusChange: (next: AccountStatus | 'all') => void;
19
- onReset: () => void;
20
- }) {
21
- const hasFilters = () =>
22
- props.query.trim().length > 0 || props.status !== 'all';
23
-
24
- return (
25
- <div class="account-filters">
26
- <label class="input-row account-search" aria-label="Search accounts">
27
- <SearchIcon size={15} aria-hidden="true" />
28
- <Input
29
- placeholder="Search by name, email, or id"
30
- value={props.query}
31
- onInput={(event: Event) =>
32
- props.onQueryChange((event.target as HTMLInputElement).value)
33
- }
34
- />
35
- </label>
36
-
37
- <Select
38
- value={props.status}
39
- onValueChange={(value) =>
40
- props.onStatusChange(value as AccountStatus | 'all')
41
- }
42
- >
43
- <SelectTrigger aria-label="Status filter">
44
- <SelectValue placeholder="Filter by status" />
45
- </SelectTrigger>
46
- <SelectPortal>
47
- <SelectContent>
48
- <SelectItem value="all">All statuses</SelectItem>
49
- <SelectItem value="active">Active</SelectItem>
50
- <SelectItem value="pending">Pending</SelectItem>
51
- <SelectItem value="archived">Archived</SelectItem>
52
- </SelectContent>
53
- </SelectPortal>
54
- </Select>
55
-
56
- <Button onPress={props.onReset} disabled={!hasFilters()}>
57
- Clear filters
58
- </Button>
59
- </div>
60
- );
61
- }
@@ -1,79 +0,0 @@
1
- import { Checkbox } from '@askrjs/ui/checkbox';
2
- import { Button } from '@askrjs/ui/button';
3
- import { EyeIcon } from '@askrjs/lucide';
4
- import DataTable, { type DataTableColumn } from '../../components/data-table';
5
- import type { AccountRecord } from '../../lib/mock-data';
6
- import { formatCurrency } from '../../lib/format';
7
-
8
- export default function AccountTable(props: {
9
- rows: () => AccountRecord[];
10
- selectedIds: () => string[];
11
- onToggleRow: (id: string) => void;
12
- onOpenRow: (row: AccountRecord) => void;
13
- loading?: boolean;
14
- errorText?: string | null;
15
- }) {
16
- const columns: DataTableColumn<AccountRecord>[] = [
17
- {
18
- key: 'select',
19
- header: 'Select',
20
- class: 'cell-select',
21
- render: (row) => (
22
- <Checkbox
23
- checked={props.selectedIds().includes(row.id)}
24
- onPress={() => props.onToggleRow(row.id)}
25
- aria-label={`Select ${row.name}`}
26
- />
27
- ),
28
- },
29
- {
30
- key: 'account',
31
- header: 'Account',
32
- render: (row) => (
33
- <div class="entity-copy">
34
- <strong>{row.name}</strong>
35
- <span class="muted">{row.email}</span>
36
- </div>
37
- ),
38
- },
39
- {
40
- key: 'plan',
41
- header: 'Plan',
42
- render: (row) => row.plan,
43
- },
44
- {
45
- key: 'mrr',
46
- header: 'MRR',
47
- render: (row) => formatCurrency(row.mrr),
48
- },
49
- {
50
- key: 'status',
51
- header: 'Status',
52
- render: (row) => (
53
- <span class={`status-pill status-${row.status}`}>{row.status}</span>
54
- ),
55
- },
56
- {
57
- key: 'actions',
58
- header: 'Actions',
59
- class: 'cell-actions',
60
- render: (row) => (
61
- <Button onPress={() => props.onOpenRow(row)}>
62
- <EyeIcon size={14} aria-hidden="true" /> View
63
- </Button>
64
- ),
65
- },
66
- ];
67
-
68
- return (
69
- <DataTable
70
- rows={props.rows}
71
- rowKey={(row) => row.id}
72
- columns={columns}
73
- isLoading={props.loading}
74
- errorText={props.errorText}
75
- emptyTitle="No accounts matched"
76
- emptyDescription="Try a broader search, or clear your status filter."
77
- />
78
- );
79
- }
@@ -1,31 +0,0 @@
1
- export function formatCurrency(value: number): string {
2
- return new Intl.NumberFormat('en-US', {
3
- style: 'currency',
4
- currency: 'USD',
5
- maximumFractionDigits: 0,
6
- }).format(value);
7
- }
8
-
9
- export function formatPercent(value: number): string {
10
- return `${value > 0 ? '+' : ''}${value.toFixed(1)}%`;
11
- }
12
-
13
- export function formatRelativeDate(timestamp: string): string {
14
- const date = new Date(timestamp);
15
- const diffMs = Date.now() - date.getTime();
16
- const hourMs = 60 * 60 * 1000;
17
- const dayMs = 24 * hourMs;
18
-
19
- if (diffMs < hourMs) {
20
- const minutes = Math.max(1, Math.floor(diffMs / (60 * 1000)));
21
- return `${minutes}m ago`;
22
- }
23
-
24
- if (diffMs < dayMs) {
25
- const hours = Math.max(1, Math.floor(diffMs / hourMs));
26
- return `${hours}h ago`;
27
- }
28
-
29
- const days = Math.max(1, Math.floor(diffMs / dayMs));
30
- return `${days}d ago`;
31
- }
@@ -1,441 +0,0 @@
1
- import { formatCurrency, formatPercent } from './format';
2
-
3
- export type AppearanceMode = 'default' | 'harbor' | 'ink';
4
- export type AccountStatus = 'active' | 'pending' | 'archived';
5
-
6
- export type DashboardStat = {
7
- key: string;
8
- label: string;
9
- value: string;
10
- trend: string;
11
- };
12
-
13
- export type ActivityEntry = {
14
- id: string;
15
- actor: string;
16
- action: string;
17
- resource: string;
18
- timestamp: string;
19
- };
20
-
21
- export type AccountRecord = {
22
- id: string;
23
- name: string;
24
- email: string;
25
- plan: 'Starter' | 'Growth' | 'Enterprise';
26
- status: AccountStatus;
27
- mrr: number;
28
- seats: number;
29
- };
30
-
31
- export type PagedResult<T> = {
32
- items: T[];
33
- totalItems: number;
34
- totalPages: number;
35
- page: number;
36
- };
37
-
38
- const appearanceStorageKey = 'startkit:appearance';
39
- const sessionStorageKey = 'startkit:session';
40
- const appearanceOrder: AppearanceMode[] = ['default', 'harbor', 'ink'];
41
- const storageFallback = new Map<string, string>();
42
-
43
- let appearance: AppearanceMode = 'default';
44
- let sessionEmail: string | null = null;
45
-
46
- const accountSeed: AccountRecord[] = [
47
- {
48
- id: 'acc_001',
49
- name: 'Northwind Logistics',
50
- email: 'ops@northwind.example',
51
- plan: 'Enterprise',
52
- status: 'active',
53
- mrr: 32000,
54
- seats: 82,
55
- },
56
- {
57
- id: 'acc_002',
58
- name: 'Pinecone Retail',
59
- email: 'team@pinecone.example',
60
- plan: 'Growth',
61
- status: 'active',
62
- mrr: 9800,
63
- seats: 28,
64
- },
65
- {
66
- id: 'acc_003',
67
- name: 'Hexa Health',
68
- email: 'care@hexa.example',
69
- plan: 'Growth',
70
- status: 'pending',
71
- mrr: 6400,
72
- seats: 16,
73
- },
74
- {
75
- id: 'acc_004',
76
- name: 'Atlas Research',
77
- email: 'lab@atlas.example',
78
- plan: 'Starter',
79
- status: 'archived',
80
- mrr: 0,
81
- seats: 4,
82
- },
83
- {
84
- id: 'acc_005',
85
- name: 'Lumen Ops',
86
- email: 'admin@lumen.example',
87
- plan: 'Growth',
88
- status: 'active',
89
- mrr: 11400,
90
- seats: 34,
91
- },
92
- {
93
- id: 'acc_006',
94
- name: 'Summit Security',
95
- email: 'it@summit.example',
96
- plan: 'Enterprise',
97
- status: 'pending',
98
- mrr: 22900,
99
- seats: 63,
100
- },
101
- {
102
- id: 'acc_007',
103
- name: 'Aster Finance',
104
- email: 'hello@aster.example',
105
- plan: 'Starter',
106
- status: 'active',
107
- mrr: 3700,
108
- seats: 11,
109
- },
110
- {
111
- id: 'acc_008',
112
- name: 'Calico Labs',
113
- email: 'team@calico.example',
114
- plan: 'Growth',
115
- status: 'active',
116
- mrr: 12400,
117
- seats: 39,
118
- },
119
- ];
120
-
121
- let accountsDb = [...accountSeed];
122
-
123
- const activitiesSeed: ActivityEntry[] = [
124
- {
125
- id: 'evt_001',
126
- actor: 'Alex Morgan',
127
- action: 'invited',
128
- resource: '2 teammates to Northwind Logistics',
129
- timestamp: new Date(Date.now() - 16 * 60 * 1000).toISOString(),
130
- },
131
- {
132
- id: 'evt_002',
133
- actor: 'Priya Shah',
134
- action: 'upgraded',
135
- resource: 'Pinecone Retail from Starter to Growth',
136
- timestamp: new Date(Date.now() - 74 * 60 * 1000).toISOString(),
137
- },
138
- {
139
- id: 'evt_003',
140
- actor: 'Jordan Kim',
141
- action: 'resolved',
142
- resource: 'billing retry for Aster Finance',
143
- timestamp: new Date(Date.now() - 5.5 * 60 * 60 * 1000).toISOString(),
144
- },
145
- {
146
- id: 'evt_004',
147
- actor: 'System',
148
- action: 'synced',
149
- resource: 'daily usage metrics',
150
- timestamp: new Date(Date.now() - 26 * 60 * 60 * 1000).toISOString(),
151
- },
152
- ];
153
-
154
- function wait(signal?: AbortSignal, delay = 220): Promise<void> {
155
- return new Promise((resolve, reject) => {
156
- const timeout = setTimeout(resolve, delay);
157
-
158
- if (!signal) {
159
- return;
160
- }
161
-
162
- const onAbort = () => {
163
- clearTimeout(timeout);
164
- reject(new Error('Request aborted'));
165
- };
166
-
167
- if (signal.aborted) {
168
- onAbort();
169
- return;
170
- }
171
-
172
- signal.addEventListener('abort', onAbort, { once: true });
173
- });
174
- }
175
-
176
- function readStorage(key: string): string | null {
177
- const fallback = storageFallback.get(key) ?? null;
178
-
179
- if (typeof window === 'undefined') {
180
- return fallback;
181
- }
182
-
183
- try {
184
- const storage = window.localStorage as {
185
- getItem?: (storageKey: string) => string | null;
186
- };
187
- if (typeof storage.getItem !== 'function') {
188
- return fallback;
189
- }
190
-
191
- return storage.getItem(key) ?? fallback;
192
- } catch {
193
- return fallback;
194
- }
195
- }
196
-
197
- function writeStorage(key: string, value: string) {
198
- storageFallback.set(key, value);
199
-
200
- if (typeof window === 'undefined') {
201
- return;
202
- }
203
-
204
- try {
205
- const storage = window.localStorage as {
206
- setItem?: (storageKey: string, storageValue: string) => void;
207
- };
208
- if (typeof storage.setItem !== 'function') {
209
- return;
210
- }
211
-
212
- storage.setItem(key, value);
213
- } catch {
214
- // Ignore storage write failures in non-browser or restricted test envs.
215
- }
216
- }
217
-
218
- function removeStorage(key: string) {
219
- storageFallback.delete(key);
220
-
221
- if (typeof window === 'undefined') {
222
- return;
223
- }
224
-
225
- try {
226
- const storage = window.localStorage as {
227
- removeItem?: (storageKey: string) => void;
228
- };
229
- if (typeof storage.removeItem !== 'function') {
230
- return;
231
- }
232
-
233
- storage.removeItem(key);
234
- } catch {
235
- // Ignore storage delete failures in non-browser or restricted test envs.
236
- }
237
- }
238
-
239
- export function initializeAppSession() {
240
- if (typeof window === 'undefined') {
241
- return;
242
- }
243
-
244
- const storedAppearance = normalizeAppearance(
245
- readStorage(appearanceStorageKey) ?? appearance
246
- );
247
- setAppearance(storedAppearance);
248
-
249
- sessionEmail = readStorage(sessionStorageKey) ?? sessionEmail;
250
- }
251
-
252
- export function appearanceMode() {
253
- return appearance;
254
- }
255
-
256
- export function setAppearance(mode: AppearanceMode) {
257
- const normalized = normalizeAppearance(mode);
258
- appearance = normalized;
259
-
260
- if (typeof document !== 'undefined') {
261
- document.documentElement.dataset.skAppearance = normalized;
262
- }
263
-
264
- writeStorage(appearanceStorageKey, normalized);
265
- }
266
-
267
- export function nextAppearance(mode: AppearanceMode): AppearanceMode {
268
- const current = normalizeAppearance(mode);
269
- const index = appearanceOrder.indexOf(current);
270
- return appearanceOrder[(index + 1) % appearanceOrder.length];
271
- }
272
-
273
- export function resetAppearancePreference() {
274
- appearance = 'default';
275
-
276
- if (typeof document !== 'undefined') {
277
- delete document.documentElement.dataset.skAppearance;
278
- }
279
-
280
- removeStorage(appearanceStorageKey);
281
- }
282
-
283
- export function isAuthenticated(): boolean {
284
- return sessionEmail !== null;
285
- }
286
-
287
- export function getSessionEmail(): string | null {
288
- return sessionEmail;
289
- }
290
-
291
- export async function signIn(input: {
292
- email: string;
293
- password: string;
294
- signal?: AbortSignal;
295
- }): Promise<void> {
296
- await wait(input.signal, 260);
297
-
298
- const email = input.email.trim().toLowerCase();
299
- if (!email.includes('@')) {
300
- throw new Error('Enter a valid email address.');
301
- }
302
-
303
- if (input.password.trim().length < 8) {
304
- throw new Error('Password must be at least 8 characters.');
305
- }
306
-
307
- sessionEmail = email;
308
-
309
- writeStorage(sessionStorageKey, email);
310
- }
311
-
312
- export function signOut() {
313
- sessionEmail = null;
314
-
315
- removeStorage(sessionStorageKey);
316
- }
317
-
318
- export async function getDashboardData(input: {
319
- signal?: AbortSignal;
320
- }): Promise<{ stats: DashboardStat[]; activities: ActivityEntry[] }> {
321
- await wait(input.signal, 240);
322
-
323
- const activeAccounts = accountsDb.filter(
324
- (item) => item.status === 'active'
325
- ).length;
326
- const pendingAccounts = accountsDb.filter(
327
- (item) => item.status === 'pending'
328
- ).length;
329
- const mrr = accountsDb.reduce((sum, item) => sum + item.mrr, 0);
330
- const seats = accountsDb.reduce((sum, item) => sum + item.seats, 0);
331
-
332
- return {
333
- stats: [
334
- {
335
- key: 'mrr',
336
- label: 'Monthly recurring revenue',
337
- value: formatCurrency(mrr),
338
- trend: formatPercent(4.2),
339
- },
340
- {
341
- key: 'accounts',
342
- label: 'Active accounts',
343
- value: String(activeAccounts),
344
- trend: formatPercent(2.3),
345
- },
346
- {
347
- key: 'pending',
348
- label: 'Pending approvals',
349
- value: String(pendingAccounts),
350
- trend: formatPercent(-1.1),
351
- },
352
- {
353
- key: 'seats',
354
- label: 'Provisioned seats',
355
- value: String(seats),
356
- trend: formatPercent(3.8),
357
- },
358
- ],
359
- activities: [...activitiesSeed],
360
- };
361
- }
362
-
363
- export async function listAccounts(input: {
364
- query: string;
365
- status: AccountStatus | 'all';
366
- page: number;
367
- pageSize: number;
368
- signal?: AbortSignal;
369
- }): Promise<PagedResult<AccountRecord>> {
370
- await wait(input.signal, 280);
371
-
372
- if (input.query.trim().toLowerCase() === 'error') {
373
- throw new Error('Mock API failed. Clear search text "error" and retry.');
374
- }
375
-
376
- const normalizedQuery = input.query.trim().toLowerCase();
377
- const filtered = accountsDb.filter((item) => {
378
- const statusMatch =
379
- input.status === 'all' ? true : item.status === input.status;
380
- if (!statusMatch) {
381
- return false;
382
- }
383
-
384
- if (!normalizedQuery) {
385
- return true;
386
- }
387
-
388
- return (
389
- item.name.toLowerCase().includes(normalizedQuery) ||
390
- item.email.toLowerCase().includes(normalizedQuery) ||
391
- item.id.toLowerCase().includes(normalizedQuery)
392
- );
393
- });
394
-
395
- const totalItems = filtered.length;
396
- const totalPages = Math.max(1, Math.ceil(totalItems / input.pageSize));
397
- const page = Math.min(Math.max(1, input.page), totalPages);
398
- const start = (page - 1) * input.pageSize;
399
-
400
- return {
401
- items: filtered.slice(start, start + input.pageSize),
402
- totalItems,
403
- totalPages,
404
- page,
405
- };
406
- }
407
-
408
- export async function archiveAccounts(input: {
409
- ids: string[];
410
- signal?: AbortSignal;
411
- }): Promise<{ archived: number }> {
412
- await wait(input.signal, 220);
413
-
414
- let archived = 0;
415
- accountsDb = accountsDb.map((item) => {
416
- if (!input.ids.includes(item.id)) {
417
- return item;
418
- }
419
-
420
- archived += 1;
421
- return {
422
- ...item,
423
- status: 'archived',
424
- };
425
- });
426
-
427
- return { archived };
428
- }
429
-
430
- export function resetMockData() {
431
- accountsDb = [...accountSeed];
432
- signOut();
433
- }
434
-
435
- function normalizeAppearance(value: string | null | undefined): AppearanceMode {
436
- if (value === 'harbor' || value === 'ink' || value === 'default') {
437
- return value;
438
- }
439
-
440
- return 'default';
441
- }