Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
614 changes: 614 additions & 0 deletions packages/benchmarks/shadcn-dashboard/app/dashboard/data.json.backup

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions packages/benchmarks/shadcn-dashboard/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
SidebarInset,
SidebarProvider,
} from "@/components/ui/sidebar"

import data from "./data.json"
import { loadTableData } from "@/lib/seed/loader"

export default function Page() {
const tableData = loadTableData()

return (
<SidebarProvider
style={
Expand All @@ -30,7 +31,7 @@ export default function Page() {
<div className="px-4 lg:px-6">
<ChartAreaInteractive />
</div>
<DataTable data={data} />
<DataTable data={tableData} />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,10 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar"
import { loadUserData } from "@/lib/seed/loader"

const data = {
user: {
name: "shadcn",
email: "m@example.com",
avatar: "/avatars/shadcn.jpg",
},
user: loadUserData(),
navMain: [
{
title: "Dashboard",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ import {
ToggleGroup,
ToggleGroupItem,
} from "@/components/ui/toggle-group"
import { loadChartData } from "@/lib/seed/loader"

export const description = "An interactive area chart"

const chartData = [
const chartData = loadChartData()

const chartDataFallback = [
{ date: "2024-04-01", desktop: 222, mobile: 150 },
{ date: "2024-04-02", desktop: 97, mobile: 180 },
{ date: "2024-04-03", desktop: 167, mobile: 120 },
Expand Down Expand Up @@ -150,7 +153,9 @@ export function ChartAreaInteractive() {
}
}, [isMobile])

const filteredData = chartData.filter((item) => {
const dataToUse = chartData.length > 0 ? chartData : chartDataFallback

const filteredData = dataToUse.filter((item) => {
const date = new Date(item.date)
const referenceDate = new Date("2024-06-30")
let daysToSubtract = 90
Expand Down
120 changes: 35 additions & 85 deletions packages/benchmarks/shadcn-dashboard/components/section-cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,94 +9,44 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { loadDashboardMetrics } from "@/lib/seed/loader"

export function SectionCards() {
const metrics = loadDashboardMetrics()

return (
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
<Card className="@container/card">
<CardHeader>
<CardDescription>Total Revenue</CardDescription>
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
$1,250.00
</CardTitle>
<CardAction>
<Badge variant="outline">
<IconTrendingUp />
+12.5%
</Badge>
</CardAction>
</CardHeader>
<CardFooter className="flex-col items-start gap-1.5 text-sm">
<div className="line-clamp-1 flex gap-2 font-medium">
Trending up this month <IconTrendingUp className="size-4" />
</div>
<div className="text-muted-foreground">
Visitors for the last 6 months
</div>
</CardFooter>
</Card>
<Card className="@container/card">
<CardHeader>
<CardDescription>New Customers</CardDescription>
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
1,234
</CardTitle>
<CardAction>
<Badge variant="outline">
<IconTrendingDown />
-20%
</Badge>
</CardAction>
</CardHeader>
<CardFooter className="flex-col items-start gap-1.5 text-sm">
<div className="line-clamp-1 flex gap-2 font-medium">
Down 20% this period <IconTrendingDown className="size-4" />
</div>
<div className="text-muted-foreground">
Acquisition needs attention
</div>
</CardFooter>
</Card>
<Card className="@container/card">
<CardHeader>
<CardDescription>Active Accounts</CardDescription>
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
45,678
</CardTitle>
<CardAction>
<Badge variant="outline">
<IconTrendingUp />
+12.5%
</Badge>
</CardAction>
</CardHeader>
<CardFooter className="flex-col items-start gap-1.5 text-sm">
<div className="line-clamp-1 flex gap-2 font-medium">
Strong user retention <IconTrendingUp className="size-4" />
</div>
<div className="text-muted-foreground">Engagement exceed targets</div>
</CardFooter>
</Card>
<Card className="@container/card">
<CardHeader>
<CardDescription>Growth Rate</CardDescription>
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
4.5%
</CardTitle>
<CardAction>
<Badge variant="outline">
<IconTrendingUp />
+4.5%
</Badge>
</CardAction>
</CardHeader>
<CardFooter className="flex-col items-start gap-1.5 text-sm">
<div className="line-clamp-1 flex gap-2 font-medium">
Steady performance increase <IconTrendingUp className="size-4" />
</div>
<div className="text-muted-foreground">Meets growth projections</div>
</CardFooter>
</Card>
{metrics.map((metric) => (
<Card key={metric.title} className="@container/card">
<CardHeader>
<CardDescription>{metric.title}</CardDescription>
<CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
{metric.value}
</CardTitle>
<CardAction>
<Badge variant="outline">
{metric.trend.direction === "up" ? (
<IconTrendingUp />
) : (
<IconTrendingDown />
)}
{metric.trend.percentage}
</Badge>
</CardAction>
</CardHeader>
<CardFooter className="flex-col items-start gap-1.5 text-sm">
<div className="line-clamp-1 flex gap-2 font-medium">
{metric.description}{" "}
{metric.trend.direction === "up" ? (
<IconTrendingUp className="size-4" />
) : (
<IconTrendingDown className="size-4" />
)}
</div>
<div className="text-muted-foreground">{metric.footer}</div>
</CardFooter>
</Card>
))}
</div>
)
}
}
183 changes: 183 additions & 0 deletions packages/benchmarks/shadcn-dashboard/lib/seed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Mock Seed Data System

Dynamic, deterministic mock data generation for the shadcn dashboard demo.

## Overview

This system replaces static hardcoded data with realistic, generated mock data in development environments while ensuring production builds use minimal or empty data.

## Key Features

- **Deterministic Seeding**: Same seed value produces identical data across runs
- **Environment-Based Loading**: Full data in development, minimal/empty in production
- **TypeScript Type Safety**: Strict interfaces for all entities
- **Realistic Data**: Weighted distributions for statuses, types, and trends
- **Easy Extension**: Add new generators following existing patterns

## Architecture

```
lib/
├── types.ts # TypeScript interfaces & enums
└── seed/
├── mock-data.ts # Environment detection & seed initialization
├── generators.ts # Data generation functions
├── loader.ts # Environment-based data loaders
└── README.md # This file
```

## Usage

### Loading Data in Components

Import the appropriate loader function:

```typescript
import { loadTableData, loadUserData, loadChartData, loadDashboardMetrics } from "@/lib/seed/loader"

// In component
const tableData = loadTableData() // Returns TableItem[]
const userData = loadUserData() // Returns User
const chartData = loadChartData() // Returns ChartDataPoint[]
const metrics = loadDashboardMetrics() // Returns DashboardCard[]
```

### Available Loaders

- `loadTableData()` - 60 table items with reviewers, statuses, document types
- `loadChartData()` - 90 days of desktop/mobile visitor data with trends
- `loadUserData()` - User profile with name, email, avatar
- `loadDashboardMetrics()` - 4 dashboard metric cards with trends

## Data Specifications

### Table Items
- **Count**: 60 items
- **Reviewers Pool**: 15 unique names (85% assigned, 15% "Assign reviewer")
- **Status Distribution**: 60% Done, 25% In Process, 10% Queued, 5% Not Started
- **Type Distribution**: 35% Narrative, 25% Technical, 10% Research, 10% Planning, 20% other types

### Chart Data
- **Count**: 90 days (2024-04-01 to 2024-06-30)
- **Metrics**: Desktop and mobile visitor counts
- **Patterns**: Upward trend, weekend dips, realistic variation

### User Data
- Realistic name, email (lowercase), avatar path

### Dashboard Metrics
- 4 cards: Revenue, Customers, Accounts, Growth Rate
- Mixed trend directions (up/down)
- Realistic values and descriptions

## Extending the System

### Adding a New Generator

1. **Define types** in `lib/types.ts`:
```typescript
export interface NewEntity {
id: number
name: string
}
```

2. **Create generator** in `lib/seed/generators.ts`:
```typescript
export const generateNewEntity = (): NewEntity[] => {
initializeSeed()
const items: NewEntity[] = []

for (let index = 0; index < 10; index++) {
items.push({
id: index + 1,
name: faker.company.name(),
})
}

return items
}
```

3. **Add loader** in `lib/seed/loader.ts`:
```typescript
export const loadNewEntity = (): NewEntity[] => {
if (isDevelopment()) {
return generateNewEntity()
}
return []
}
```

4. **Use in component**:
```typescript
import { loadNewEntity } from "@/lib/seed/loader"

const data = loadNewEntity()
```

## Seed Control

### Change Seed Value

Edit `SEED_VALUE` in `lib/seed/mock-data.ts`:

```typescript
export const SEED_VALUE = 12345 // Change to any number
```

Same seed = same data across all runs (deterministic).

### Environment Detection

The system checks `process.env.NODE_ENV`:

- `development` or `test` → Full generated data
- `production` → Minimal/empty data

Override by modifying `isDevelopment()` in `lib/seed/mock-data.ts`.

## Best Practices

1. **Always initialize seed** in generators: Call `initializeSeed()` at start
2. **Use weighted distributions** for realistic variety
3. **Provide fallback data** for production environments
4. **Keep generators pure** - no side effects, same input = same output
5. **Document new generators** in this README

## Testing

Verify determinism:
```bash
# Run dev server twice, check data is identical
nr dev
```

Verify production fallback:
```bash
# Build and check no mock data exposed
nr build
nr start
```

## Troubleshooting

**Data not showing in development:**
- Check `NODE_ENV` is set to `development`
- Verify loader imports are correct
- Check browser console for errors

**Data showing in production:**
- Verify build ran with `NODE_ENV=production`
- Check `isDevelopment()` logic
- Review loader conditionals

**Data changes on refresh:**
- Verify `initializeSeed()` is called in generator
- Check `SEED_VALUE` is consistent

## Dependencies

- `@faker-js/faker@^9.3.0` - Mock data generation library
- TypeScript 5+ - Type safety and interfaces
- Next.js 16+ - Build and environment variables
Loading