mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 14:39:39 +02:00
feat(manascore): add API conformity and cross-app consistency checks
- API Conformity: 7 checks (responses, errors, pagination, versioning, docs, health, validation) with checklist UI - Cross-App Consistency: track shared package usage (auth, ui, theme, branding, i18n, error-tracking + optional storage, llm) - Both shown as compact badges on overview, detailed cards on detail page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
61c23d5e79
commit
06694eac19
5 changed files with 206 additions and 0 deletions
|
|
@ -228,6 +228,31 @@ const manascoreCollection = defineCollection({
|
|||
lastChecked: z.string().optional(), // ISO date
|
||||
})
|
||||
.optional(),
|
||||
// API conformity checks
|
||||
apiConformity: z
|
||||
.object({
|
||||
consistentResponses: z.boolean(), // Uses ApiResult<T> format
|
||||
errorCodes: z.boolean(), // Consistent HTTP error codes
|
||||
pagination: z.boolean(), // Supports pagination where needed
|
||||
versioning: z.boolean(), // API versioning (/api/v1/)
|
||||
documentation: z.boolean(), // Swagger/OpenAPI docs
|
||||
healthEndpoint: z.boolean(), // /health endpoint
|
||||
validation: z.boolean(), // DTO validation on all inputs
|
||||
})
|
||||
.optional(),
|
||||
// Cross-app consistency (shared package usage)
|
||||
consistency: z
|
||||
.object({
|
||||
sharedAuth: z.boolean(), // Uses @manacore/shared-nestjs-auth or nestjs-integration
|
||||
sharedUi: z.boolean(), // Uses @manacore/shared-ui components
|
||||
sharedTheme: z.boolean(), // Uses @manacore/shared-theme
|
||||
sharedBranding: z.boolean(), // Uses @manacore/shared-branding
|
||||
sharedI18n: z.boolean(), // Uses @manacore/shared-i18n
|
||||
sharedErrorTracking: z.boolean(), // Uses @manacore/shared-error-tracking
|
||||
sharedStorage: z.boolean().optional(), // Uses @manacore/shared-storage (if applicable)
|
||||
sharedLlm: z.boolean().optional(), // Uses @manacore/shared-llm (if applicable)
|
||||
})
|
||||
.optional(),
|
||||
// Score history for trend visualization
|
||||
history: z
|
||||
.array(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,23 @@ scores:
|
|||
documentation: 98
|
||||
security: 92
|
||||
ux: 95
|
||||
apiConformity:
|
||||
consistentResponses: true
|
||||
errorCodes: true
|
||||
pagination: true
|
||||
versioning: true
|
||||
documentation: true
|
||||
healthEndpoint: true
|
||||
validation: true
|
||||
consistency:
|
||||
sharedAuth: true
|
||||
sharedUi: true
|
||||
sharedTheme: true
|
||||
sharedBranding: true
|
||||
sharedI18n: true
|
||||
sharedErrorTracking: true
|
||||
sharedStorage: false
|
||||
sharedLlm: false
|
||||
dependencies:
|
||||
total: 42
|
||||
outdated: 9
|
||||
|
|
|
|||
|
|
@ -18,6 +18,23 @@ scores:
|
|||
documentation: 95
|
||||
security: 90
|
||||
ux: 94
|
||||
apiConformity:
|
||||
consistentResponses: true
|
||||
errorCodes: true
|
||||
pagination: false
|
||||
versioning: true
|
||||
documentation: true
|
||||
healthEndpoint: true
|
||||
validation: true
|
||||
consistency:
|
||||
sharedAuth: true
|
||||
sharedUi: true
|
||||
sharedTheme: true
|
||||
sharedBranding: true
|
||||
sharedI18n: true
|
||||
sharedErrorTracking: true
|
||||
sharedStorage: false
|
||||
sharedLlm: false
|
||||
dependencies:
|
||||
total: 38
|
||||
outdated: 9
|
||||
|
|
|
|||
|
|
@ -274,6 +274,108 @@ function getBarColor(score: number): string {
|
|||
})()
|
||||
}
|
||||
|
||||
{/* API Conformity */}
|
||||
{
|
||||
audit.data.apiConformity &&
|
||||
(() => {
|
||||
const api = audit.data.apiConformity;
|
||||
const checks = [
|
||||
{
|
||||
key: 'Konsistente Responses',
|
||||
value: api.consistentResponses,
|
||||
desc: 'ApiResult<T>',
|
||||
},
|
||||
{ key: 'Error Codes', value: api.errorCodes, desc: 'HTTP Status' },
|
||||
{ key: 'Pagination', value: api.pagination, desc: 'Offset/Cursor' },
|
||||
{ key: 'Versioning', value: api.versioning, desc: '/api/v1/' },
|
||||
{ key: 'Dokumentation', value: api.documentation, desc: 'Swagger' },
|
||||
{ key: 'Health Endpoint', value: api.healthEndpoint, desc: '/health' },
|
||||
{ key: 'Validation', value: api.validation, desc: 'DTO' },
|
||||
];
|
||||
const passed = checks.filter((c) => c.value).length;
|
||||
|
||||
return (
|
||||
<div class="border-border/50 mb-8 rounded-xl border bg-gradient-to-br from-white/5 to-white/[0.02] p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h2 class="text-foreground text-sm font-semibold">API Conformity</h2>
|
||||
<p class="text-muted-foreground text-xs">Konsistenz der Backend-API</p>
|
||||
</div>
|
||||
<span
|
||||
class={`text-lg font-bold ${passed === checks.length ? 'text-emerald-500' : 'text-yellow-500'}`}
|
||||
>
|
||||
{passed}/{checks.length}
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
{checks.map(({ key, value, desc }) => (
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={`text-sm ${value ? 'text-emerald-500' : 'text-red-500'}`}>
|
||||
{value ? '✓' : '✗'}
|
||||
</span>
|
||||
<span class="text-foreground text-xs font-medium">{key}</span>
|
||||
<span class="text-muted-foreground/60 text-[10px]">({desc})</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
}
|
||||
|
||||
{/* Cross-App Consistency */}
|
||||
{
|
||||
audit.data.consistency &&
|
||||
(() => {
|
||||
const c = audit.data.consistency;
|
||||
const packages = [
|
||||
{ key: 'shared-auth', used: c.sharedAuth, req: true },
|
||||
{ key: 'shared-ui', used: c.sharedUi, req: true },
|
||||
{ key: 'shared-theme', used: c.sharedTheme, req: true },
|
||||
{ key: 'shared-branding', used: c.sharedBranding, req: true },
|
||||
{ key: 'shared-i18n', used: c.sharedI18n, req: true },
|
||||
{ key: 'shared-error-tracking', used: c.sharedErrorTracking, req: true },
|
||||
{ key: 'shared-storage', used: c.sharedStorage ?? false, req: false },
|
||||
{ key: 'shared-llm', used: c.sharedLlm ?? false, req: false },
|
||||
];
|
||||
const core = packages.filter((p) => p.req);
|
||||
const coreUsed = core.filter((p) => p.used).length;
|
||||
|
||||
return (
|
||||
<div class="border-border/50 mb-8 rounded-xl border bg-gradient-to-br from-white/5 to-white/[0.02] p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h2 class="text-foreground text-sm font-semibold">Cross-App Consistency</h2>
|
||||
<p class="text-muted-foreground text-xs">Nutzung der shared Packages</p>
|
||||
</div>
|
||||
<span
|
||||
class={`text-lg font-bold ${coreUsed === core.length ? 'text-emerald-500' : 'text-yellow-500'}`}
|
||||
>
|
||||
{coreUsed}/{core.length} Core
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 sm:grid-cols-4">
|
||||
{packages.map(({ key, used, req }) => (
|
||||
<div
|
||||
class={`rounded-lg border p-2 text-center ${used ? 'border-emerald-500/30 bg-emerald-500/5' : req ? 'border-red-500/30 bg-red-500/5' : 'border-border/30'}`}
|
||||
>
|
||||
<span
|
||||
class={`text-sm ${used ? 'text-emerald-500' : req ? 'text-red-500' : 'text-muted-foreground/50'}`}
|
||||
>
|
||||
{used ? '✓' : req ? '✗' : '—'}
|
||||
</span>
|
||||
<span class="text-[10px] block text-muted-foreground mt-0.5">{key}</span>
|
||||
{!req && (
|
||||
<span class="text-[8px] text-muted-foreground/50 block">optional</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
}
|
||||
|
||||
{/* Dependency Health */}
|
||||
{
|
||||
audit.data.dependencies &&
|
||||
|
|
|
|||
|
|
@ -172,6 +172,51 @@ const statuses = [...new Set(sortedAudits.map((a) => a.data.status))];
|
|||
))}
|
||||
</div>
|
||||
|
||||
{/* API & Consistency badges */}
|
||||
{(data.apiConformity || data.consistency) && (
|
||||
<div class="mt-1.5 flex items-center gap-3">
|
||||
{data.apiConformity &&
|
||||
(() => {
|
||||
const api = data.apiConformity;
|
||||
const passed = [
|
||||
api.consistentResponses,
|
||||
api.errorCodes,
|
||||
api.pagination,
|
||||
api.versioning,
|
||||
api.documentation,
|
||||
api.healthEndpoint,
|
||||
api.validation,
|
||||
].filter(Boolean).length;
|
||||
return (
|
||||
<span
|
||||
class={`text-[10px] font-medium ${passed === 7 ? 'text-emerald-500' : 'text-yellow-500'}`}
|
||||
>
|
||||
API: {passed}/7
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
{data.consistency &&
|
||||
(() => {
|
||||
const c = data.consistency;
|
||||
const core = [
|
||||
c.sharedAuth,
|
||||
c.sharedUi,
|
||||
c.sharedTheme,
|
||||
c.sharedBranding,
|
||||
c.sharedI18n,
|
||||
c.sharedErrorTracking,
|
||||
].filter(Boolean).length;
|
||||
return (
|
||||
<span
|
||||
class={`text-[10px] font-medium ${core === 6 ? 'text-emerald-500' : 'text-yellow-500'}`}
|
||||
>
|
||||
Shared: {core}/6
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Dependency health */}
|
||||
{data.dependencies &&
|
||||
(() => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue