Fixes from biome and consistency review.

This commit is contained in:
sean-eskerium
2025-10-09 14:26:37 -04:00
parent 4e6116fa2f
commit daf915c083
11 changed files with 339 additions and 125 deletions

View File

@@ -234,6 +234,36 @@ Save to `PRPs/reviews/ui-consistency-review-[feature].md`.
**Note**: The PRPs/reviews/ directory is gitignored and won't be committed.
### Step 7: Create Implementation PRP
After completing the review report, **immediately create a PRP** for implementing the fixes using the review findings.
**CRITICAL**: Do not stop after generating the report. The review is only the first phase - the PRP creation is required.
**Use**: `/prp-claude-code:prp-claude-code-create` command with argument: `ui-consistency-fixes-[feature]`
**PRP Should Include**:
1. **Feature Goal**: Fix all UI consistency violations identified in the review
2. **Context**: Reference the review report and specific violations with file:line numbers
3. **Implementation Tasks**: Ordered by priority (Critical → High → Medium → Low)
- Each task should reference specific violations from the review
- Include exact code snippets for fixes (from review report)
- Use dependency ordering (e.g., fix unconstrained scrolls before testing)
4. **Validation Gates**:
- Re-run automated scans from Step 3
- Verify all violations are fixed
- Test responsive behavior at all breakpoints (375px, 768px, 1024px, 1440px)
5. **Success Metrics**:
- Zero violations in automated scans
- All scores improved to 10/10
- Overall grade improved to A or A+
**PRP Template Sections to Emphasize**:
- **codebase_patterns**: Link to review report and UI_STANDARDS.md sections violated
- **existing_code**: Include specific file:line references from violation findings
- **implementation_notes**: Include "why this matters" context from review report
- **edge_cases**: Include responsive testing requirements and dark mode validation
---
Start the review now.
Start the review now and create the PRP when complete.

View File

@@ -1,7 +1,7 @@
import { Layout, Palette } from "lucide-react";
import { useState } from "react";
import { PillNavigation, type PillNavigationItem } from "@/features/ui/primitives/pill-navigation";
import { ThemeToggle } from "../../../components/ui/ThemeToggle";
import { PillNavigation, type PillNavigationItem } from "../shared/PillNavigation";
import { LayoutsTab } from "../tabs/LayoutsTab";
import { StyleGuideTab } from "../tabs/StyleGuideTab";

View File

@@ -1,5 +1,5 @@
export { PillNavigation, type PillNavigationItem } from "@/features/ui/primitives/pill-navigation";
export { StyleGuideView } from "./components/StyleGuideView";
export { PillNavigation } from "./shared/PillNavigation";
export { SideNavigation } from "./shared/SideNavigation";
export { LayoutsTab } from "./tabs/LayoutsTab";
export { StyleGuideTab } from "./tabs/StyleGuideTab";

View File

