Marketplace frontend-agent
Handles frontend/UX/route work for Unite-Hub. Fixes UI bugs, implements React components, updates layouts, ensures responsive design, and maintains shadcn/ui consistency.
git clone https://github.com/aiskillstore/marketplace
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/cleanexpo/frontend-agent" ~/.claude/skills/aiskillstore-marketplace-frontend-agent && rm -rf "$T"
skills/cleanexpo/frontend-agent/SKILL.mdFrontend Agent Skill
⚠️ PRE-GENERATION CHECKLIST (MANDATORY)
Before creating ANY UI component, complete this checklist:
PRE_GENERATION_CHECKLIST: 1. READ_DESIGN_SYSTEM: - [ ] Read /DESIGN-SYSTEM.md for forbidden patterns - [ ] Check /src/app/globals.css @theme block for tokens - [ ] Note: accent-500 = #ff6b35 (orange) 2. CHECK_EXISTING_COMPONENTS: - [ ] Look in /src/components/ui/ first (48 components) - [ ] Check components.json for shadcn configuration - [ ] Review existing patterns in landing page 3. REFERENCE_UI_LIBRARIES: - [ ] See /docs/UI-LIBRARY-INDEX.md for premium components - [ ] Priority: Project → StyleUI/KokonutUI/Cult UI → shadcn base - [ ] NEVER use shadcn defaults without customization 4. VERIFY_NO_FORBIDDEN_PATTERNS: - [ ] No bg-white, text-gray-600, or generic hover states - [ ] No uniform grid-cols-3 gap-4 layouts - [ ] No unstyled <Card className="p-6"> - [ ] No icons without brand colors
FORBIDDEN CODE PATTERNS:
// ❌ NEVER GENERATE THESE className="bg-white rounded-lg shadow p-4" // Generic card className="grid grid-cols-3 gap-4" // Uniform grid className="text-gray-600" // Default muted className="hover:bg-gray-100" // Generic hover <Card className="p-6"> // Unstyled shadcn
REQUIRED PATTERNS:
// ✅ ALWAYS USE DESIGN TOKENS className="bg-bg-card border border-border-base hover:border-accent-500" className="text-text-primary" className="text-text-secondary" className="bg-accent-500 hover:bg-accent-400"
Overview
The Frontend Agent is responsible for all UI/UX work in the Unite-Hub Next.js application:
- React 19 / Next.js 16 development with App Router
- shadcn/ui component implementation and customization
- Tailwind CSS styling and responsive design
- Route creation and breadcrumb setup
- Client-side state management (React Context, hooks)
- Accessibility and performance optimization
How to Use This Agent
Trigger
User says: "Fix dashboard layout", "Add new contact page", "Update navigation", "Create modal component"
What the Agent Does
1. Understand the Request
Questions to Ask:
- Which page/component needs work?
- What's the desired behavior?
- Are there design references (screenshots, wireframes)?
- What's the priority (P0/P1/P2)?
2. Analyze Current Implementation
Step A: Locate Files
# Find the component or page find src/app -name "*.tsx" | grep -i "contacts" find src/components -name "*.tsx" | grep -i "hotleads"
Step B: Read Current Code
// Use text_editor tool text_editor.view("src/app/dashboard/contacts/page.tsx")
Step C: Identify Dependencies
- What shadcn/ui components are used?
- What contexts are consumed (AuthContext, etc.)?
- What API routes are called?
- What database queries are made?
3. Implement Changes
Step A: Component Updates
For existing components:
// src/components/HotLeadsPanel.tsx import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { useAuth } from "@/contexts/AuthContext"; export function HotLeadsPanel({ workspaceId }: { workspaceId: string }) { const { currentOrganization } = useAuth(); // Fetch hot leads const [leads, setLeads] = useState([]); useEffect(() => { async function fetchLeads() { const res = await fetch("/api/agents/contact-intelligence", { method: "POST", body: JSON.stringify({ action: "get_hot_leads", workspaceId }), }); const data = await res.json(); setLeads(data.leads || []); } if (workspaceId) fetchLeads(); }, [workspaceId]); return ( <Card> {/* UI implementation */} </Card> ); }
Step B: Route Creation
For new pages:
// src/app/dashboard/new-page/page.tsx import { Metadata } from "next"; export const metadata: Metadata = { title: "New Page | Unite Hub", description: "Description of new page" }; export default async function NewPage() { return ( <div className="container mx-auto py-8"> <h1 className="text-3xl font-bold">New Page</h1> {/* Content */} </div> ); }
Step C: shadcn/ui Components
Install new components if needed:
npx shadcn@latest add dialog npx shadcn@latest add dropdown-menu npx shadcn@latest add toast
Use components following shadcn patterns:
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; <Dialog> <DialogTrigger asChild> <Button>Open Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Title</DialogTitle> <DialogDescription>Description</DialogDescription> </DialogHeader> {/* Content */} </DialogContent> </Dialog>
4. Add Workspace Filtering (CRITICAL for V1)
All database queries MUST filter by workspace:
// ❌ BAD - Shows data from all workspaces const { data: contacts } = await supabase .from("contacts") .select("*"); // ✅ GOOD - Only shows data from user's workspace const { data: contacts } = await supabase .from("contacts") .select("*") .eq("workspace_id", workspaceId);
Required for these tables:
-contacts.eq("workspace_id", workspaceId)
-campaigns.eq("workspace_id", workspaceId)
-drip_campaigns.eq("workspace_id", workspaceId)
-emails.eq("workspace_id", workspaceId)
-generatedContent.eq("workspace_id", workspaceId)
5. Handle Loading and Error States
Loading State:
const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { async function fetchData() { try { setIsLoading(true); const data = await fetch("..."); setData(data); } catch (err) { setError(err.message); } finally { setIsLoading(false); } } fetchData(); }, []); if (isLoading) return <Spinner />; if (error) return <ErrorBanner message={error} />; return <DataDisplay data={data} />;
6. Responsive Design
Tailwind Breakpoints:
<div className=" grid grid-cols-1 // Mobile: 1 column md:grid-cols-2 // Tablet: 2 columns lg:grid-cols-3 // Desktop: 3 columns gap-4 "> {/* Cards */} </div>
Mobile-First Approach:
- Start with mobile layout (default classes)
- Add
classes for tabletmd: - Add
andlg:
for desktopxl:
7. Test Changes
Step A: Visual Testing
# Start dev server npm run dev # Navigate to page in browser # Test on mobile viewport (DevTools) # Test dark theme
Step B: Accessibility
// Check for: // - Proper ARIA labels // - Keyboard navigation // - Focus states // - Screen reader support <button aria-label="Close dialog">×</button> <input aria-describedby="email-help" /> <div role="alert" aria-live="polite">{error}</div>
Step C: Performance
// Use React.memo for expensive components import { memo } from "react"; export const ExpensiveComponent = memo(function ExpensiveComponent({ data }) { return <div>{/* Render */}</div>; }); // Use dynamic imports for heavy components import dynamic from "next/dynamic"; const HeavyChart = dynamic(() => import("@/components/HeavyChart"), { loading: () => <Spinner />, ssr: false });
Common Tasks
Task 1: Fix Missing Workspace Filter
Example: Dashboard Overview page showing all contacts
Steps:
- Read
src/app/dashboard/overview/page.tsx - Find all Supabase queries
- Add
to each.eq("workspace_id", workspaceId) - Add null check for workspaceId before querying
- Test with multiple workspaces
Code:
// Before const { data: contacts } = await supabase.from("contacts").select("*"); // After if (!workspaceId) { return <div>No workspace selected</div>; } const { data: contacts, error } = await supabase .from("contacts") .select("*") .eq("workspace_id", workspaceId); if (error) { console.error("Error fetching contacts:", error); return <ErrorBanner />; }
Task 2: Create New Dashboard Page
Example: Add "Analytics" page to dashboard
Steps:
- Create
src/app/dashboard/analytics/page.tsx - Add to navigation in
src/app/dashboard/layout.tsx - Implement page content with shadcn/ui components
- Add breadcrumbs
- Test navigation
Code:
// src/app/dashboard/analytics/page.tsx import { Metadata } from "next"; import { Card } from "@/components/ui/card"; export const metadata: Metadata = { title: "Analytics | Unite Hub", }; export default async function AnalyticsPage() { return ( <div className="container mx-auto py-8 space-y-6"> <div> <h1 className="text-3xl font-bold">Analytics</h1> <p className="text-muted-foreground">Track your campaign performance</p> </div> <div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <Card> {/* Stat card 1 */} </Card> <Card> {/* Stat card 2 */} </Card> <Card> {/* Stat card 3 */} </Card> </div> </div> ); }
// src/app/dashboard/layout.tsx - Add to navigation const navigation = [ { name: "Dashboard", href: "/dashboard/overview", icon: HomeIcon }, { name: "Contacts", href: "/dashboard/contacts", icon: UsersIcon }, { name: "Campaigns", href: "/dashboard/campaigns", icon: MailIcon }, { name: "Analytics", href: "/dashboard/analytics", icon: ChartIcon }, // NEW ];
Task 3: Implement Button Functionality
Example: Hot Leads panel "Send Email" button
Steps:
- Read
src/components/HotLeadsPanel.tsx - Find button location
- Implement onClick handler
- Call appropriate API endpoint
- Show success/error toast
Code:
import { useToast } from "@/components/ui/use-toast"; function HotLeadsPanel() { const { toast } = useToast(); async function handleSendEmail(contactId: string) { try { const res = await fetch("/api/emails/send", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ contactId, templateType: "followup" }), }); if (!res.ok) throw new Error("Failed to send email"); toast({ title: "Email sent", description: "Your email has been queued for sending.", }); } catch (error) { toast({ variant: "destructive", title: "Error", description: error.message, }); } } return ( <Button onClick={() => handleSendEmail(contact.id)}> Send Email </Button> ); }
Styling Guidelines
Tailwind CSS Best Practices
Use Utility Classes:
// ✅ Good <div className="flex items-center justify-between p-4 bg-background border rounded-lg"> // ❌ Bad (custom CSS) <div style={{ display: "flex", padding: "16px" }}>
Use CSS Variables from Theme:
// Defined in globals.css @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --primary: 222.2 47.4% 11.2%; } } // Use in components <div className="bg-background text-foreground"> <div className="bg-card text-card-foreground"> <div className="bg-primary text-primary-foreground">
Responsive Design:
<div className=" text-sm md:text-base lg:text-lg p-2 md:p-4 lg:p-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 ">
Component Library Reference
shadcn/ui Components Available
- Collapsible content panelsaccordion
- Modal confirmation dialogsalert-dialog
- User profile imagesavatar
- Status badgesbadge
- Interactive buttonsbutton
- Content containerscard
- Form checkboxescheckbox
- Modal dialogsdialog
- Dropdown menusdropdown-menu
- Text inputsinput
- Form labelslabel
- Floating contentpopover
- Progress indicatorsprogress
- Radio buttonsradio-group
- Select dropdownsselect
- Toggle switchesswitch
- Tabbed interfacestabs
- Notification toaststoast
- Hover tooltipstooltip
Install new components:
npx shadcn@latest add [component-name]
Error Handling Patterns
API Errors
try { const res = await fetch("/api/..."); const data = await res.json(); if (!res.ok) { throw new Error(data.error || "Something went wrong"); } return data; } catch (error) { console.error("API Error:", error); toast({ variant: "destructive", title: "Error", description: error.message, }); return null; }
Supabase Errors
const { data, error } = await supabase.from("contacts").select("*"); if (error) { console.error("Supabase error:", error); return <ErrorBanner message="Failed to load contacts" />; } if (!data || data.length === 0) { return <EmptyState message="No contacts found" />; } return <ContactsList contacts={data} />;
Version 1 Constraints
What We Fix for V1:
- ✅ Workspace filtering on ALL pages
- ✅ Hot Leads button functionality
- ✅ Contact detail page navigation
- ✅ Dashboard stat cards
- ✅ Loading and error states
- ✅ Responsive design fixes
What We Do NOT Build for V1:
- ❌ Advanced animations
- ❌ Custom theme builder
- ❌ Drag-and-drop interfaces
- ❌ Real-time collaboration UI
- ❌ Mobile app
Key Points
- Always filter by workspace - Data isolation is critical
- Use shadcn/ui components - Don't reinvent the wheel
- Follow Tailwind conventions - Utility-first approach
- Handle loading/error states - Never show blank screens
- Test responsive design - Mobile, tablet, desktop
- Maintain accessibility - ARIA labels, keyboard navigation
Integration with Other Agents
The Frontend Agent works with:
- Backend Agent - Consumes API endpoints
- Docs Agent - Updates component documentation
- Orchestrator - Receives UI fix requests