tpmjs/apps/web/src/components/style-guide/SectionDataViz.tsx
Ajax Davis 48c41e8733 feat: add use cases marketing product with AI-generated content
Transform qualifying scenarios into marketing-ready use cases with:
- AI-generated titles, descriptions, ROI estimates, business value
- Persona/industry/category taxonomy for targeting
- Browseable feed with filtering and ranking
- SEO-optimized case study pages
- Daily cron job for generation and ranking

Database:
- Add Persona, Industry, Category lookup tables
- Add UseCase model with marketing content fields
- Add junction tables for personas/industries/categories
- Add SocialProof model for cached metrics

API:
- GET /api/use-cases - Global directory with filtering
- GET /api/use-cases/[id] - Individual use case details
- GET /api/public/users/[username]/collections/[slug]/use-cases
- POST /api/cron/use-cases - Nightly generation job

Frontend:
- /use-cases - Global feed with persona dropdown
- /use-cases/[slug] - SEO case study page
- /[username]/collections/[slug]/use-cases - Collection feed
- UseCasesFeed component - Sortable table component
- UseCaseCaseStudy component - Full case study layout
2026-01-20 16:17:35 +10:00

136 lines
5.6 KiB
TypeScript

'use client';
import { AnimatedCounter } from '@tpmjs/ui/AnimatedCounter/AnimatedCounter';
import { Button } from '@tpmjs/ui/Button/Button';
import { ProgressBar } from '@tpmjs/ui/ProgressBar/ProgressBar';
import { StatCard } from '@tpmjs/ui/StatCard/StatCard';
import { FieldsetSection, SubSection } from './shared';
interface SectionDataVizProps {
counterValue: number;
onCounterChange: (value: number) => void;
}
export function SectionDataViz({
counterValue,
onCounterChange,
}: SectionDataVizProps): React.ReactElement {
return (
<FieldsetSection title="9. data visualization" id="data-viz">
<p className="text-foreground-secondary mb-8 font-sans prose-width">
Guidelines for charts, metrics, and data displays. Semantic colors only no rainbow charts.
</p>
<SubSection title="data color palette">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<div className="bg-accent p-4 text-center">
<p className="font-mono text-sm text-white">primary data</p>
</div>
<div className="bg-foreground-secondary p-4 text-center">
<p className="font-mono text-sm text-white">secondary data</p>
</div>
<div className="bg-foreground-tertiary p-4 text-center">
<p className="font-mono text-sm text-white">tertiary data</p>
</div>
<div className="bg-border-strong p-4 text-center">
<p className="font-mono text-sm text-white">baseline</p>
</div>
</div>
<div className="bg-surface p-6 border border-dashed border-border">
<h4 className="font-mono text-sm font-medium mb-4">color rules for data</h4>
<ul className="space-y-2 text-sm text-foreground-secondary font-sans">
<li> Use copper for primary/highlighted data series</li>
<li> Use grayscale for comparison and secondary series</li>
<li> Reserve semantic colors (success/warning/error) for threshold indicators</li>
<li> Create emphasis through opacity variation, not hue changes</li>
<li> Maximum 4 colors in any single visualization</li>
</ul>
</div>
</SubSection>
<SubSection title="quality score visualization">
<p className="font-sans text-sm text-foreground-secondary mb-6">
Tool quality scores use a consistent visual language across the platform.
</p>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<StatCard label="excellent" value={95} suffix="%" showBar barProgress={95} />
<StatCard label="good" value={75} suffix="%" showBar barProgress={75} />
<StatCard label="fair" value={50} suffix="%" showBar barProgress={50} />
<StatCard label="poor" value={25} suffix="%" showBar barProgress={25} />
</div>
</SubSection>
<SubSection title="progress indicators">
<div className="space-y-6 max-w-xl">
<div>
<p className="font-mono text-sm text-foreground-secondary mb-2">primary (default)</p>
<ProgressBar value={75} variant="primary" />
</div>
<div>
<p className="font-mono text-sm text-foreground-secondary mb-2">success (completion)</p>
<ProgressBar value={100} variant="success" />
</div>
<div>
<p className="font-mono text-sm text-foreground-secondary mb-2">
warning (approaching limit)
</p>
<ProgressBar value={85} variant="warning" />
</div>
<div>
<p className="font-mono text-sm text-foreground-secondary mb-2">danger (at limit)</p>
<ProgressBar value={98} variant="danger" />
</div>
</div>
</SubSection>
<SubSection title="animated counters">
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="text-center">
<div className="font-mono text-3xl font-semibold">
<AnimatedCounter value={counterValue} />
</div>
<p className="font-mono text-xs text-foreground-secondary mt-2">downloads</p>
</div>
<div className="text-center">
<div className="font-mono text-3xl font-semibold">
<AnimatedCounter value={Math.floor(counterValue * 0.8)} />
</div>
<p className="font-mono text-xs text-foreground-secondary mt-2">users</p>
</div>
<div className="text-center">
<div className="font-mono text-3xl font-semibold">
<AnimatedCounter value={Math.floor(counterValue * 0.05)} />
</div>
<p className="font-mono text-xs text-foreground-secondary mt-2">tools</p>
</div>
<div className="text-center">
<div className="font-mono text-3xl font-semibold">
<AnimatedCounter value={Math.floor(counterValue * 0.01)} />%
</div>
<p className="font-mono text-xs text-foreground-secondary mt-2">uptime</p>
</div>
</div>
<div className="flex gap-2 mt-6 justify-center">
<Button size="sm" onClick={() => onCounterChange(counterValue + 1000)}>
+1000
</Button>
<Button
size="sm"
variant="outline"
onClick={() => onCounterChange(Math.max(0, counterValue - 1000))}
>
-1000
</Button>
<Button
size="sm"
variant="secondary"
onClick={() => onCounterChange(Math.floor(Math.random() * 100000))}
>
random
</Button>
</div>
</SubSection>
</FieldsetSection>
);
}