@@ -1,9 +1,8 @@
import { Code, File, FileText, Globe, Search } from "lucide-react";
import { Code, FileText, Globe, Search } from "lucide-react";
import { useState } from "react";
import { Button } from "@/features/ui/primitives/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/features/ui/primitives/dialog";
import { Dialog, DialogContent } from "@/features/ui/primitives/dialog";
import { Input } from "@/features/ui/primitives/input";
import { StatPill } from "@/features/ui/primitives/pill";
import { cn } from "@/features/ui/primitives/styles";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/features/ui/primitives/tabs";
@@ -75,7 +74,6 @@ const DocumentBrowserModal = ({ open, onOpenChange }: { open: boolean; onOpenCha
const [searchQuery, setSearchQuery] = useState("");
const [selectedDoc, setSelectedDoc] = useState(MOCK_DOCUMENTS[0]);
const [selectedCode, setSelectedCode] = useState(MOCK_CODE[0]);
const [sourceType, setSourceType] = useState<"web" | "document">("web");
const filteredDocuments = MOCK_DOCUMENTS.filter((doc) => doc.title.toLowerCase().includes(searchQuery.toLowerCase()));
@@ -231,7 +229,7 @@ const DocumentBrowserModal = ({ open, onOpenChange }: { open: boolean; onOpenCha
<h3 className="text-lg font-semibold text-white">{selectedCode.summary}</h3>
<span className="px-2 py-1 text-xs bg-cyan-500/20 text-cyan-400 rounded">{selectedCode.language}</span>
</div>
<pre className="bg-black/30 rounded-lg p-4 overflow-x-auto">
<pre className="bg-black/30 rounded-lg p-4 overflow-x-auto scrollbar-hide">
<code className="text-gray-300 text-sm">{selectedCode.code}</code>
</pre>
</div>

View File

@@ -133,23 +133,25 @@ export const KnowledgeLayoutExample = () => {
</div>
) : (
// Table View - matching TaskView standard pattern
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border-b-2 border-gray-200 dark:border-gray-700">
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Title</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Type</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Source</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Chunks</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Date</th>
</tr>
</thead>
<tbody>
{MOCK_KNOWLEDGE_ITEMS.map((item, index) => (
<KnowledgeTableRow key={item.id} item={item} index={index} />
))}
</tbody>
</table>
<div className="w-full">
<div className="overflow-x-auto scrollbar-hide">
<table className="w-full">
<thead>
<tr className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border-b-2 border-gray-200 dark:border-gray-700">
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Title</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Type</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Source</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Chunks</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Date</th>
</tr>
</thead>
<tbody>
{MOCK_KNOWLEDGE_ITEMS.map((item, index) => (
<KnowledgeTableRow key={item.id} item={item} index={index} />
))}
</tbody>
</table>
</div>
</div>
)}

View File

@@ -21,10 +21,10 @@ import { Button } from "@/features/ui/primitives/button";
import { DraggableCard } from "@/features/ui/primitives/draggable-card";
import { Input } from "@/features/ui/primitives/input";
import { StatPill } from "@/features/ui/primitives/pill";
import { PillNavigation, type PillNavigationItem } from "@/features/ui/primitives/pill-navigation";
import { SelectableCard } from "@/features/ui/primitives/selectable-card";
import { cn } from "@/features/ui/primitives/styles";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip";
import { PillNavigation, type PillNavigationItem } from "../shared/PillNavigation";
const MOCK_PROJECTS = [
{
@@ -731,7 +731,7 @@ const TaskCardExample = ({ task, index }: { task: (typeof MOCK_TASKS)[0]; index:
const TaskTableView = () => {
return (
<div className="w-full">
<div className="overflow-x-auto">
<div className="overflow-x-auto scrollbar-hide">
<table className="w-full">
<thead>
<tr className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border-b-2 border-gray-200 dark:border-gray-700">

View File

@@ -1,4 +1,5 @@
import { Code, Database, FileText, Flame, Globe, Key, Monitor, Moon, Palette, Settings } from "lucide-react";
import { useId } from "react";
import { CollapsibleSettingsCard } from "@/components/ui/CollapsibleSettingsCard";
import { Card } from "@/features/ui/primitives/card";
import { Input } from "@/features/ui/primitives/input";
@@ -6,6 +7,17 @@ import { Label } from "@/features/ui/primitives/label";
import { Switch } from "@/features/ui/primitives/switch";
export const SettingsLayoutExample = () => {
const openaiKeyId = useId();
const googleKeyId = useId();
const dbUrlId = useId();
const autoBackupId = useId();
const extractCodeId = useId();
const maxExamplesId = useId();
const matchCountId = useId();
const rerankId = useId();
const maxDepthId = useId();
const followLinksId = useId();
return (
<div className="space-y-4">
{/* Explanation Text */}
@@ -84,11 +96,11 @@ export const SettingsLayoutExample = () => {
</p>
<div className="space-y-4">
<div>
<Label htmlFor="openai-key" className="text-xs font-medium text-gray-600 dark:text-gray-400">
<Label htmlFor={openaiKeyId} className="text-xs font-medium text-gray-600 dark:text-gray-400">
OPENAI_API_KEY
</Label>
<Input
id="openai-key"
id={openaiKeyId}
type="password"
placeholder="Enter new value (encrypted)"
className="mt-2"
@@ -96,11 +108,11 @@ export const SettingsLayoutExample = () => {
/>
</div>
<div>
<Label htmlFor="google-key" className="text-xs font-medium text-gray-600 dark:text-gray-400">
<Label htmlFor={googleKeyId} className="text-xs font-medium text-gray-600 dark:text-gray-400">
GOOGLE_API_KEY
</Label>
<Input
id="google-key"
id={googleKeyId}
type="password"
placeholder="Enter new value (encrypted)"
className="mt-2"
@@ -115,21 +127,21 @@ export const SettingsLayoutExample = () => {
<CollapsibleSettingsCard title="Database Settings" icon={Database} accentColor="blue" defaultExpanded={false}>
<Card edgePosition="top" edgeColor="blue">
<div>
<Label htmlFor="db-url" className="text-sm font-medium">
<Label htmlFor={dbUrlId} className="text-sm font-medium">
Database URL
</Label>
<Input
id="db-url"
id={dbUrlId}
placeholder="postgresql://..."
className="mt-2"
defaultValue="postgresql://localhost:5432/archon"
/>
</div>
<div className="flex items-center justify-between mt-4">
<Label htmlFor="auto-backup" className="text-sm font-medium">
<Label htmlFor={autoBackupId} className="text-sm font-medium">
Auto Backup
</Label>
<Switch id="auto-backup" />
<Switch id={autoBackupId} />
</div>
</Card>
</CollapsibleSettingsCard>
@@ -141,16 +153,16 @@ export const SettingsLayoutExample = () => {
Configure how code blocks are extracted from crawled documents.
</p>
<div className="flex items-center justify-between mb-4">
<Label htmlFor="extract-code" className="text-sm font-medium">
<Label htmlFor={extractCodeId} className="text-sm font-medium">
Extract Code Examples
</Label>
<Switch id="extract-code" defaultChecked />
<Switch id={extractCodeId} defaultChecked />
</div>
<div>
<Label htmlFor="max-examples" className="text-sm font-medium">
<Label htmlFor={maxExamplesId} className="text-sm font-medium">
Max Examples per Source
</Label>
<Input id="max-examples" type="number" placeholder="50" className="mt-2" defaultValue="50" />
<Input id={maxExamplesId} type="number" placeholder="50" className="mt-2" defaultValue="50" />
</div>
</Card>
</CollapsibleSettingsCard>
@@ -159,16 +171,16 @@ export const SettingsLayoutExample = () => {
<CollapsibleSettingsCard title="RAG Configuration" icon={Settings} accentColor="orange" defaultExpanded={true}>
<Card edgePosition="top" edgeColor="orange">
<div>
<Label htmlFor="match-count" className="text-sm font-medium">
<Label htmlFor={matchCountId} className="text-sm font-medium">
Match Count
</Label>
<Input id="match-count" type="number" placeholder="5" className="mt-2" defaultValue="5" />
<Input id={matchCountId} type="number" placeholder="5" className="mt-2" defaultValue="5" />
</div>
<div className="flex items-center justify-between mt-4">
<Label htmlFor="rerank" className="text-sm font-medium">
<Label htmlFor={rerankId} className="text-sm font-medium">
Enable Reranking
</Label>
<Switch id="rerank" defaultChecked />
<Switch id={rerankId} defaultChecked />
</div>
</Card>
</CollapsibleSettingsCard>
@@ -177,16 +189,16 @@ export const SettingsLayoutExample = () => {
<CollapsibleSettingsCard title="Crawling Settings" icon={Globe} accentColor="pink" defaultExpanded={false}>
<Card edgePosition="top" edgeColor="pink">
<div>
<Label htmlFor="max-depth" className="text-sm font-medium">
<Label htmlFor={maxDepthId} className="text-sm font-medium">
Max Crawl Depth
</Label>
<Input id="max-depth" type="number" placeholder="3" className="mt-2" defaultValue="3" />
<Input id={maxDepthId} type="number" placeholder="3" className="mt-2" defaultValue="3" />
</div>
<div className="flex items-center justify-between mt-4">
<Label htmlFor="follow-links" className="text-sm font-medium">
<Label htmlFor={followLinksId} className="text-sm font-medium">
Follow External Links
</Label>
<Switch id="follow-links" />
<Switch id={followLinksId} />
</div>
</Card>
</CollapsibleSettingsCard>

View File

@@ -1,3 +1,4 @@
import { useId } from "react";
import { Button } from "@/features/ui/primitives/button";
import { Card } from "@/features/ui/primitives/card";
import { Checkbox } from "@/features/ui/primitives/checkbox";
@@ -7,6 +8,19 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Switch } from "@/features/ui/primitives/switch";
export const StaticForms = () => {
const exampleInputId = useId();
const exampleDisabledId = useId();
const exampleTextareaId = useId();
const check1Id = useId();
const check2Id = useId();
const check3Id = useId();
const switch1Id = useId();
const switch2Id = useId();
const switch3Id = useId();
const selectCyanId = useId();
const selectPurpleId = useId();
const formInputId = useId();
return (
<div className="space-y-6">
<div>
@@ -20,12 +34,12 @@ export const StaticForms = () => {
<h4 className="text-sm font-semibold mb-3 text-gray-900 dark:text-white">Text Input</h4>
<div className="space-y-3">
<div>
<Label htmlFor="example-input">Label</Label>
<Input id="example-input" placeholder="Enter text..." className="mt-1" />
<Label htmlFor={exampleInputId}>Label</Label>
<Input id={exampleInputId} placeholder="Enter text..." className="mt-1" />
</div>
<div>
<Label htmlFor="example-disabled">Disabled</Label>
<Input id="example-disabled" placeholder="Disabled..." disabled className="mt-1" />
<Label htmlFor={exampleDisabledId}>Disabled</Label>
<Input id={exampleDisabledId} placeholder="Disabled..." disabled className="mt-1" />
</div>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-3 font-mono">{"<Input />"}</p>
@@ -35,9 +49,9 @@ export const StaticForms = () => {
<Card className="p-6">
<h4 className="text-sm font-semibold mb-3 text-gray-900 dark:text-white">Textarea</h4>
<div>
<Label htmlFor="example-textarea">Description</Label>
<Label htmlFor={exampleTextareaId}>Description</Label>
<textarea
id="example-textarea"
id={exampleTextareaId}
placeholder="Enter description..."
rows={4}
className="mt-1 w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white/50 dark:bg-black/30 px-3 py-2 text-sm backdrop-blur-sm focus:border-cyan-500 focus:outline-none"
@@ -51,16 +65,16 @@ export const StaticForms = () => {
<h4 className="text-sm font-semibold mb-3 text-gray-900 dark:text-white">Checkbox</h4>
<div className="space-y-3">
<div className="flex items-center gap-2">
<Checkbox id="check-1" defaultChecked color="cyan" />
<Label htmlFor="check-1">Checked</Label>
<Checkbox id={check1Id} defaultChecked color="cyan" />
<Label htmlFor={check1Id}>Checked</Label>
</div>
<div className="flex items-center gap-2">
<Checkbox id="check-2" color="purple" />
<Label htmlFor="check-2">Unchecked</Label>
<Checkbox id={check2Id} color="purple" />
<Label htmlFor={check2Id}>Unchecked</Label>
</div>
<div className="flex items-center gap-2">
<Checkbox id="check-3" disabled defaultChecked />
<Label htmlFor="check-3">Disabled</Label>
<Checkbox id={check3Id} disabled defaultChecked />
<Label htmlFor={check3Id}>Disabled</Label>
</div>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-3 font-mono">{"<Checkbox />"}</p>
@@ -71,16 +85,16 @@ export const StaticForms = () => {
<h4 className="text-sm font-semibold mb-3 text-gray-900 dark:text-white">Switch Toggle</h4>
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label htmlFor="switch-1">Enable Feature</Label>
<Switch id="switch-1" defaultChecked />
<Label htmlFor={switch1Id}>Enable Feature</Label>
<Switch id={switch1Id} defaultChecked />
</div>
<div className="flex items-center justify-between">
<Label htmlFor="switch-2">Dark Mode</Label>
<Switch id="switch-2" />
<Label htmlFor={switch2Id}>Dark Mode</Label>
<Switch id={switch2Id} />
</div>
<div className="flex items-center justify-between">
<Label htmlFor="switch-3">Disabled</Label>
<Switch id="switch-3" disabled />
<Label htmlFor={switch3Id}>Disabled</Label>
<Switch id={switch3Id} disabled />
</div>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-3 font-mono">{"<Switch />"}</p>
@@ -91,9 +105,9 @@ export const StaticForms = () => {
<h4 className="text-sm font-semibold mb-3 text-gray-900 dark:text-white">Select Dropdown</h4>
<div className="space-y-3">
<div>
<Label htmlFor="select-cyan">Cyan Variant</Label>
<Label htmlFor={selectCyanId}>Cyan Variant</Label>
<Select defaultValue="option2">
<SelectTrigger id="select-cyan" color="cyan" className="mt-1">
<SelectTrigger id={selectCyanId} color="cyan" className="mt-1">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -104,9 +118,9 @@ export const StaticForms = () => {
</Select>
</div>
<div>
<Label htmlFor="select-purple">Purple Variant</Label>
<Label htmlFor={selectPurpleId}>Purple Variant</Label>
<Select defaultValue="option1">
<SelectTrigger id="select-purple" color="purple" className="mt-1">
<SelectTrigger id={selectPurpleId} color="purple" className="mt-1">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -125,8 +139,8 @@ export const StaticForms = () => {
<h4 className="text-sm font-semibold mb-3 text-gray-900 dark:text-white">Form Submission</h4>
<div className="space-y-3">
<div>
<Label htmlFor="form-input">Email</Label>
<Input id="form-input" type="email" placeholder="email@example.com" className="mt-1" />
<Label htmlFor={formInputId}>Email</Label>
<Input id={formInputId} type="email" placeholder="email@example.com" className="mt-1" />
</div>
<Button variant="default" className="w-full">
Submit

View File

@@ -9,55 +9,57 @@ export const StaticTables = () => {
{/* Standard Table - matching TaskView pattern */}
<div>
<h3 className="text-sm font-semibold mb-3 text-gray-800 dark:text-gray-200">Standard Table</h3>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border-b-2 border-gray-200 dark:border-gray-700">
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Name</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Status</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Count</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Date</th>
</tr>
</thead>
<tbody>
<tr className="bg-white/50 dark:bg-black/50 hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20 border-b border-gray-200 dark:border-gray-800 transition-all duration-200">
<td className="px-4 py-2">
<span className="font-medium text-sm text-gray-900 dark:text-white">React Documentation</span>
</td>
<td className="px-4 py-2">
<span className="px-2 py-1 text-xs rounded-md font-medium bg-green-500/10 text-green-600 dark:text-green-400">
Active
</span>
</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">145</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">2024-01-15</td>
</tr>
<tr className="bg-gray-50/80 dark:bg-gray-900/30 hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20 border-b border-gray-200 dark:border-gray-800 transition-all duration-200">
<td className="px-4 py-2">
<span className="font-medium text-sm text-gray-900 dark:text-white">API Integration</span>
</td>
<td className="px-4 py-2">
<span className="px-2 py-1 text-xs rounded-md font-medium bg-yellow-500/10 text-yellow-600 dark:text-yellow-400">
Processing
</span>
</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">89</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">2024-01-18</td>
</tr>
<tr className="bg-white/50 dark:bg-black/50 hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20 border-b border-gray-200 dark:border-gray-800 transition-all duration-200">
<td className="px-4 py-2">
<span className="font-medium text-sm text-gray-900 dark:text-white">TypeScript Guide</span>
</td>
<td className="px-4 py-2">
<span className="px-2 py-1 text-xs rounded-md font-medium bg-cyan-500/10 text-cyan-600 dark:text-cyan-400">
Complete
</span>
</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">203</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">2024-01-20</td>
</tr>
</tbody>
</table>
<div className="w-full">
<div className="overflow-x-auto scrollbar-hide">
<table className="w-full">
<thead>
<tr className="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border-b-2 border-gray-200 dark:border-gray-700">
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Name</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Status</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Count</th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700 dark:text-gray-300">Date</th>
</tr>
</thead>
<tbody>
<tr className="bg-white/50 dark:bg-black/50 hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20 border-b border-gray-200 dark:border-gray-800 transition-all duration-200">
<td className="px-4 py-2">
<span className="font-medium text-sm text-gray-900 dark:text-white">React Documentation</span>
</td>
<td className="px-4 py-2">
<span className="px-2 py-1 text-xs rounded-md font-medium bg-green-500/10 text-green-600 dark:text-green-400">
Active
</span>
</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">145</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">2024-01-15</td>
</tr>
<tr className="bg-gray-50/80 dark:bg-gray-900/30 hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20 border-b border-gray-200 dark:border-gray-800 transition-all duration-200">
<td className="px-4 py-2">
<span className="font-medium text-sm text-gray-900 dark:text-white">API Integration</span>
</td>
<td className="px-4 py-2">
<span className="px-2 py-1 text-xs rounded-md font-medium bg-yellow-500/10 text-yellow-600 dark:text-yellow-400">
Processing
</span>
</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">89</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">2024-01-18</td>
</tr>
<tr className="bg-white/50 dark:bg-black/50 hover:bg-gradient-to-r hover:from-cyan-50/70 hover:to-purple-50/70 dark:hover:from-cyan-900/20 dark:hover:to-purple-900/20 border-b border-gray-200 dark:border-gray-800 transition-all duration-200">
<td className="px-4 py-2">
<span className="font-medium text-sm text-gray-900 dark:text-white">TypeScript Guide</span>
</td>
<td className="px-4 py-2">
<span className="px-2 py-1 text-xs rounded-md font-medium bg-cyan-500/10 text-cyan-600 dark:text-cyan-400">
Complete
</span>
</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">203</td>
<td className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400">2024-01-20</td>
</tr>
</tbody>
</table>
</div>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-3">
Features: Gradient header, alternating rows, hover gradient, consistent spacing

View File

@@ -1,10 +1,13 @@
import { useState } from "react";
import { useId, useState } from "react";
import { PowerButton } from "@/components/ui/PowerButton";
import { Card } from "@/features/ui/primitives/card";
import { Label } from "@/features/ui/primitives/label";
import { Switch } from "@/features/ui/primitives/switch";
export const StaticToggles = () => {
const toggle1Id = useId();
const toggle2Id = useId();
const [powerStates, setPowerStates] = useState({
purple: true,
cyan: false,
@@ -74,12 +77,12 @@ export const StaticToggles = () => {
</p>
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label htmlFor="toggle-1">Enable Feature</Label>
<Switch id="toggle-1" defaultChecked />
<Label htmlFor={toggle1Id}>Enable Feature</Label>
<Switch id={toggle1Id} defaultChecked />
</div>
<div className="flex items-center justify-between">
<Label htmlFor="toggle-2">Auto Save</Label>
<Switch id="toggle-2" />
<Label htmlFor={toggle2Id}>Auto Save</Label>
<Switch id={toggle2Id} />
</div>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-4 font-mono">{"<Switch />"}</p>

View File

@@ -0,0 +1,153 @@
import { ChevronRight } from "lucide-react";
import type { ReactNode } from "react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/features/ui/primitives/select";
import { cn } from "@/features/ui/primitives/styles";
export interface PillNavigationItem {
id: string;
label: string;
icon?: ReactNode;
items?: string[];
}
interface PillNavigationProps {
items: PillNavigationItem[];
activeSection: string;
activeItem?: string;
onSectionClick: (sectionId: string) => void;
onItemClick?: (item: string) => void;
colorVariant?: "blue" | "orange" | "cyan" | "purple" | "emerald";
size?: "small" | "default" | "large";
showIcons?: boolean;
showText?: boolean;
hasSubmenus?: boolean;
openDropdown?: string | null;
}
export const PillNavigation = ({
items,
activeSection,
activeItem,
onSectionClick,
onItemClick,
colorVariant = "cyan",
size = "default",
showIcons = true,
showText = true,
hasSubmenus = true,
openDropdown,
}: PillNavigationProps) => {
const getColorClasses = (variant: string, isSelected: boolean) => {
const colors = {
blue: isSelected
? "bg-blue-500/20 dark:bg-blue-400/20 text-blue-700 dark:text-blue-300 border border-blue-400/50 shadow-[0_0_10px_rgba(59,130,246,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5",
orange: isSelected
? "bg-orange-500/20 dark:bg-orange-400/20 text-orange-700 dark:text-orange-300 border border-orange-400/50 shadow-[0_0_10px_rgba(251,146,60,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5",
cyan: isSelected
? "bg-cyan-500/20 dark:bg-cyan-400/20 text-cyan-700 dark:text-cyan-300 border border-cyan-400/50 shadow-[0_0_10px_rgba(34,211,238,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5",
purple: isSelected
? "bg-purple-500/20 dark:bg-purple-400/20 text-purple-700 dark:text-purple-300 border border-purple-400/50 shadow-[0_0_10px_rgba(147,51,234,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5",
emerald: isSelected
? "bg-emerald-500/20 dark:bg-emerald-400/20 text-emerald-700 dark:text-emerald-300 border border-emerald-400/50 shadow-[0_0_10px_rgba(16,185,129,0.5)]"
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5",
};
return colors[variant as keyof typeof colors] || colors.cyan;
};
const getSizeClasses = (sizeVariant: string) => {
const sizes = {
small: "px-4 py-2 text-xs",
default: "px-6 py-3 text-sm",
large: "px-8 py-4 text-base",
};
return sizes[sizeVariant as keyof typeof sizes] || sizes.default;
};
return (
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border border-white/30 dark:border-white/15 rounded-full p-1 shadow-lg transition-all duration-300 ease-in-out">
<div className="flex gap-1 items-center">
{items.map((item) => {
const isSelected = activeSection === item.id;
const hasDropdown = hasSubmenus && item.items && item.items.length > 0;
const isThisExpanded = openDropdown === item.id && hasDropdown;
return (
<div key={item.id} className="relative">
{/* Extended pill for selected item with dropdown */}
{isSelected && hasDropdown ? (
<div
className={cn(
"flex items-center gap-2 rounded-full transition-all duration-200",
"font-medium whitespace-nowrap",
getSizeClasses(size),
getColorClasses(colorVariant, true),
)}
>
{showIcons && item.icon}
{showText && item.label}
{/* Dropdown selector inside the pill */}
{onItemClick && (
<div className="flex items-center ml-4 pl-4 border-l border-current/30">
<Select value={activeItem || ""} onValueChange={onItemClick}>
<SelectTrigger
className="bg-transparent border-none outline-none font-medium cursor-pointer text-inherit w-auto px-0 hover:border-none focus:border-none focus:shadow-none"
showChevron={false}
color={colorVariant}
>
<SelectValue placeholder="Select..." />
</SelectTrigger>
<SelectContent color={colorVariant}>
{item.items?.map((subItem) => (
<SelectItem key={subItem} value={subItem} color={colorVariant}>
{subItem}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
<ChevronRight
className={cn(
"w-4 h-4 transition-transform duration-300 ml-2 cursor-pointer",
isThisExpanded ? "-rotate-90" : "rotate-0",
)}
onClick={() => onSectionClick(item.id)}
/>
</div>
) : (
/* Regular pill for non-selected items */
<button
type="button"
onClick={() => onSectionClick(item.id)}
className={cn(
"flex items-center gap-2 rounded-full transition-all duration-200",
"font-medium whitespace-nowrap",
getSizeClasses(size),
getColorClasses(colorVariant, isSelected),
)}
>
{showIcons && item.icon}
{showText && item.label}
{hasDropdown && (
<ChevronRight
className={cn(
"w-4 h-4 transition-transform duration-300",
isThisExpanded ? "-rotate-90" : "rotate-0",
)}
/>
)}
</button>
)}
</div>
);
})}
</div>
</div>
);
};