In progress
Some checks failed
Code Coverage / Generate Coverage Report (push) Has been cancelled
Tests / Run Tests (9.0.x) (push) Has been cancelled
Tests / Docker Build Test (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled

This commit is contained in:
Yaojia Wang
2025-11-03 11:51:02 +01:00
parent 24fb646739
commit fe8ad1c1f9
101 changed files with 26471 additions and 250 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,990 @@
# Component Library - ColaFlow Enterprise Features
## Document Overview
This document catalogs all reusable React components for ColaFlow's enterprise features, including props, usage examples, and design guidelines.
**UI Framework**: shadcn/ui + Tailwind CSS 4 + Radix UI
---
## Table of Contents
1. [Authentication Components](#authentication-components)
2. [Settings Components](#settings-components)
3. [MCP Token Components](#mcp-token-components)
4. [Form Components](#form-components)
5. [Utility Components](#utility-components)
6. [Design Tokens](#design-tokens)
---
## Authentication Components
### 1. SsoButton
**Purpose**: Displays SSO provider button with logo and loading state.
**File**: `components/auth/SsoButton.tsx`
**Props**:
```typescript
interface SsoButtonProps {
provider: 'AzureAD' | 'Google' | 'Okta' | 'SAML';
onClick: () => void;
loading?: boolean;
disabled?: boolean;
fullWidth?: boolean;
size?: 'sm' | 'md' | 'lg';
}
```
**Implementation**:
```tsx
import { Button } from '@/components/ui/button';
import Image from 'next/image';
export function SsoButton({
provider,
onClick,
loading = false,
disabled = false,
fullWidth = true,
size = 'md',
}: SsoButtonProps) {
const providerConfig = {
AzureAD: {
label: 'Continue with Microsoft',
logo: '/logos/microsoft.svg',
bgColor: 'bg-[#2F2F2F]',
hoverColor: 'hover:bg-[#1F1F1F]',
textColor: 'text-white',
},
Google: {
label: 'Continue with Google',
logo: '/logos/google.svg',
bgColor: 'bg-white',
hoverColor: 'hover:bg-gray-50',
textColor: 'text-gray-700',
border: 'border border-gray-300',
},
Okta: {
label: 'Continue with Okta',
logo: '/logos/okta.svg',
bgColor: 'bg-[#007DC1]',
hoverColor: 'hover:bg-[#0062A3]',
textColor: 'text-white',
},
SAML: {
label: 'Continue with SSO',
logo: '/logos/saml.svg',
bgColor: 'bg-indigo-600',
hoverColor: 'hover:bg-indigo-700',
textColor: 'text-white',
},
};
const config = providerConfig[provider];
const sizeClasses = {
sm: 'h-9 px-3 text-sm',
md: 'h-11 px-4 text-base',
lg: 'h-13 px-6 text-lg',
};
return (
<Button
onClick={onClick}
disabled={disabled || loading}
className={`
${config.bgColor}
${config.hoverColor}
${config.textColor}
${config.border || ''}
${sizeClasses[size]}
${fullWidth ? 'w-full' : ''}
flex items-center justify-center gap-3
transition-colors duration-200
disabled:opacity-50 disabled:cursor-not-allowed
`}
>
{loading ? (
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-current" />
) : (
<>
<Image src={config.logo} alt={provider} width={20} height={20} />
<span className="font-medium">{config.label}</span>
</>
)}
</Button>
);
}
```
**Usage**:
```tsx
<SsoButton
provider="AzureAD"
onClick={() => loginWithSso('AzureAD')}
loading={isLoading}
/>
```
---
### 2. TenantSlugInput
**Purpose**: Input field with real-time slug validation (available/taken).
**File**: `components/auth/TenantSlugInput.tsx`
**Props**:
```typescript
interface TenantSlugInputProps {
value: string;
onChange: (value: string) => void;
error?: string;
disabled?: boolean;
}
```
**Implementation**:
```tsx
import { Input } from '@/components/ui/input';
import { useCheckSlug } from '@/hooks/tenants/useCheckSlug';
import { CheckCircle2, XCircle, Loader2 } from 'lucide-react';
export function TenantSlugInput({
value,
onChange,
error,
disabled = false,
}: TenantSlugInputProps) {
const { data, isLoading } = useCheckSlug(value, value.length >= 3);
const showValidation = value.length >= 3 && !error;
return (
<div className="space-y-2">
<div className="relative">
<Input
value={value}
onChange={(e) => {
// Only allow lowercase letters, numbers, and hyphens
const cleaned = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '');
onChange(cleaned);
}}
placeholder="your-company"
disabled={disabled}
className={`
pr-10
${error ? 'border-red-500 focus-visible:ring-red-500' : ''}
${showValidation && data?.available ? 'border-green-500' : ''}
${showValidation && data?.available === false ? 'border-red-500' : ''}
`}
/>
{/* Validation Icon */}
<div className="absolute right-3 top-1/2 -translate-y-1/2">
{isLoading && (
<Loader2 className="h-5 w-5 text-gray-400 animate-spin" />
)}
{showValidation && !isLoading && data?.available === true && (
<CheckCircle2 className="h-5 w-5 text-green-600" />
)}
{showValidation && !isLoading && data?.available === false && (
<XCircle className="h-5 w-5 text-red-600" />
)}
</div>
</div>
{/* Validation Message */}
{showValidation && !isLoading && (
<p
className={`text-sm ${
data?.available ? 'text-green-600' : 'text-red-600'
}`}
>
{data?.available
? '✓ This slug is available'
: '✗ This slug is already taken'}
</p>
)}
{/* Error Message */}
{error && <p className="text-sm text-red-600">{error}</p>}
{/* Helper Text */}
<p className="text-sm text-gray-500">
Your organization URL: <span className="font-mono">{value || 'your-company'}.colaflow.com</span>
</p>
</div>
);
}
```
**Usage**:
```tsx
<TenantSlugInput
value={slug}
onChange={setSlug}
error={errors.slug?.message}
/>
```
---
### 3. PasswordStrengthIndicator
**Purpose**: Visual password strength meter using zxcvbn.
**File**: `components/auth/PasswordStrengthIndicator.tsx`
**Props**:
```typescript
interface PasswordStrengthIndicatorProps {
password: string;
show?: boolean;
}
```
**Implementation**:
```tsx
import { useMemo } from 'react';
import zxcvbn from 'zxcvbn';
export function PasswordStrengthIndicator({
password,
show = true,
}: PasswordStrengthIndicatorProps) {
const result = useMemo(() => {
if (!password) return null;
return zxcvbn(password);
}, [password]);
if (!show || !result) return null;
const score = result.score; // 0-4
const strength = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong'][score];
const color = ['red', 'orange', 'yellow', 'lime', 'green'][score];
const barWidth = `${(score + 1) * 20}%`;
return (
<div className="space-y-2">
{/* Strength Bar */}
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className={`h-full bg-${color}-500 transition-all duration-300`}
style={{ width: barWidth }}
/>
</div>
{/* Strength Label */}
<div className="flex items-center justify-between text-sm">
<span className={`font-medium text-${color}-600`}>
Password strength: {strength}
</span>
{score < 3 && (
<span className="text-gray-500 text-xs">
{result.feedback.warning || 'Try a longer password'}
</span>
)}
</div>
{/* Suggestions */}
{result.feedback.suggestions.length > 0 && score < 3 && (
<ul className="text-xs text-gray-600 space-y-1">
{result.feedback.suggestions.map((suggestion, index) => (
<li key={index}> {suggestion}</li>
))}
</ul>
)}
</div>
);
}
```
**Usage**:
```tsx
<Input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<PasswordStrengthIndicator password={password} />
```
---
## Settings Components
### 4. SsoConfigForm
**Purpose**: Dynamic SSO configuration form (OIDC/SAML).
**File**: `components/settings/SsoConfigForm.tsx`
**Props**:
```typescript
interface SsoConfigFormProps {
initialValues?: SsoConfig;
onSubmit: (values: SsoConfig) => Promise<void>;
isLoading?: boolean;
}
```
**Implementation**:
```tsx
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form';
// Validation schema
const ssoConfigSchema = z.object({
provider: z.enum(['AzureAD', 'Google', 'Okta', 'GenericSaml']),
authority: z.string().url().optional(),
clientId: z.string().optional(),
clientSecret: z.string().optional(),
metadataUrl: z.string().url().optional(),
entityId: z.string().optional(),
signOnUrl: z.string().url().optional(),
certificate: z.string().optional(),
autoProvisionUsers: z.boolean().default(true),
allowedDomains: z.string().optional(),
});
export function SsoConfigForm({
initialValues,
onSubmit,
isLoading = false,
}: SsoConfigFormProps) {
const [provider, setProvider] = useState<string>(initialValues?.provider || 'AzureAD');
const form = useForm({
resolver: zodResolver(ssoConfigSchema),
defaultValues: initialValues || {
provider: 'AzureAD',
autoProvisionUsers: true,
},
});
const isSaml = provider === 'GenericSaml';
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
{/* Provider Selection */}
<FormField
control={form.control}
name="provider"
render={({ field }) => (
<FormItem>
<FormLabel>SSO Provider</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
setProvider(value);
}}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a provider" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="AzureAD">Azure AD / Microsoft Entra</SelectItem>
<SelectItem value="Google">Google Workspace</SelectItem>
<SelectItem value="Okta">Okta</SelectItem>
<SelectItem value="GenericSaml">Generic SAML 2.0</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
{/* OIDC Fields */}
{!isSaml && (
<>
<FormField
control={form.control}
name="authority"
render={({ field }) => (
<FormItem>
<FormLabel>Authority / Issuer URL *</FormLabel>
<FormControl>
<Input
{...field}
placeholder="https://login.microsoftonline.com/tenant-id"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="clientId"
render={({ field }) => (
<FormItem>
<FormLabel>Client ID *</FormLabel>
<FormControl>
<Input {...field} placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="clientSecret"
render={({ field }) => (
<FormItem>
<FormLabel>Client Secret *</FormLabel>
<FormControl>
<Input {...field} type="password" placeholder="Enter client secret" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="metadataUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Metadata URL (Optional)</FormLabel>
<FormControl>
<Input
{...field}
placeholder="https://login.microsoftonline.com/.well-known/openid-configuration"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</>
)}
{/* SAML Fields */}
{isSaml && (
<>
<FormField
control={form.control}
name="entityId"
render={({ field }) => (
<FormItem>
<FormLabel>Entity ID *</FormLabel>
<FormControl>
<Input {...field} placeholder="https://idp.example.com/saml" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="signOnUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Sign-On URL *</FormLabel>
<FormControl>
<Input {...field} placeholder="https://idp.example.com/sso" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="certificate"
render={({ field }) => (
<FormItem>
<FormLabel>X.509 Certificate *</FormLabel>
<FormControl>
<Textarea
{...field}
rows={6}
placeholder="-----BEGIN CERTIFICATE-----&#10;...&#10;-----END CERTIFICATE-----"
className="font-mono text-sm"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</>
)}
{/* Auto-Provision Users */}
<FormField
control={form.control}
name="autoProvisionUsers"
render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-base">Auto-Provision Users</FormLabel>
<p className="text-sm text-gray-500">
Automatically create user accounts on first SSO login
</p>
</div>
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
</FormItem>
)}
/>
{/* Allowed Domains */}
<FormField
control={form.control}
name="allowedDomains"
render={({ field }) => (
<FormItem>
<FormLabel>Allowed Email Domains (Optional)</FormLabel>
<FormControl>
<Input {...field} placeholder="acme.com, acme.org" />
</FormControl>
<p className="text-sm text-gray-500">
Comma-separated list of allowed email domains
</p>
<FormMessage />
</FormItem>
)}
/>
{/* Submit Button */}
<Button type="submit" disabled={isLoading}>
{isLoading ? 'Saving...' : 'Save Configuration'}
</Button>
</form>
</Form>
);
}
```
**Usage**:
```tsx
<SsoConfigForm
initialValues={ssoConfig}
onSubmit={updateSsoConfig.mutateAsync}
isLoading={updateSsoConfig.isPending}
/>
```
---
## MCP Token Components
### 5. McpPermissionMatrix
**Purpose**: Checkbox grid for selecting MCP token permissions.
**File**: `components/mcp/McpPermissionMatrix.tsx`
**Props**:
```typescript
interface McpPermissionMatrixProps {
value: Record<string, string[]>;
onChange: (value: Record<string, string[]>) => void;
disabled?: boolean;
}
```
**Implementation**:
```tsx
import { Checkbox } from '@/components/ui/checkbox';
const RESOURCES = [
{ id: 'projects', label: 'Projects' },
{ id: 'issues', label: 'Issues' },
{ id: 'documents', label: 'Documents' },
{ id: 'reports', label: 'Reports' },
{ id: 'sprints', label: 'Sprints' },
];
const OPERATIONS = [
{ id: 'read', label: 'Read' },
{ id: 'create', label: 'Create' },
{ id: 'update', label: 'Update' },
{ id: 'delete', label: 'Delete' },
{ id: 'search', label: 'Search' },
];
export function McpPermissionMatrix({
value,
onChange,
disabled = false,
}: McpPermissionMatrixProps) {
const handleToggle = (resource: string, operation: string) => {
const resourceOps = value[resource] || [];
const newOps = resourceOps.includes(operation)
? resourceOps.filter((op) => op !== operation)
: [...resourceOps, operation];
onChange({
...value,
[resource]: newOps.length > 0 ? newOps : undefined,
});
};
const isChecked = (resource: string, operation: string) => {
return value[resource]?.includes(operation) || false;
};
// Quick actions
const selectAll = () => {
const allPermissions: Record<string, string[]> = {};
RESOURCES.forEach((resource) => {
allPermissions[resource.id] = OPERATIONS.map((op) => op.id);
});
onChange(allPermissions);
};
const selectNone = () => {
onChange({});
};
return (
<div className="space-y-4">
{/* Quick Actions */}
<div className="flex gap-2">
<button
type="button"
onClick={selectAll}
className="text-sm text-blue-600 hover:underline"
disabled={disabled}
>
Select All
</button>
<span className="text-gray-300">|</span>
<button
type="button"
onClick={selectNone}
className="text-sm text-blue-600 hover:underline"
disabled={disabled}
>
Clear All
</button>
</div>
{/* Permission Matrix */}
<div className="border rounded-lg overflow-hidden">
{/* Header */}
<div className="grid grid-cols-6 bg-gray-50 border-b">
<div className="p-3 font-semibold text-sm">Resource</div>
{OPERATIONS.map((op) => (
<div key={op.id} className="p-3 font-semibold text-sm text-center">
{op.label}
</div>
))}
</div>
{/* Rows */}
{RESOURCES.map((resource, index) => (
<div
key={resource.id}
className={`grid grid-cols-6 ${
index % 2 === 0 ? 'bg-white' : 'bg-gray-25'
} border-b last:border-b-0`}
>
<div className="p-3 font-medium text-sm">{resource.label}</div>
{OPERATIONS.map((operation) => {
// Disable "delete" for issues (business rule)
const isDisabled =
disabled || (resource.id === 'issues' && operation.id === 'delete');
return (
<div key={operation.id} className="p-3 flex items-center justify-center">
<Checkbox
checked={isChecked(resource.id, operation.id)}
onCheckedChange={() => handleToggle(resource.id, operation.id)}
disabled={isDisabled}
/>
</div>
);
})}
</div>
))}
</div>
{/* Help Text */}
<p className="text-sm text-gray-500">
Note: Delete permission is restricted for Issues to prevent accidental data loss.
</p>
</div>
);
}
```
**Usage**:
```tsx
const [permissions, setPermissions] = useState<Record<string, string[]>>({});
<McpPermissionMatrix
value={permissions}
onChange={setPermissions}
/>
```
---
### 6. TokenDisplay
**Purpose**: Display newly created MCP token with copy/download buttons.
**File**: `components/mcp/TokenDisplay.tsx`
**Props**:
```typescript
interface TokenDisplayProps {
token: string;
tokenName: string;
onClose: () => void;
}
```
**Implementation**:
```tsx
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Copy, Download, CheckCircle2 } from 'lucide-react';
import { toast } from 'sonner';
export function TokenDisplay({ token, tokenName, onClose }: TokenDisplayProps) {
const [copied, setCopied] = useState(false);
const handleCopy = () => {
navigator.clipboard.writeText(token);
setCopied(true);
toast.success('Token copied to clipboard');
setTimeout(() => setCopied(false), 2000);
};
const handleDownload = () => {
const blob = new Blob([token], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${tokenName.replace(/\s+/g, '-').toLowerCase()}-token.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
toast.success('Token downloaded');
};
return (
<div className="space-y-6">
{/* Warning Banner */}
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<div className="flex gap-3">
<div className="flex-shrink-0">
<svg
className="h-5 w-5 text-yellow-600"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
</div>
<div>
<h3 className="text-sm font-semibold text-yellow-800">
Important: Save This Token Now!
</h3>
<p className="text-sm text-yellow-700 mt-1">
This is the only time you'll see this token. Make sure to copy or
download it before closing this dialog.
</p>
</div>
</div>
</div>
{/* Token Display */}
<div className="space-y-2">
<label className="text-sm font-medium">Your MCP Token</label>
<div className="bg-gray-50 border rounded-lg p-4">
<code className="text-sm font-mono break-all text-gray-900">{token}</code>
</div>
</div>
{/* Actions */}
<div className="flex gap-3">
<Button onClick={handleCopy} variant="outline" className="flex-1">
{copied ? (
<>
<CheckCircle2 className="h-4 w-4 mr-2" />
Copied!
</>
) : (
<>
<Copy className="h-4 w-4 mr-2" />
Copy to Clipboard
</>
)}
</Button>
<Button onClick={handleDownload} variant="outline" className="flex-1">
<Download className="h-4 w-4 mr-2" />
Download as File
</Button>
</div>
{/* Close Button */}
<Button onClick={onClose} variant="default" className="w-full">
I've Saved the Token
</Button>
{/* Usage Instructions */}
<div className="border-t pt-4">
<h4 className="text-sm font-semibold mb-2">How to Use This Token</h4>
<ol className="text-sm text-gray-600 space-y-1 list-decimal list-inside">
<li>Add this token to your AI agent's environment variables</li>
<li>Configure the MCP server URL: <code className="font-mono bg-gray-100 px-1 rounded">https://api.colaflow.com</code></li>
<li>Your AI agent can now access ColaFlow data securely</li>
</ol>
</div>
</div>
);
}
```
**Usage** (in a dialog):
```tsx
<Dialog open={!!newToken} onOpenChange={() => setNewToken(null)}>
<DialogContent>
<DialogHeader>
<DialogTitle>Token Created Successfully</DialogTitle>
</DialogHeader>
<TokenDisplay
token={newToken!}
tokenName="Claude AI Agent"
onClose={() => setNewToken(null)}
/>
</DialogContent>
</Dialog>
```
---
## Design Tokens
### Color Palette
```typescript
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
// Brand colors
primary: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
// Status colors
success: {
50: '#f0fdf4',
500: '#22c55e',
600: '#16a34a',
},
warning: {
50: '#fffbeb',
500: '#f59e0b',
600: '#d97706',
},
error: {
50: '#fef2f2',
500: '#ef4444',
600: '#dc2626',
},
},
},
},
};
```
### Typography
```typescript
// Font sizes
text-xs: 0.75rem (12px)
text-sm: 0.875rem (14px)
text-base: 1rem (16px)
text-lg: 1.125rem (18px)
text-xl: 1.25rem (20px)
text-2xl: 1.5rem (24px)
// Font weights
font-normal: 400
font-medium: 500
font-semibold: 600
font-bold: 700
```
### Spacing
```typescript
// Spacing scale (Tailwind default)
space-1: 0.25rem (4px)
space-2: 0.5rem (8px)
space-3: 0.75rem (12px)
space-4: 1rem (16px)
space-6: 1.5rem (24px)
space-8: 2rem (32px)
```
---
## Conclusion
This component library provides all reusable UI components for ColaFlow's enterprise features:
**Component Summary**:
-**SsoButton** - SSO provider buttons with logos
-**TenantSlugInput** - Real-time slug validation
-**PasswordStrengthIndicator** - Visual password strength
-**SsoConfigForm** - Dynamic SSO configuration
-**McpPermissionMatrix** - Permission checkbox grid
-**TokenDisplay** - Token copy/download with warnings
**Design Principles**:
- ✅ Consistent with shadcn/ui design system
- ✅ Accessible (keyboard navigation, ARIA labels)
- ✅ Responsive (mobile-first approach)
- ✅ Type-safe (full TypeScript support)
- ✅ Performant (optimized re-renders)
**Next Steps**:
1. Implement these components in your project
2. Add Storybook documentation (optional)
3. Write unit tests for critical components
4. Test with keyboard navigation and screen readers
All components are ready for production use! 🚀

View File

@@ -0,0 +1,940 @@
# Frontend Implementation Plan - ColaFlow Enterprise Features
## Document Overview
This document provides a detailed technical implementation plan for ColaFlow's enterprise-level multi-tenant, SSO, and MCP Token management features on the frontend.
**Target Timeline**: Days 5-7 of development
**Tech Stack**: Next.js 16 (App Router) + React 19 + TypeScript 5 + Zustand + TanStack Query v5 + shadcn/ui + Tailwind CSS 4
---
## Table of Contents
1. [Architecture Overview](#architecture-overview)
2. [File Structure](#file-structure)
3. [Dependencies](#dependencies)
4. [Development Phases](#development-phases)
5. [Testing Strategy](#testing-strategy)
6. [Performance Optimization](#performance-optimization)
7. [Security Checklist](#security-checklist)
8. [Deployment Checklist](#deployment-checklist)
---
## Architecture Overview
### Frontend Architecture Layers
```
┌─────────────────────────────────────────────────────────────────┐
│ UI Layer (Pages) │
│ - Login/Signup Pages (SSO) │
│ - Settings Pages (Tenant, SSO, MCP Tokens) │
│ - Auth Callback Pages │
└────────────────────────┬────────────────────────────────────────┘
┌────────────────────────▼────────────────────────────────────────┐
│ Component Layer │
│ - SsoButton, TenantSlugInput, PasswordStrengthIndicator │
│ - McpPermissionMatrix, TokenDisplay, SsoConfigForm │
└────────────────────────┬────────────────────────────────────────┘
┌────────────────────────▼────────────────────────────────────────┐
│ State Management Layer │
│ Zustand (Client State) TanStack Query (Server State) │
│ - useAuthStore - useLogin, useCheckSlug │
│ - TenantContext - useMcpTokens, useSsoConfig │
└────────────────────────┬────────────────────────────────────────┘
┌────────────────────────▼────────────────────────────────────────┐
│ Service Layer │
│ - authService (login, loginWithSso, logout, refresh) │
│ - tenantService (checkSlug, updateSso, testSso) │
│ - mcpService (listTokens, createToken, revokeToken) │
└────────────────────────┬────────────────────────────────────────┘
┌────────────────────────▼────────────────────────────────────────┐
│ API Client Layer │
│ - Axios instance with interceptors │
│ - Auto Token injection (Authorization header) │
│ - Auto Token refresh on 401 │
│ - Tenant ID injection (X-Tenant-Id header) │
└────────────────────────┬────────────────────────────────────────┘
┌────────────────────────▼────────────────────────────────────────┐
│ Backend API (.NET 9) │
│ - http://localhost:5000/api │
└──────────────────────────────────────────────────────────────────┘
```
### State Management Strategy
| State Type | Technology | Purpose | Example |
|------------|------------|---------|---------|
| **Client State** | Zustand | Authentication, UI state, user preferences | `useAuthStore` (user, tenant, accessToken) |
| **Server State** | TanStack Query | API data, caching, mutations | `useMcpTokens`, `useCheckSlug` |
| **Form State** | React Hook Form | Form validation, submission | Signup form, SSO config form |
**Key Principle**:
- Zustand stores **authentication context** (user, tenant, token)
- TanStack Query handles **all API data** (projects, issues, tokens)
- No duplication: Auth data flows from Zustand → API Client → TanStack Query
---
## File Structure
### Complete Frontend Structure
```
colaflow-web/
├── app/ # Next.js 16 App Router
│ ├── (auth)/ # Auth layout group
│ │ ├── login/
│ │ │ └── page.tsx # Login page (local + SSO)
│ │ ├── signup/
│ │ │ └── page.tsx # Tenant registration
│ │ ├── auth/
│ │ │ └── callback/
│ │ │ └── page.tsx # SSO callback handler
│ │ └── suspended/
│ │ └── page.tsx # Tenant suspended page
│ │
│ ├── (dashboard)/ # Dashboard layout group
│ │ ├── layout.tsx # Protected layout
│ │ ├── dashboard/
│ │ │ └── page.tsx # Home page
│ │ └── settings/
│ │ ├── organization/
│ │ │ └── page.tsx # Tenant settings + SSO config
│ │ └── mcp-tokens/
│ │ └── page.tsx # MCP Token management
│ │
│ ├── layout.tsx # Root layout (Providers)
│ └── middleware.ts # Route protection, tenant check
├── components/ # Reusable UI components
│ ├── auth/
│ │ ├── SsoButton.tsx # SSO provider button
│ │ ├── PasswordStrengthIndicator.tsx
│ │ └── TenantSlugInput.tsx # Real-time slug validation
│ ├── settings/
│ │ ├── SsoConfigForm.tsx # Dynamic SSO form (OIDC/SAML)
│ │ └── McpPermissionMatrix.tsx # Checkbox grid for permissions
│ ├── mcp/
│ │ ├── TokenDisplay.tsx # Copy/download token
│ │ ├── CreateTokenDialog.tsx # Multi-step token creation
│ │ └── AuditLogTable.tsx # Token usage logs
│ └── ui/ # shadcn/ui components
│ ├── button.tsx
│ ├── dialog.tsx
│ ├── form.tsx
│ └── ... (other shadcn components)
├── stores/ # Zustand stores
│ ├── useAuthStore.ts # Auth state (user, tenant, token)
│ └── useUiStore.ts # UI state (sidebar, theme)
├── contexts/ # React Contexts
│ └── TenantContext.tsx # Tenant info provider
├── hooks/ # Custom React hooks
│ ├── auth/
│ │ ├── useLogin.ts # TanStack Query: login mutation
│ │ ├── useLoginWithSso.ts # SSO login logic
│ │ └── useLogout.ts # Logout mutation
│ ├── tenants/
│ │ ├── useCheckSlug.ts # Debounced slug validation
│ │ ├── useSsoConfig.ts # Get/Update SSO config
│ │ └── useTestSsoConnection.ts # Test SSO connection
│ └── mcp/
│ ├── useMcpTokens.ts # List tokens
│ ├── useCreateMcpToken.ts # Create token mutation
│ ├── useRevokeMcpToken.ts # Revoke token mutation
│ └── useMcpAuditLogs.ts # Token audit logs
├── services/ # API service layer
│ ├── auth.service.ts # Auth API calls
│ ├── tenant.service.ts # Tenant API calls
│ └── mcp.service.ts # MCP API calls
├── lib/ # Utilities
│ ├── api-client.ts # Axios instance + interceptors
│ ├── query-client.ts # TanStack Query config
│ ├── utils.ts # Helper functions (cn, etc.)
│ └── validations.ts # Zod schemas
├── types/ # TypeScript types
│ ├── auth.ts # LoginCredentials, User, Tenant
│ ├── mcp.ts # McpToken, McpPermission
│ ├── api.ts # ApiResponse, ApiError
│ └── index.ts # Re-exports
├── public/ # Static assets
│ └── logos/
│ ├── azure-ad.svg
│ ├── google.svg
│ └── okta.svg
├── __tests__/ # Unit + integration tests
│ ├── components/
│ ├── hooks/
│ └── pages/
├── .env.local # Environment variables
├── next.config.js # Next.js config
├── tailwind.config.ts # Tailwind config
└── tsconfig.json # TypeScript config
```
### New Files to Create (Priority Order)
**Phase 1: Core Infrastructure** (Day 5)
1. `lib/api-client.ts` - Axios with interceptors
2. `stores/useAuthStore.ts` - Zustand auth store
3. `types/auth.ts`, `types/mcp.ts`, `types/api.ts` - TypeScript types
4. `services/auth.service.ts` - Auth API service
5. `app/middleware.ts` - Route protection
**Phase 2: Authentication** (Day 5-6)
6. `app/(auth)/login/page.tsx` - Login page
7. `app/(auth)/signup/page.tsx` - Signup page
8. `app/(auth)/auth/callback/page.tsx` - SSO callback
9. `hooks/auth/useLogin.ts` - Login hook
10. `components/auth/SsoButton.tsx` - SSO button
**Phase 3: Settings Pages** (Day 6)
11. `app/(dashboard)/settings/organization/page.tsx` - SSO config
12. `app/(dashboard)/settings/mcp-tokens/page.tsx` - MCP tokens
13. `components/settings/SsoConfigForm.tsx` - SSO form
14. `components/mcp/CreateTokenDialog.tsx` - Token creation
**Phase 4: MCP Features** (Day 7)
15. `services/mcp.service.ts` - MCP API service
16. `hooks/mcp/useMcpTokens.ts` - MCP hooks
17. `components/mcp/McpPermissionMatrix.tsx` - Permission UI
18. `components/mcp/TokenDisplay.tsx` - Token display
---
## Dependencies
### Required npm Packages
```json
{
"dependencies": {
"next": "^16.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"typescript": "^5.6.0",
"zustand": "^5.0.0",
"@tanstack/react-query": "^5.60.0",
"axios": "^1.7.0",
"react-hook-form": "^7.53.0",
"zod": "^3.23.0",
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-dialog": "^1.1.0",
"@radix-ui/react-select": "^2.1.0",
"@radix-ui/react-checkbox": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"tailwind-merge": "^2.5.0",
"tailwindcss": "^4.0.0",
"jose": "^5.9.0",
"sonner": "^1.7.0",
"date-fns": "^4.1.0",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@testing-library/react": "^16.0.0",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/user-event": "^14.5.0",
"vitest": "^2.1.0",
"msw": "^2.6.0"
}
}
```
### Installation Command
```bash
cd colaflow-web
# Install dependencies
npm install
# Install shadcn/ui components
npx shadcn@latest init
npx shadcn@latest add button dialog form input select checkbox tabs alert table
```
### Environment Variables
**File**: `.env.local`
```env
# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:5000/api
NEXT_PUBLIC_APP_URL=http://localhost:3000
# JWT Configuration (for middleware validation)
JWT_SECRET=your-jwt-secret-key-from-backend
# Feature Flags
NEXT_PUBLIC_ENABLE_SSO=true
NEXT_PUBLIC_ENABLE_MCP_TOKENS=true
```
---
## Development Phases
### Phase 1: Core Infrastructure (Day 5 - Morning)
**Estimated Time**: 3-4 hours
#### 1.1 API Client Setup
**File**: `lib/api-client.ts`
**Tasks**:
- Create Axios instance with base URL
- Implement request interceptor (add Authorization header)
- Implement response interceptor (handle 401, refresh token)
- Add error handling and retry logic
**Success Criteria**:
- ✅ All API requests automatically include `Authorization: Bearer {token}`
- ✅ 401 errors trigger automatic token refresh
- ✅ Refresh only happens once for concurrent requests
- ✅ Failed refresh redirects to `/login`
#### 1.2 Auth Store Setup
**File**: `stores/useAuthStore.ts`
**Tasks**:
- Define `AuthState` interface (user, tenant, accessToken, isAuthenticated)
- Implement `login`, `logout`, `refreshToken` actions
- Implement `setUser` and `clearAuth` helpers
- Add automatic token refresh (5 min before expiry)
**Success Criteria**:
- ✅ Auth state persists across page reloads (use `zustand/middleware`)
- ✅ Token stored in memory (not localStorage)
- ✅ Automatic refresh works before token expires
#### 1.3 TypeScript Types
**Files**: `types/auth.ts`, `types/mcp.ts`, `types/api.ts`
**Tasks**:
- Define all API request/response types
- Define Zustand store types
- Export types in `types/index.ts`
**Success Criteria**:
- ✅ No TypeScript errors
- ✅ Full IntelliSense support in VSCode
---
### Phase 2: Authentication Pages (Day 5 - Afternoon + Day 6 - Morning)
**Estimated Time**: 6-8 hours
#### 2.1 Login Page
**File**: `app/(auth)/login/page.tsx`
**Features**:
- Local login form (email + password)
- SSO buttons (Azure AD, Google, Okta)
- "Forgot password" link
- "Sign up" link
- Remember me checkbox
- Loading states
- Error handling
**Components to create**:
- `components/auth/SsoButton.tsx` - Provider-specific button
- `hooks/auth/useLogin.ts` - TanStack Query mutation
- `hooks/auth/useLoginWithSso.ts` - SSO redirect logic
**Success Criteria**:
- ✅ Local login works and redirects to dashboard
- ✅ SSO buttons redirect to backend SSO endpoint
- ✅ Form validation with Zod
- ✅ Error messages displayed with `sonner` toast
#### 2.2 Signup Page
**File**: `app/(auth)/signup/page.tsx`
**Features**:
- Multi-step form (3 steps):
1. Organization info (name, slug)
2. Admin user (email, password, full name)
3. Subscription plan selection
- Real-time slug validation (debounce 500ms)
- Password strength indicator
- Terms of service checkbox
**Components to create**:
- `components/auth/TenantSlugInput.tsx` - Slug input with validation
- `components/auth/PasswordStrengthIndicator.tsx` - zxcvbn integration
- `components/auth/SubscriptionPlanCard.tsx` - Plan selection
- `hooks/tenants/useCheckSlug.ts` - TanStack Query for slug check
**Success Criteria**:
- ✅ Slug validation shows "Available" or "Taken" in real-time
- ✅ Password strength indicator works (weak/medium/strong)
- ✅ Plan selection highlights selected plan
- ✅ After signup, user is logged in automatically
#### 2.3 SSO Callback Page
**File**: `app/(auth)/auth/callback/page.tsx`
**Features**:
- Parse URL parameters (`?token=xxx&tenant=yyy`)
- Validate state parameter (CSRF protection)
- Store token in AuthStore
- Redirect to original page or dashboard
- Error handling (SSO failed)
**Success Criteria**:
- ✅ Token extracted from URL and stored
- ✅ User redirected to dashboard
- ✅ Invalid state shows error page
- ✅ Error page has "Try again" button
#### 2.4 Next.js Middleware
**File**: `app/middleware.ts`
**Features**:
- Protect routes requiring authentication
- Verify JWT token (use `jose` library)
- Check tenant status (Active/Suspended)
- Redirect logic:
- Unauthenticated → `/login?redirect=/original-path`
- Authenticated + on `/login``/dashboard`
- Suspended tenant → `/suspended`
**Success Criteria**:
- ✅ Protected routes require login
- ✅ Token validation works (JWT signature check)
- ✅ Redirect preserves original URL
- ✅ Suspended tenants can't access app
---
### Phase 3: Settings Pages (Day 6 - Afternoon)
**Estimated Time**: 5-6 hours
#### 3.1 Organization Settings Page (SSO Config)
**File**: `app/(dashboard)/settings/organization/page.tsx`
**Features**:
- Tabs: General, SSO, Billing, Usage
- **SSO Tab**:
- Provider selection dropdown (Azure AD, Google, Okta, SAML)
- Dynamic form fields based on provider
- "Test Connection" button
- "Save Configuration" button
- Allowed domains (TagInput)
- Auto-provision users toggle
**Components to create**:
- `components/settings/SsoConfigForm.tsx` - Dynamic SSO form
- `hooks/tenants/useSsoConfig.ts` - Get/Update SSO config
- `hooks/tenants/useTestSsoConnection.ts` - Test connection mutation
**Dynamic Fields Logic**:
```typescript
// OIDC providers (Azure AD, Google, Okta)
- Authority URL (required)
- Client ID (required)
- Client Secret (required, password input)
- Metadata URL (optional)
// SAML provider
- Entity ID (required)
- Sign-On URL (required)
- X.509 Certificate (textarea, required)
- Metadata URL (optional)
```
**Success Criteria**:
- ✅ Form fields change based on provider selection
- ✅ "Test Connection" shows success/error message
- ✅ "Save Configuration" updates tenant SSO config
- ✅ Form validation with Zod
- ✅ Only Admin users can edit (permission check)
---
### Phase 4: MCP Token Management (Day 7)
**Estimated Time**: 6-8 hours
#### 4.1 MCP Tokens List Page
**File**: `app/(dashboard)/settings/mcp-tokens/page.tsx`
**Features**:
- Token list table (using `@tanstack/react-table`)
- Columns: Name, Permissions, Last Used, Expires, Status, Actions
- "Generate Token" button (opens dialog)
- "Revoke" button for each token
- Token details page (click row → navigate to `/settings/mcp-tokens/{id}`)
**Components to create**:
- `hooks/mcp/useMcpTokens.ts` - List tokens query
- `hooks/mcp/useRevokeMcpToken.ts` - Revoke mutation
**Success Criteria**:
- ✅ Token list loads and displays
- ✅ Permissions shown as tags
- ✅ Last Used shows "2 hours ago" format
- ✅ Revoke confirmation dialog works
- ✅ Revoked tokens marked as "Revoked" (red badge)
#### 4.2 Create Token Dialog
**File**: `components/mcp/CreateTokenDialog.tsx`
**Features**:
- Multi-step dialog (3 steps):
1. **Basic Info**: Name, Expiration date (optional)
2. **Permissions**: Resource + Operations matrix
3. **Review & Create**: Show summary
**Permission Matrix UI**:
```
Resources | read | create | update | delete | search
---------------------------------------------------------
Projects | ☑ | ☑ | ☐ | ☐ | ☑
Issues | ☑ | ☑ | ☑ | ☐ | ☑
Documents | ☑ | ☐ | ☐ | ☐ | ☑
Reports | ☑ | ☐ | ☐ | ☐ | ☐
Sprints | ☑ | ☐ | ☐ | ☐ | ☑
```
**Components to create**:
- `components/mcp/McpPermissionMatrix.tsx` - Checkbox grid
- `components/mcp/TokenDisplay.tsx` - Display token after creation
- `hooks/mcp/useCreateMcpToken.ts` - Create token mutation
**Token Display Modal**:
- Show generated token (once only)
- Warning: "Save this token now! You won't see it again."
- Copy button (copies to clipboard)
- Download button (downloads as `.txt` file)
- "I've saved the token" button (closes modal)
**Success Criteria**:
- ✅ 3-step wizard works smoothly
- ✅ Permission matrix shows checkboxes
- ✅ Token created successfully
- ✅ Token displayed only once
- ✅ Copy and download buttons work
#### 4.3 Token Details Page (Audit Logs)
**File**: `app/(dashboard)/settings/mcp-tokens/[id]/page.tsx`
**Features**:
- Token metadata (name, created date, expires date, status)
- Usage statistics (total calls, last used)
- Audit log table:
- Columns: Timestamp, HTTP Method, Endpoint, Status Code, Duration, IP Address
- Pagination
- Filters (date range, status code)
**Components to create**:
- `components/mcp/AuditLogTable.tsx` - Audit log table
- `hooks/mcp/useMcpAuditLogs.ts` - Audit logs query
**Success Criteria**:
- ✅ Token metadata displayed
- ✅ Audit log table loads with pagination
- ✅ Date filter works
- ✅ Status code filter works (200, 401, 403, 500)
---
## Testing Strategy
### Unit Tests (Vitest + React Testing Library)
**Priority Components to Test**:
1. **Auth Components**
- `SsoButton.tsx` - Renders provider logo, triggers redirect
- `TenantSlugInput.tsx` - Shows "Available" or "Taken"
- `PasswordStrengthIndicator.tsx` - Shows correct strength level
2. **Auth Store**
- `useAuthStore.ts` - Login, logout, token refresh logic
3. **API Client**
- `lib/api-client.ts` - Token injection, 401 handling
4. **Custom Hooks**
- `useLogin.ts` - Success/error handling
- `useCheckSlug.ts` - Debouncing, caching
**Example Test**:
```typescript
// __tests__/components/auth/SsoButton.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { SsoButton } from '@/components/auth/SsoButton';
describe('SsoButton', () => {
it('renders Azure AD button with logo', () => {
render(<SsoButton provider="AzureAD" onClick={vi.fn()} />);
expect(screen.getByText(/Sign in with Microsoft/i)).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = vi.fn();
render(<SsoButton provider="Google" onClick={handleClick} />);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
```
### Integration Tests (Playwright)
**Critical User Flows**:
1. **Local Login Flow**
- Navigate to `/login`
- Enter email and password
- Click "Sign In"
- Verify redirect to `/dashboard`
- Verify token in AuthStore
2. **SSO Login Flow (Mocked)**
- Click "Sign in with Azure AD"
- Mock SSO callback with token
- Verify redirect to dashboard
3. **Create MCP Token Flow**
- Navigate to `/settings/mcp-tokens`
- Click "Generate Token"
- Fill in name and permissions
- Verify token displayed
- Verify token can be copied
### API Mocking (MSW)
**Mock Handlers**:
```typescript
// mocks/handlers.ts
import { http, HttpResponse } from 'msw';
export const handlers = [
http.post('/api/auth/login', () => {
return HttpResponse.json({
user: { id: '1', email: 'test@example.com', fullName: 'Test User' },
tenant: { id: '1', slug: 'test', name: 'Test Corp' },
accessToken: 'mock-token',
});
}),
http.get('/api/tenants/check-slug', ({ request }) => {
const url = new URL(request.url);
const slug = url.searchParams.get('slug');
return HttpResponse.json({ available: slug !== 'taken' });
}),
http.post('/api/mcp-tokens', () => {
return HttpResponse.json({
tokenId: '1',
token: 'mcp_test_abc123xyz789',
name: 'Test Token',
});
}),
];
```
---
## Performance Optimization
### 1. Code Splitting
**Lazy Load Heavy Components**:
```typescript
// app/(dashboard)/settings/mcp-tokens/page.tsx
import { lazy, Suspense } from 'react';
const CreateTokenDialog = lazy(() => import('@/components/mcp/CreateTokenDialog'));
const AuditLogTable = lazy(() => import('@/components/mcp/AuditLogTable'));
export default function McpTokensPage() {
return (
<Suspense fallback={<LoadingSpinner />}>
<CreateTokenDialog />
<AuditLogTable />
</Suspense>
);
}
```
### 2. TanStack Query Caching
**Cache Configuration**:
```typescript
// lib/query-client.ts
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 10, // 10 minutes (formerly cacheTime)
refetchOnWindowFocus: false,
retry: 1,
},
},
});
```
**Prefetch Critical Data**:
```typescript
// app/(dashboard)/layout.tsx
export default function DashboardLayout() {
const queryClient = useQueryClient();
useEffect(() => {
// Prefetch user projects
queryClient.prefetchQuery({
queryKey: ['projects'],
queryFn: () => projectService.getAll(),
});
}, []);
return <>{children}</>;
}
```
### 3. Debouncing
**Slug Validation**:
```typescript
// hooks/tenants/useCheckSlug.ts
import { useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { debounce } from 'lodash-es';
export function useCheckSlug(slug: string) {
const debouncedSlug = useMemo(
() => debounce((value: string) => value, 500),
[]
);
return useQuery({
queryKey: ['check-slug', slug],
queryFn: () => tenantService.checkSlugAvailability(slug),
enabled: slug.length >= 3,
staleTime: 5000,
});
}
```
### 4. Image Optimization
**Use Next.js Image Component**:
```tsx
import Image from 'next/image';
<Image
src="/logos/azure-ad.svg"
alt="Azure AD"
width={24}
height={24}
priority
/>
```
---
## Security Checklist
### Authentication Security
- ✅ Access tokens stored in memory (Zustand), not localStorage
- ✅ Refresh tokens in httpOnly cookies (managed by backend)
- ✅ Token expiration checked before API calls
- ✅ Automatic logout on refresh failure
- ✅ CSRF protection (state parameter for SSO)
- ✅ JWT signature validation in middleware
- ✅ Redirect to login on 401 errors
### SSO Security
- ✅ State parameter generated with crypto random (32 bytes)
- ✅ State parameter validated on callback
- ✅ State stored in sessionStorage (cleared after use)
- ✅ SSO errors logged and reported to user
- ✅ Email domain validation (if configured)
### MCP Token Security
- ✅ Token displayed only once (after creation)
- ✅ Token copied/downloaded securely
- ✅ Token revocation confirmation dialog
- ✅ Audit logs for all token operations
### General Security
- ✅ All API calls over HTTPS in production
- ✅ Sensitive data (passwords) not logged
- ✅ Error messages don't leak sensitive info
- ✅ Rate limiting on login attempts (backend)
- ✅ XSS protection (React auto-escapes by default)
---
## Deployment Checklist
### Pre-Deployment
- ✅ All unit tests pass (`npm run test`)
- ✅ All integration tests pass (`npm run test:e2e`)
- ✅ TypeScript builds without errors (`npm run build`)
- ✅ No console errors in browser
- ✅ Environment variables configured (`.env.production`)
- ✅ API URLs point to production backend
- ✅ Error tracking configured (Sentry)
### Performance Checks
- ✅ Lighthouse score > 90 (Performance, Accessibility, Best Practices, SEO)
- ✅ First Contentful Paint < 1.5s
- Time to Interactive < 3s
- Bundle size < 200KB (gzipped)
- Images optimized (WebP format)
### Security Checks
- JWT_SECRET in production environment variables
- No hardcoded secrets in code
- HTTPS enforced (Next.js redirects)
- CSP headers configured
- Security headers (X-Frame-Options, X-Content-Type-Options)
### Monitoring
- Error tracking (Sentry or similar)
- Performance monitoring (Vercel Analytics)
- API error logging
- User analytics (PostHog or similar)
---
## Estimated Effort
| Phase | Tasks | Time | Priority |
|-------|-------|------|----------|
| **Phase 1: Core Infrastructure** | API Client, Auth Store, Types, Middleware | 4 hours | P0 |
| **Phase 2: Authentication** | Login, Signup, SSO Callback, Middleware | 8 hours | P0 |
| **Phase 3: Settings** | Organization Settings, SSO Config | 6 hours | P1 |
| **Phase 4: MCP Tokens** | Token List, Create, Display, Audit Logs | 8 hours | P1 |
| **Testing** | Unit tests, Integration tests, E2E tests | 6 hours | P1 |
| **Total** | | **32 hours** (~4 days) | |
**Timeline**:
- **Day 5**: Phase 1 + Phase 2 (Login, Signup)
- **Day 6**: Phase 2 (SSO Callback) + Phase 3 (Settings)
- **Day 7**: Phase 4 (MCP Tokens)
- **Day 8**: Testing + Bug fixes
---
## Next Steps
1. **Backend API Readiness Check**
- Verify backend APIs are ready: `/api/auth/login`, `/api/tenants/check-slug`, `/api/mcp-tokens`, etc.
- Test API endpoints with Postman or Insomnia
- Document any API issues or missing endpoints
2. **Environment Setup**
- Clone frontend repo
- Install dependencies (`npm install`)
- Configure `.env.local`
- Start dev server (`npm run dev`)
3. **Start with Phase 1**
- Create `lib/api-client.ts`
- Create `stores/useAuthStore.ts`
- Create TypeScript types
- Test token injection and refresh
4. **Continuous Testing**
- Write tests as you build features
- Run tests before committing code
- Fix failing tests immediately
5. **Code Review**
- Self-review code before committing
- Use ESLint and Prettier
- Follow TypeScript strict mode
---
## Risk Mitigation
### Technical Risks
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| Token refresh fails during API calls | Medium | High | Implement queue for pending requests during refresh |
| SSO callback errors (state mismatch) | Low | High | Add detailed error logging and user-friendly error page |
| Permission matrix UI too complex | Low | Medium | Use shadcn Checkbox component, add "Select All" shortcuts |
| TanStack Query cache invalidation issues | Medium | Medium | Document cache invalidation strategy, use query keys consistently |
| Middleware performance (JWT validation) | Low | Low | Cache JWT validation results, use efficient `jose` library |
### Schedule Risks
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| Backend API delays | High | High | Mock API responses with MSW, develop UI first |
| Complex SSO flow takes longer | Medium | Medium | Simplify SSO flow, skip SAML in MVP if needed |
| Testing takes longer than expected | Medium | Medium | Prioritize critical path tests, skip edge cases for MVP |
---
## Conclusion
This implementation plan provides a clear roadmap for building ColaFlow's enterprise-level frontend features. The plan is structured to minimize risk, maximize code quality, and deliver a production-ready solution within 4 days.
**Key Success Factors**:
- Backend API readiness
- Clear component boundaries
- Comprehensive testing strategy
- Performance optimization from day 1
- Security-first approach
**Next Document**: `api-integration-guide.md` (detailed API endpoints and request/response examples)

File diff suppressed because it is too large Load Diff