@hustle-together/api-dev-tools 3.12.3 → 3.12.16
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.
- package/.claude/commands/hustle-build.md +259 -0
- package/.claude/commands/hustle-combine.md +1089 -0
- package/.claude/commands/hustle-ui-create-page.md +1078 -0
- package/.claude/commands/hustle-ui-create.md +1058 -0
- package/.claude/hooks/auto-answer.py +305 -0
- package/.claude/hooks/cache-research.py +337 -0
- package/.claude/hooks/check-api-routes.py +168 -0
- package/.claude/hooks/check-playwright-setup.py +103 -0
- package/.claude/hooks/check-storybook-setup.py +81 -0
- package/.claude/hooks/check-update.py +132 -0
- package/.claude/hooks/completion-promise-detector.py +293 -0
- package/.claude/hooks/context-capacity-warning.py +171 -0
- package/.claude/hooks/detect-interruption.py +165 -0
- package/.claude/hooks/docs-update-check.py +120 -0
- package/.claude/hooks/enforce-a11y-audit.py +202 -0
- package/.claude/hooks/enforce-brand-guide.py +241 -0
- package/.claude/hooks/enforce-component-type-confirm.py +97 -0
- package/.claude/hooks/enforce-dry-run.py +134 -0
- package/.claude/hooks/enforce-freshness.py +184 -0
- package/.claude/hooks/enforce-page-components.py +186 -0
- package/.claude/hooks/enforce-page-data-schema.py +155 -0
- package/.claude/hooks/enforce-questions-sourced.py +146 -0
- package/.claude/hooks/enforce-schema-from-interview.py +248 -0
- package/.claude/hooks/enforce-ui-disambiguation.py +108 -0
- package/.claude/hooks/enforce-ui-interview.py +130 -0
- package/.claude/hooks/generate-adr-options.py +282 -0
- package/.claude/hooks/generate-manifest-entry.py +1161 -0
- package/.claude/hooks/hook_utils.py +609 -0
- package/.claude/hooks/lib/__init__.py +1 -0
- package/.claude/hooks/lib/__pycache__/__init__.cpython-314.pyc +0 -0
- package/.claude/hooks/lib/__pycache__/greptile.cpython-314.pyc +0 -0
- package/.claude/hooks/lib/__pycache__/ntfy.cpython-314.pyc +0 -0
- package/.claude/hooks/lib/greptile.py +355 -0
- package/.claude/hooks/lib/ntfy.py +209 -0
- package/.claude/hooks/notify-input-needed.py +73 -0
- package/.claude/hooks/notify-phase-complete.py +90 -0
- package/.claude/hooks/ntfy-on-question.py +240 -0
- package/.claude/hooks/orchestrator-completion.py +313 -0
- package/.claude/hooks/orchestrator-handoff.py +267 -0
- package/.claude/hooks/orchestrator-session-startup.py +146 -0
- package/.claude/hooks/parallel-orchestrator.py +451 -0
- package/.claude/hooks/project-document-prompt.py +302 -0
- package/.claude/hooks/remote-question-proxy.py +284 -0
- package/.claude/hooks/remote-question-server.py +1224 -0
- package/.claude/hooks/run-code-review.py +393 -0
- package/.claude/hooks/run-visual-qa.py +338 -0
- package/.claude/hooks/session-logger.py +323 -0
- package/.claude/hooks/test-orchestrator-reground.py +248 -0
- package/.claude/hooks/track-scope-coverage.py +220 -0
- package/.claude/hooks/track-token-usage.py +121 -0
- package/.claude/hooks/update-adr-decision.py +236 -0
- package/.claude/hooks/update-api-showcase.py +161 -0
- package/.claude/hooks/update-registry.py +352 -0
- package/.claude/hooks/update-testing-checklist.py +195 -0
- package/.claude/hooks/update-ui-showcase.py +224 -0
- package/.claude/settings.local.json +7 -1
- package/.claude/test-auto-answer-bot.py +183 -0
- package/.claude/test-completion-detector.py +263 -0
- package/.claude/test-orchestrator-state.json +20 -0
- package/.claude/test-orchestrator.sh +271 -0
- package/.skills/api-create/SKILL.md +88 -3
- package/.skills/docs-sync/SKILL.md +260 -0
- package/.skills/hustle-build/SKILL.md +459 -0
- package/.skills/hustle-build-review/SKILL.md +518 -0
- package/CHANGELOG.md +87 -0
- package/README.md +86 -9
- package/bin/cli.js +1302 -88
- package/commands/hustle-api-create.md +22 -0
- package/commands/hustle-combine.md +81 -2
- package/commands/hustle-ui-create-page.md +84 -2
- package/commands/hustle-ui-create.md +82 -2
- package/hooks/auto-answer.py +228 -0
- package/hooks/check-update.py +132 -0
- package/hooks/ntfy-on-question.py +227 -0
- package/hooks/orchestrator-completion.py +313 -0
- package/hooks/orchestrator-handoff.py +189 -0
- package/hooks/orchestrator-session-startup.py +146 -0
- package/hooks/periodic-reground.py +230 -67
- package/hooks/update-api-showcase.py +13 -1
- package/hooks/update-ui-showcase.py +13 -1
- package/package.json +7 -3
- package/scripts/extract-schema-docs.cjs +322 -0
- package/templates/CLAUDE-SECTION.md +89 -64
- package/templates/api-showcase/_components/APIModal.tsx +100 -8
- package/templates/api-showcase/_components/APIShowcase.tsx +36 -4
- package/templates/api-showcase/_components/APITester.tsx +367 -58
- package/templates/docs/page.tsx +230 -0
- package/templates/hustle-build-defaults.json +84 -0
- package/templates/hustle-dev-dashboard/page.tsx +365 -0
- package/templates/playwright-report/page.tsx +258 -0
- package/templates/settings.json +88 -7
- package/templates/test-results/page.tsx +237 -0
- package/templates/typedoc.json +19 -0
- package/templates/ui-showcase/_components/UIShowcase.tsx +1 -1
- package/templates/ui-showcase/page.tsx +1 -1
- package/.claude/api-dev-state.json +0 -466
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Documentation Page
|
|
8
|
+
*
|
|
9
|
+
* Shows TypeDoc-generated documentation or instructions to generate it.
|
|
10
|
+
* Links to the generated markdown files in docs/api/.
|
|
11
|
+
*
|
|
12
|
+
* Created with Hustle API Dev Tools (v3.12.12)
|
|
13
|
+
*/
|
|
14
|
+
export default function DocsPage() {
|
|
15
|
+
const [hasContent, setHasContent] = useState<boolean | null>(null);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// Check if docs exist by trying to fetch the index
|
|
19
|
+
fetch("/docs/api/README.md")
|
|
20
|
+
.then((res) => {
|
|
21
|
+
setHasContent(res.ok);
|
|
22
|
+
})
|
|
23
|
+
.catch(() => {
|
|
24
|
+
setHasContent(false);
|
|
25
|
+
});
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="min-h-screen bg-white dark:bg-gray-950">
|
|
30
|
+
{/* Header */}
|
|
31
|
+
<header className="border-b-4 border-black bg-[#BA0C2F] px-6 py-8 dark:border-gray-600">
|
|
32
|
+
<div className="mx-auto max-w-4xl">
|
|
33
|
+
<div className="flex items-center gap-2">
|
|
34
|
+
<Link
|
|
35
|
+
href="/hustle-dev-dashboard"
|
|
36
|
+
className="text-white/80 hover:text-white"
|
|
37
|
+
>
|
|
38
|
+
Dashboard
|
|
39
|
+
</Link>
|
|
40
|
+
<span className="text-white/60">/</span>
|
|
41
|
+
<span className="text-white">Documentation</span>
|
|
42
|
+
</div>
|
|
43
|
+
<h1 className="mt-2 text-3xl font-black text-white">
|
|
44
|
+
API DOCUMENTATION
|
|
45
|
+
</h1>
|
|
46
|
+
<p className="mt-2 text-white/80">
|
|
47
|
+
TypeDoc-generated code documentation
|
|
48
|
+
</p>
|
|
49
|
+
</div>
|
|
50
|
+
</header>
|
|
51
|
+
|
|
52
|
+
{/* Main Content */}
|
|
53
|
+
<main className="mx-auto max-w-4xl px-6 py-8">
|
|
54
|
+
{hasContent === null ? (
|
|
55
|
+
<div className="flex items-center justify-center py-16">
|
|
56
|
+
<div className="h-8 w-8 animate-spin rounded-full border-4 border-[#BA0C2F] border-t-transparent" />
|
|
57
|
+
</div>
|
|
58
|
+
) : hasContent ? (
|
|
59
|
+
<DocsContent />
|
|
60
|
+
) : (
|
|
61
|
+
<EmptyState />
|
|
62
|
+
)}
|
|
63
|
+
</main>
|
|
64
|
+
|
|
65
|
+
{/* Footer */}
|
|
66
|
+
<footer className="border-t-2 border-black px-6 py-4 dark:border-gray-600">
|
|
67
|
+
<div className="mx-auto max-w-4xl text-center text-sm text-gray-600 dark:text-gray-400">
|
|
68
|
+
Built with{" "}
|
|
69
|
+
<a
|
|
70
|
+
href="https://github.com/hustle-together/api-dev-tools"
|
|
71
|
+
className="font-bold text-[#BA0C2F] hover:underline"
|
|
72
|
+
>
|
|
73
|
+
Hustle API Dev Tools
|
|
74
|
+
</a>
|
|
75
|
+
</div>
|
|
76
|
+
</footer>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function DocsContent() {
|
|
82
|
+
return (
|
|
83
|
+
<div className="space-y-6">
|
|
84
|
+
<div className="border-2 border-black p-6 dark:border-gray-600">
|
|
85
|
+
<h2 className="mb-4 text-xl font-bold text-black dark:text-white">
|
|
86
|
+
Generated Documentation
|
|
87
|
+
</h2>
|
|
88
|
+
<p className="mb-4 text-gray-600 dark:text-gray-400">
|
|
89
|
+
Documentation has been generated. Browse the API reference:
|
|
90
|
+
</p>
|
|
91
|
+
<div className="space-y-2">
|
|
92
|
+
<DocLink href="/docs/api" title="API Reference" description="Full API documentation" />
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className="border-2 border-black bg-gray-50 p-6 dark:border-gray-600 dark:bg-gray-900">
|
|
97
|
+
<h3 className="mb-2 font-bold text-black dark:text-white">
|
|
98
|
+
Regenerate Documentation
|
|
99
|
+
</h3>
|
|
100
|
+
<p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
101
|
+
Run this command to update the documentation:
|
|
102
|
+
</p>
|
|
103
|
+
<code className="block rounded bg-white px-4 py-2 font-mono text-[#BA0C2F] dark:bg-gray-800">
|
|
104
|
+
pnpm typedoc
|
|
105
|
+
</code>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function EmptyState() {
|
|
112
|
+
return (
|
|
113
|
+
<div className="space-y-6">
|
|
114
|
+
{/* Status Card */}
|
|
115
|
+
<div className="border-2 border-black p-6 dark:border-gray-600">
|
|
116
|
+
<div className="flex items-center gap-3">
|
|
117
|
+
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gray-100 text-2xl dark:bg-gray-800">
|
|
118
|
+
0
|
|
119
|
+
</div>
|
|
120
|
+
<div>
|
|
121
|
+
<h2 className="text-xl font-bold text-black dark:text-white">
|
|
122
|
+
No Documentation Generated
|
|
123
|
+
</h2>
|
|
124
|
+
<p className="text-gray-600 dark:text-gray-400">
|
|
125
|
+
Run TypeDoc to generate API documentation
|
|
126
|
+
</p>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* Instructions */}
|
|
132
|
+
<div className="border-2 border-black bg-gray-50 p-6 dark:border-gray-600 dark:bg-gray-900">
|
|
133
|
+
<h3 className="mb-4 font-bold text-black dark:text-white">
|
|
134
|
+
Generate Documentation
|
|
135
|
+
</h3>
|
|
136
|
+
<div className="space-y-4">
|
|
137
|
+
<Step
|
|
138
|
+
number={1}
|
|
139
|
+
title="Run TypeDoc"
|
|
140
|
+
command="pnpm typedoc"
|
|
141
|
+
description="Generates Markdown documentation from TSDoc comments"
|
|
142
|
+
/>
|
|
143
|
+
<Step
|
|
144
|
+
number={2}
|
|
145
|
+
title="View Output"
|
|
146
|
+
command="ls docs/api/"
|
|
147
|
+
description="Documentation files are generated in the docs/api/ folder"
|
|
148
|
+
/>
|
|
149
|
+
<Step
|
|
150
|
+
number={3}
|
|
151
|
+
title="Refresh Page"
|
|
152
|
+
description="Refresh this page to see your documentation"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
{/* Requirements */}
|
|
158
|
+
<div className="border-2 border-black p-6 dark:border-gray-600">
|
|
159
|
+
<h3 className="mb-4 font-bold text-black dark:text-white">
|
|
160
|
+
Requirements
|
|
161
|
+
</h3>
|
|
162
|
+
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
|
163
|
+
<li className="flex items-center gap-2">
|
|
164
|
+
<span className="text-[#BA0C2F]">*</span>
|
|
165
|
+
typedoc.json configuration file in project root
|
|
166
|
+
</li>
|
|
167
|
+
<li className="flex items-center gap-2">
|
|
168
|
+
<span className="text-[#BA0C2F]">*</span>
|
|
169
|
+
TSDoc comments in your TypeScript files
|
|
170
|
+
</li>
|
|
171
|
+
<li className="flex items-center gap-2">
|
|
172
|
+
<span className="text-[#BA0C2F]">*</span>
|
|
173
|
+
typedoc and typedoc-plugin-markdown installed
|
|
174
|
+
</li>
|
|
175
|
+
</ul>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function Step({
|
|
182
|
+
number,
|
|
183
|
+
title,
|
|
184
|
+
command,
|
|
185
|
+
description,
|
|
186
|
+
}: {
|
|
187
|
+
number: number;
|
|
188
|
+
title: string;
|
|
189
|
+
command?: string;
|
|
190
|
+
description: string;
|
|
191
|
+
}) {
|
|
192
|
+
return (
|
|
193
|
+
<div className="flex gap-4">
|
|
194
|
+
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-[#BA0C2F] text-sm font-bold text-white">
|
|
195
|
+
{number}
|
|
196
|
+
</div>
|
|
197
|
+
<div>
|
|
198
|
+
<p className="font-medium text-black dark:text-white">{title}</p>
|
|
199
|
+
{command && (
|
|
200
|
+
<code className="mt-1 block rounded bg-white px-3 py-1 font-mono text-sm text-[#BA0C2F] dark:bg-gray-800">
|
|
201
|
+
{command}
|
|
202
|
+
</code>
|
|
203
|
+
)}
|
|
204
|
+
<p className="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
|
205
|
+
{description}
|
|
206
|
+
</p>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function DocLink({
|
|
213
|
+
href,
|
|
214
|
+
title,
|
|
215
|
+
description,
|
|
216
|
+
}: {
|
|
217
|
+
href: string;
|
|
218
|
+
title: string;
|
|
219
|
+
description: string;
|
|
220
|
+
}) {
|
|
221
|
+
return (
|
|
222
|
+
<Link
|
|
223
|
+
href={href}
|
|
224
|
+
className="block rounded border border-gray-200 p-4 transition-colors hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800"
|
|
225
|
+
>
|
|
226
|
+
<p className="font-medium text-[#BA0C2F]">{title}</p>
|
|
227
|
+
<p className="text-sm text-gray-600 dark:text-gray-400">{description}</p>
|
|
228
|
+
</Link>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "hustle-build-defaults",
|
|
3
|
+
"description": "Pre-configured default answers for --auto mode. These are used when no explicit answer is provided.",
|
|
4
|
+
|
|
5
|
+
"orchestrator": {
|
|
6
|
+
"auth_required": true,
|
|
7
|
+
"error_handling": "partial-success",
|
|
8
|
+
"brand_guide": true,
|
|
9
|
+
"testing_level": "full",
|
|
10
|
+
"caching_strategy": "individual",
|
|
11
|
+
"documentation_level": "comprehensive"
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"api": {
|
|
15
|
+
"include_all_params": true,
|
|
16
|
+
"rate_limiting": true,
|
|
17
|
+
"caching": "individual",
|
|
18
|
+
"error_format": "detailed",
|
|
19
|
+
"validation": "strict",
|
|
20
|
+
"logging": true,
|
|
21
|
+
"metrics": true
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
"component": {
|
|
25
|
+
"all_variants": true,
|
|
26
|
+
"accessibility": "wcag-aa",
|
|
27
|
+
"animations": true,
|
|
28
|
+
"responsive": true,
|
|
29
|
+
"dark_mode": true,
|
|
30
|
+
"loading_states": true,
|
|
31
|
+
"error_states": true,
|
|
32
|
+
"storybook": true
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
"page": {
|
|
36
|
+
"layout": "responsive-grid",
|
|
37
|
+
"seo": true,
|
|
38
|
+
"loading_states": true,
|
|
39
|
+
"error_boundary": true,
|
|
40
|
+
"suspense": true,
|
|
41
|
+
"prefetch": true,
|
|
42
|
+
"meta_tags": true
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
"combined": {
|
|
46
|
+
"execution_order": "parallel",
|
|
47
|
+
"caching": "unified",
|
|
48
|
+
"error_handling": "partial-success",
|
|
49
|
+
"retry_strategy": "exponential",
|
|
50
|
+
"timeout": 30000
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
"testing": {
|
|
54
|
+
"unit_tests": true,
|
|
55
|
+
"integration_tests": true,
|
|
56
|
+
"e2e_tests": true,
|
|
57
|
+
"visual_tests": true,
|
|
58
|
+
"coverage_threshold": 80,
|
|
59
|
+
"snapshot_tests": true
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
"documentation": {
|
|
63
|
+
"tsdoc": true,
|
|
64
|
+
"readme": true,
|
|
65
|
+
"changelog": true,
|
|
66
|
+
"storybook_docs": true,
|
|
67
|
+
"api_reference": true
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
"question_mappings": {
|
|
71
|
+
"auth": "auth_required",
|
|
72
|
+
"authentication": "auth_required",
|
|
73
|
+
"error": "error_handling",
|
|
74
|
+
"brand": "brand_guide",
|
|
75
|
+
"styling": "brand_guide",
|
|
76
|
+
"testing": "testing_level",
|
|
77
|
+
"cache": "caching_strategy",
|
|
78
|
+
"variant": "all_variants",
|
|
79
|
+
"a11y": "accessibility",
|
|
80
|
+
"accessibility": "accessibility",
|
|
81
|
+
"layout": "layout",
|
|
82
|
+
"seo": "seo"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
// Import registry for stats
|
|
7
|
+
import registryData from "@/../.claude/registry.json";
|
|
8
|
+
|
|
9
|
+
interface Registry {
|
|
10
|
+
version: string;
|
|
11
|
+
apis: Record<string, unknown>;
|
|
12
|
+
components?: Record<string, unknown>;
|
|
13
|
+
pages?: Record<string, unknown>;
|
|
14
|
+
combined?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Hustle Dev Dashboard
|
|
19
|
+
*
|
|
20
|
+
* Central hub linking to all development tools and showcases.
|
|
21
|
+
* Provides quick access to:
|
|
22
|
+
* - API Showcase & Documentation
|
|
23
|
+
* - UI Showcase & Storybook
|
|
24
|
+
* - Test Results
|
|
25
|
+
* - TypeDoc API Documentation
|
|
26
|
+
*
|
|
27
|
+
* Created with Hustle API Dev Tools (v3.12.11)
|
|
28
|
+
*/
|
|
29
|
+
export default function HustleDevDashboard() {
|
|
30
|
+
const registry: Registry = registryData || {
|
|
31
|
+
version: "1.0.0",
|
|
32
|
+
apis: {},
|
|
33
|
+
components: {},
|
|
34
|
+
pages: {},
|
|
35
|
+
combined: {},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const stats = useMemo(() => {
|
|
39
|
+
return {
|
|
40
|
+
apis: Object.keys(registry.apis || {}).length,
|
|
41
|
+
combined: Object.keys(registry.combined || {}).length,
|
|
42
|
+
components: Object.keys(registry.components || {}).length,
|
|
43
|
+
pages: Object.keys(registry.pages || {}).length,
|
|
44
|
+
};
|
|
45
|
+
}, [registry]);
|
|
46
|
+
|
|
47
|
+
const totalAPIs = stats.apis + stats.combined;
|
|
48
|
+
const totalUI = stats.components + stats.pages;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="min-h-screen bg-white dark:bg-gray-950">
|
|
52
|
+
{/* Header */}
|
|
53
|
+
<header className="border-b-4 border-black bg-[#BA0C2F] px-6 py-8 dark:border-gray-600">
|
|
54
|
+
<div className="mx-auto max-w-6xl">
|
|
55
|
+
<h1 className="text-3xl font-black text-white">
|
|
56
|
+
HUSTLE DEV DASHBOARD
|
|
57
|
+
</h1>
|
|
58
|
+
<p className="mt-2 text-white/80">
|
|
59
|
+
Central hub for all development tools and showcases
|
|
60
|
+
</p>
|
|
61
|
+
</div>
|
|
62
|
+
</header>
|
|
63
|
+
|
|
64
|
+
{/* Main Content */}
|
|
65
|
+
<main className="mx-auto max-w-6xl px-6 py-8">
|
|
66
|
+
{/* Stats Overview */}
|
|
67
|
+
<div className="mb-8 grid gap-4 sm:grid-cols-4">
|
|
68
|
+
<StatCard label="APIs" value={stats.apis} />
|
|
69
|
+
<StatCard label="Combined" value={stats.combined} />
|
|
70
|
+
<StatCard label="Components" value={stats.components} />
|
|
71
|
+
<StatCard label="Pages" value={stats.pages} />
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Links Grid */}
|
|
75
|
+
<div className="grid gap-6 md:grid-cols-2">
|
|
76
|
+
{/* APIs Section */}
|
|
77
|
+
<DashboardSection
|
|
78
|
+
icon="📡"
|
|
79
|
+
title="APIs"
|
|
80
|
+
description={`${totalAPIs} endpoint${totalAPIs !== 1 ? "s" : ""} registered`}
|
|
81
|
+
>
|
|
82
|
+
<DashboardLink
|
|
83
|
+
href="/api-showcase"
|
|
84
|
+
title="API Showcase"
|
|
85
|
+
description="Interactive API testing and documentation"
|
|
86
|
+
primary
|
|
87
|
+
/>
|
|
88
|
+
<DashboardLink
|
|
89
|
+
href="/docs/api"
|
|
90
|
+
title="API Documentation"
|
|
91
|
+
description="TypeDoc-generated API reference"
|
|
92
|
+
/>
|
|
93
|
+
</DashboardSection>
|
|
94
|
+
|
|
95
|
+
{/* UI Section */}
|
|
96
|
+
<DashboardSection
|
|
97
|
+
icon="🧩"
|
|
98
|
+
title="Components & Pages"
|
|
99
|
+
description={`${totalUI} item${totalUI !== 1 ? "s" : ""} registered`}
|
|
100
|
+
>
|
|
101
|
+
<DashboardLink
|
|
102
|
+
href="/ui-showcase"
|
|
103
|
+
title="UI Showcase"
|
|
104
|
+
description="Component gallery with live previews"
|
|
105
|
+
primary
|
|
106
|
+
/>
|
|
107
|
+
<DashboardLink
|
|
108
|
+
href="http://localhost:6006"
|
|
109
|
+
title="Storybook"
|
|
110
|
+
description="Component development environment"
|
|
111
|
+
external
|
|
112
|
+
/>
|
|
113
|
+
</DashboardSection>
|
|
114
|
+
|
|
115
|
+
{/* Testing Section */}
|
|
116
|
+
<DashboardSection
|
|
117
|
+
icon="🧪"
|
|
118
|
+
title="Testing"
|
|
119
|
+
description="Test results and coverage reports"
|
|
120
|
+
>
|
|
121
|
+
<DashboardLink
|
|
122
|
+
href="/test-results"
|
|
123
|
+
title="Test Results"
|
|
124
|
+
description="Vitest unit test results"
|
|
125
|
+
/>
|
|
126
|
+
<DashboardLink
|
|
127
|
+
href="/playwright-report"
|
|
128
|
+
title="Playwright Reports"
|
|
129
|
+
description="E2E test results and screenshots"
|
|
130
|
+
/>
|
|
131
|
+
</DashboardSection>
|
|
132
|
+
|
|
133
|
+
{/* Documentation Section */}
|
|
134
|
+
<DashboardSection
|
|
135
|
+
icon="📚"
|
|
136
|
+
title="Documentation"
|
|
137
|
+
description="Project documentation and guides"
|
|
138
|
+
>
|
|
139
|
+
<DashboardLink
|
|
140
|
+
href="/docs"
|
|
141
|
+
title="TypeDoc Documentation"
|
|
142
|
+
description="Auto-generated code documentation"
|
|
143
|
+
/>
|
|
144
|
+
<DashboardLink
|
|
145
|
+
href="https://github.com/hustle-together/api-dev-tools"
|
|
146
|
+
title="GitHub Repository"
|
|
147
|
+
description="Source code and README"
|
|
148
|
+
external
|
|
149
|
+
/>
|
|
150
|
+
</DashboardSection>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
{/* Workflow Status */}
|
|
154
|
+
<div className="mt-8 border-2 border-black bg-gray-50 p-6 dark:border-gray-600 dark:bg-gray-900">
|
|
155
|
+
<h2 className="mb-4 text-lg font-bold text-black dark:text-white">
|
|
156
|
+
Workflow Status
|
|
157
|
+
</h2>
|
|
158
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
159
|
+
<StatusItem
|
|
160
|
+
label="TypeDoc"
|
|
161
|
+
status="ready"
|
|
162
|
+
command="pnpm typedoc"
|
|
163
|
+
description="Auto-generated on build"
|
|
164
|
+
/>
|
|
165
|
+
<StatusItem
|
|
166
|
+
label="Unit Tests"
|
|
167
|
+
status="ready"
|
|
168
|
+
command="pnpm test"
|
|
169
|
+
description="Run during TDD phases"
|
|
170
|
+
/>
|
|
171
|
+
<StatusItem
|
|
172
|
+
label="Storybook"
|
|
173
|
+
status="manual"
|
|
174
|
+
command="pnpm storybook"
|
|
175
|
+
description="Start server manually"
|
|
176
|
+
/>
|
|
177
|
+
<StatusItem
|
|
178
|
+
label="E2E Tests"
|
|
179
|
+
status="ready"
|
|
180
|
+
command="pnpm test:e2e"
|
|
181
|
+
description="Run during verification"
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
<p className="mt-4 text-xs text-gray-500 dark:text-gray-400">
|
|
185
|
+
Most commands run automatically during workflows. Manual items require server startup.
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
</main>
|
|
189
|
+
|
|
190
|
+
{/* Footer */}
|
|
191
|
+
<footer className="border-t-2 border-black px-6 py-4 dark:border-gray-600">
|
|
192
|
+
<div className="mx-auto max-w-6xl text-center text-sm text-gray-600 dark:text-gray-400">
|
|
193
|
+
Built with{" "}
|
|
194
|
+
<a
|
|
195
|
+
href="https://github.com/hustle-together/api-dev-tools"
|
|
196
|
+
className="font-bold text-[#BA0C2F] hover:underline"
|
|
197
|
+
>
|
|
198
|
+
Hustle API Dev Tools
|
|
199
|
+
</a>
|
|
200
|
+
</div>
|
|
201
|
+
</footer>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function StatCard({ label, value }: { label: string; value: number }) {
|
|
207
|
+
return (
|
|
208
|
+
<div className="border-2 border-black bg-white p-4 dark:border-gray-600 dark:bg-gray-900">
|
|
209
|
+
<p className="text-3xl font-black text-[#BA0C2F]">{value}</p>
|
|
210
|
+
<p className="text-sm text-gray-600 dark:text-gray-400">{label}</p>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function DashboardSection({
|
|
216
|
+
icon,
|
|
217
|
+
title,
|
|
218
|
+
description,
|
|
219
|
+
children,
|
|
220
|
+
}: {
|
|
221
|
+
icon: string;
|
|
222
|
+
title: string;
|
|
223
|
+
description: string;
|
|
224
|
+
children: React.ReactNode;
|
|
225
|
+
}) {
|
|
226
|
+
return (
|
|
227
|
+
<div className="border-2 border-black dark:border-gray-600">
|
|
228
|
+
<div className="border-b-2 border-black bg-gray-50 px-4 py-3 dark:border-gray-600 dark:bg-gray-800">
|
|
229
|
+
<div className="flex items-center gap-2">
|
|
230
|
+
<span className="text-xl">{icon}</span>
|
|
231
|
+
<div>
|
|
232
|
+
<h2 className="font-bold text-black dark:text-white">{title}</h2>
|
|
233
|
+
<p className="text-xs text-gray-600 dark:text-gray-400">
|
|
234
|
+
{description}
|
|
235
|
+
</p>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
<div className="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-900">
|
|
240
|
+
{children}
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function DashboardLink({
|
|
247
|
+
href,
|
|
248
|
+
title,
|
|
249
|
+
description,
|
|
250
|
+
primary,
|
|
251
|
+
external,
|
|
252
|
+
}: {
|
|
253
|
+
href: string;
|
|
254
|
+
title: string;
|
|
255
|
+
description: string;
|
|
256
|
+
primary?: boolean;
|
|
257
|
+
external?: boolean;
|
|
258
|
+
}) {
|
|
259
|
+
const LinkComponent = external ? "a" : Link;
|
|
260
|
+
const externalProps = external
|
|
261
|
+
? { target: "_blank", rel: "noopener noreferrer" }
|
|
262
|
+
: {};
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<LinkComponent
|
|
266
|
+
href={href}
|
|
267
|
+
className={`block px-4 py-3 transition-colors hover:bg-gray-50 dark:hover:bg-gray-800 ${
|
|
268
|
+
primary ? "bg-[#BA0C2F]/5" : ""
|
|
269
|
+
}`}
|
|
270
|
+
{...externalProps}
|
|
271
|
+
>
|
|
272
|
+
<div className="flex items-center justify-between">
|
|
273
|
+
<div>
|
|
274
|
+
<p
|
|
275
|
+
className={`font-medium ${
|
|
276
|
+
primary
|
|
277
|
+
? "text-[#BA0C2F]"
|
|
278
|
+
: "text-black dark:text-white"
|
|
279
|
+
}`}
|
|
280
|
+
>
|
|
281
|
+
{title}
|
|
282
|
+
{external && (
|
|
283
|
+
<span className="ml-1 text-xs text-gray-400">↗</span>
|
|
284
|
+
)}
|
|
285
|
+
</p>
|
|
286
|
+
<p className="text-xs text-gray-600 dark:text-gray-400">
|
|
287
|
+
{description}
|
|
288
|
+
</p>
|
|
289
|
+
</div>
|
|
290
|
+
<svg
|
|
291
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
292
|
+
width="16"
|
|
293
|
+
height="16"
|
|
294
|
+
viewBox="0 0 24 24"
|
|
295
|
+
fill="none"
|
|
296
|
+
stroke="currentColor"
|
|
297
|
+
strokeWidth="2"
|
|
298
|
+
strokeLinecap="round"
|
|
299
|
+
strokeLinejoin="round"
|
|
300
|
+
className="text-gray-400"
|
|
301
|
+
>
|
|
302
|
+
<polyline points="9 18 15 12 9 6" />
|
|
303
|
+
</svg>
|
|
304
|
+
</div>
|
|
305
|
+
</LinkComponent>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function StatusItem({
|
|
310
|
+
label,
|
|
311
|
+
status,
|
|
312
|
+
command,
|
|
313
|
+
description,
|
|
314
|
+
}: {
|
|
315
|
+
label: string;
|
|
316
|
+
status: "ready" | "manual" | "running" | "error";
|
|
317
|
+
command: string;
|
|
318
|
+
description: string;
|
|
319
|
+
}) {
|
|
320
|
+
const statusConfig = {
|
|
321
|
+
ready: {
|
|
322
|
+
bg: "bg-green-100 dark:bg-green-900/30",
|
|
323
|
+
text: "text-green-700 dark:text-green-400",
|
|
324
|
+
label: "Auto",
|
|
325
|
+
icon: "✓",
|
|
326
|
+
},
|
|
327
|
+
manual: {
|
|
328
|
+
bg: "bg-yellow-100 dark:bg-yellow-900/30",
|
|
329
|
+
text: "text-yellow-700 dark:text-yellow-400",
|
|
330
|
+
label: "Manual",
|
|
331
|
+
icon: "⚡",
|
|
332
|
+
},
|
|
333
|
+
running: {
|
|
334
|
+
bg: "bg-blue-100 dark:bg-blue-900/30",
|
|
335
|
+
text: "text-blue-700 dark:text-blue-400",
|
|
336
|
+
label: "Running",
|
|
337
|
+
icon: "◐",
|
|
338
|
+
},
|
|
339
|
+
error: {
|
|
340
|
+
bg: "bg-red-100 dark:bg-red-900/30",
|
|
341
|
+
text: "text-red-700 dark:text-red-400",
|
|
342
|
+
label: "Error",
|
|
343
|
+
icon: "✗",
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const config = statusConfig[status];
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<div className="rounded border border-gray-200 bg-white p-3 dark:border-gray-700 dark:bg-gray-800">
|
|
351
|
+
<div className="flex items-center justify-between">
|
|
352
|
+
<span className="font-medium text-black dark:text-white">{label}</span>
|
|
353
|
+
<span
|
|
354
|
+
className={`rounded px-2 py-0.5 text-xs font-medium ${config.bg} ${config.text}`}
|
|
355
|
+
>
|
|
356
|
+
{config.icon} {config.label}
|
|
357
|
+
</span>
|
|
358
|
+
</div>
|
|
359
|
+
<code className="mt-1 block text-xs text-[#BA0C2F]">{command}</code>
|
|
360
|
+
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
361
|
+
{description}
|
|
362
|
+
</p>
|
|
363
|
+
</div>
|
|
364
|
+
);
|
|
365
|
+
}
|