feat(client): add UI kit, data layer, app shell, and Table component

- design token system (Atlantis, Kimberly, Powder Ash palette)
- Button, Badge, Input, Select, Card, SidePanel, Breadcrumbs components
- generic typed Table<T> component with loading/empty states
- API data layer: typed fetch client for environments, keys, sessions, scenarios
- Vite dev proxy targeting server on port 13000
- App shell with SidePanel nav and four entity pages (Environments, Keys, Sessions, Scenarios)
- Storybook config with dark/light theme toggle and a11y addon
This commit is contained in:
2026-04-09 00:11:55 +03:00
parent 5cc16725fb
commit 9916ef5aaf
47 changed files with 3910 additions and 10 deletions
+11
View File
@@ -0,0 +1,11 @@
.card {
background: var(--color-bg-subtle);
border: var(--border-width) solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
font-family: var(--font-family);
}
.sm { padding: var(--space-3) var(--space-4); }
.md { padding: var(--space-4) var(--space-6); }
.lg { padding: var(--space-6) var(--space-8); }
+46
View File
@@ -0,0 +1,46 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Card } from './Card';
import { Badge } from '../Badge/Badge';
import { Button } from '../Button/Button';
const meta: Meta<typeof Card> = {
title: 'UI/Card',
component: Card,
parameters: { layout: 'centered' },
tags: ['autodocs'],
decorators: [
(Story) => (
<div style={{ width: 360 }}>
<Story />
</div>
),
],
argTypes: {
padding: { control: 'select', options: ['sm', 'md', 'lg'] },
},
};
export default meta;
type Story = StoryObj<typeof Card>;
export const Default: Story = {
args: { children: 'Card content goes here.', padding: 'md' },
};
export const ScenarioCard: Story = {
render: () => (
<Card>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>
<span style={{ fontWeight: 600, fontSize: 14 }}>Login flow</span>
<Badge variant="success" dot>Passed</Badge>
</div>
<p style={{ margin: '0 0 16px', fontSize: 13, color: 'var(--color-text-muted)' }}>
Last run 2 minutes ago · 4 steps
</p>
<div style={{ display: 'flex', gap: 8 }}>
<Button size="sm" variant="primary">Run</Button>
<Button size="sm" variant="ghost">Edit</Button>
</div>
</Card>
),
};
+16
View File
@@ -0,0 +1,16 @@
import React from 'react';
import styles from './Card.module.css';
export interface CardProps {
children: React.ReactNode;
className?: string;
padding?: 'sm' | 'md' | 'lg';
}
export function Card({ children, className, padding = 'md' }: CardProps) {
return (
<div className={[styles.card, styles[padding], className].filter(Boolean).join(' ')}>
{children}
</div>
);
}