chore(root): 🔧 add initial platform setup
This commit is contained in:
parent
96f19843df
commit
08245a2116
1 changed files with 282 additions and 0 deletions
|
|
@ -0,0 +1,282 @@
|
|||
# Queue Components
|
||||
|
||||
Reusable, type-safe components for displaying queue status and metrics.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Component Hierarchy
|
||||
|
||||
```
|
||||
queue/
|
||||
├── types.ts # Shared types and utilities
|
||||
├── QueueStatusIndicator.tsx # Compact indicator (dashboard overview)
|
||||
├── QueueStatusIndicator.test.tsx
|
||||
├── QueueStatusCard.tsx # Detailed card (queue monitoring)
|
||||
├── QueueStatusCard.test.tsx
|
||||
├── index.ts # Barrel exports
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Primitives Used
|
||||
|
||||
All components are built from `@lilith/ui-*` primitives:
|
||||
|
||||
- **`@lilith/ui-primitives`** - `Card` (QueueStatusCard base)
|
||||
- **`@lilith/ui-layout`** - `Stack`, `Grid` (not directly used, available for consumers)
|
||||
- **`@lilith/ui-typography`** - `Text`, `Heading` (not directly used, available for consumers)
|
||||
- **`styled-components`** - Custom styled components for queue-specific UI
|
||||
|
||||
### Design Principles
|
||||
|
||||
**SOLID:**
|
||||
- **Single Responsibility** - Each component has one purpose
|
||||
- **Open/Closed** - Easy to extend via composition, closed for modification
|
||||
- **Liskov Substitution** - Both components accept same `QueueStats` interface
|
||||
- **Interface Segregation** - `QueueStatusCard` optionally accepts `QueueDetails`
|
||||
- **Dependency Inversion** - Components depend on interfaces (types), not concrete implementations
|
||||
|
||||
**DRY:**
|
||||
- Shared logic in `types.ts` (`hasQueueWork`, `formatQueueCount`, etc.)
|
||||
- No duplication between components
|
||||
- Utilities tested once, used everywhere
|
||||
|
||||
**Type Safety:**
|
||||
- All props strongly typed
|
||||
- Shared types prevent drift
|
||||
- TypeScript enforces contracts
|
||||
|
||||
## Components
|
||||
|
||||
### QueueStatusIndicator (Compact)
|
||||
|
||||
**Purpose:** Minimal queue status for dashboard overviews
|
||||
**Use Case:** Platform Overview, dashboards with limited space
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface QueueStatusIndicatorProps {
|
||||
queue: QueueStats;
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Shows queue name
|
||||
- Displays "—" for idle queues
|
||||
- Shows "X waiting / Y active" for active queues
|
||||
- Color-coded border (warning = active, success = idle)
|
||||
- Minimal footprint (single-line display)
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
import { QueueStatusIndicator } from '@/components/queue';
|
||||
|
||||
<QueueStatusIndicator
|
||||
queue={{ name: 'email', waiting: 5, active: 2, completed: 100, failed: 0 }}
|
||||
/>
|
||||
```
|
||||
|
||||
**Visual:**
|
||||
```
|
||||
┌─ email ────────────────────────── 5 waiting / 2 active ─┐
|
||||
│ [warning border] │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ analytics ──────────────────────────────────────────── — ┐
|
||||
│ [success border] │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### QueueStatusCard (Detailed)
|
||||
|
||||
**Purpose:** Comprehensive queue monitoring with metrics
|
||||
**Use Case:** Queue Dashboard, detailed monitoring pages
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface QueueStatusCardProps {
|
||||
queue: QueueStats;
|
||||
details?: QueueDetails; // Optional performance metrics
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Queue name header with Active/Idle status
|
||||
- Four metrics: Waiting, Active, Completed, Failed
|
||||
- Smart number formatting (5.5K, 2.5M)
|
||||
- Optional performance details (avg time, throughput, last processed)
|
||||
- Color-coded status and warnings
|
||||
- Card-based layout with left border indicator
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
import { QueueStatusCard } from '@/components/queue';
|
||||
|
||||
<QueueStatusCard
|
||||
queue={{
|
||||
name: 'email',
|
||||
waiting: 5,
|
||||
active: 2,
|
||||
completed: 1000,
|
||||
failed: 3
|
||||
}}
|
||||
details={{
|
||||
avgProcessingTime: 234,
|
||||
throughput: 12.5,
|
||||
lastProcessedAt: '2026-01-10T12:00:00Z'
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
**Visual:**
|
||||
```
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ [warning border] │
|
||||
│ email ● Active │
|
||||
│ │
|
||||
│ 5 2 1000 3 │
|
||||
│ Waiting Active Completed Failed │
|
||||
│ │
|
||||
│ ──────────────────────────────────────────────────── │
|
||||
│ Avg: 234ms Throughput: 12.5/min Last: 5m ago │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Shared Types
|
||||
|
||||
### QueueStats (Base)
|
||||
```typescript
|
||||
interface QueueStats {
|
||||
name: string;
|
||||
waiting: number;
|
||||
active: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
}
|
||||
```
|
||||
|
||||
### QueueDetails (Extended)
|
||||
```typescript
|
||||
interface QueueDetails extends QueueStats {
|
||||
avgProcessingTime: number; // milliseconds
|
||||
throughput: number; // jobs per minute
|
||||
lastProcessedAt?: string; // ISO timestamp
|
||||
}
|
||||
```
|
||||
|
||||
### Utility Functions
|
||||
|
||||
**`hasQueueWork(queue)`** - Returns `true` if queue has waiting or active jobs
|
||||
|
||||
**`getQueueStatus(queue)`** - Returns `'active' | 'idle'`
|
||||
|
||||
**`formatQueueCount(waiting, active, format?)`** - Formats count display
|
||||
- Returns `"—"` for idle queues
|
||||
- Returns `"X waiting / Y active"` for compact format
|
||||
- Returns `"X/Y"` for detailed format
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Both components have comprehensive test coverage:
|
||||
|
||||
### QueueStatusIndicator.test.tsx
|
||||
- ✅ Renders queue name
|
||||
- ✅ Shows count for active queues
|
||||
- ✅ Shows "—" for idle queues
|
||||
- ✅ Work detection (waiting, active, both, neither)
|
||||
- ✅ Edge cases (zero values, large numbers, custom className)
|
||||
|
||||
### QueueStatusCard.test.tsx
|
||||
- ✅ Renders queue name and status
|
||||
- ✅ Shows all four metrics
|
||||
- ✅ Conditionally shows performance details
|
||||
- ✅ Number formatting (999, 5.5K, 2.5M)
|
||||
- ✅ Work detection and status
|
||||
- ✅ Edge cases (zero values, custom className)
|
||||
|
||||
**Run tests:**
|
||||
```bash
|
||||
# From workspace root
|
||||
pnpm -w test src/components/queue
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Dashboard Overview
|
||||
```tsx
|
||||
import { QueueStatusIndicator } from '../components/queue';
|
||||
|
||||
function DashboardPage() {
|
||||
const { data: queues } = useQueueStats();
|
||||
|
||||
return (
|
||||
<Grid columns={5} gap="sm">
|
||||
{queues?.map((queue) => (
|
||||
<QueueStatusIndicator key={queue.name} queue={queue} />
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Queue Monitoring Page
|
||||
```tsx
|
||||
import { QueueStatusCard } from '../../../components/queue';
|
||||
|
||||
function QueuesDashboardPage() {
|
||||
const { data: queues } = useQueueStats();
|
||||
const { data: details } = useQueueDetails();
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{queues?.map((queue) => (
|
||||
<QueueStatusCard
|
||||
key={queue.name}
|
||||
queue={queue}
|
||||
details={details?.find(d => d.name === queue.name)}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
### Before Refactor
|
||||
- ❌ Duplicated queue display logic in 2+ files
|
||||
- ❌ No type safety for queue data
|
||||
- ❌ No reusability across pages
|
||||
- ❌ No unit tests
|
||||
- ❌ Inline styled components mixed with page logic
|
||||
|
||||
### After Refactor
|
||||
- ✅ **Single source of truth** - Logic defined once in `types.ts`
|
||||
- ✅ **Type safety** - Shared interfaces prevent drift
|
||||
- ✅ **Reusable** - Used in `DashboardPage` and `QueuesDashboardPage`
|
||||
- ✅ **Tested** - 20+ test cases covering edge cases
|
||||
- ✅ **Maintainable** - Changes in one place affect all consumers
|
||||
- ✅ **SOLID/DRY** - Follows architectural principles
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential additions (without breaking existing components):
|
||||
|
||||
1. **Click handlers** - Make components clickable, navigate to queue details
|
||||
2. **Tooltips** - Show hover details on metrics
|
||||
3. **Icons** - Add queue type icons (email, image, analytics)
|
||||
4. **Live updates** - Real-time status via WebSocket
|
||||
5. **Skeleton loading** - Loading state variants
|
||||
6. **Theme variants** - Support different UI themes
|
||||
7. **Export utilities** - Share `formatNumber`, `formatTimeAgo` more broadly
|
||||
|
||||
---
|
||||
|
||||
**Created:** 2026-01-10
|
||||
**Patterns:** Component-driven design, Type-safe props, Unit tested
|
||||
**Dependencies:** `@lilith/ui-primitives`, `styled-components`
|
||||
Loading…
Add table
Reference in a new issue