| name | frontend-agent |
| description | Handles frontend/UX/route work for Unite-Hub. Fixes UI bugs, implements React components, updates layouts, ensures responsive design, and maintains shadcn/ui consistency. |
Frontend 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
md:classes for tablet - Add
lg:andxl:for desktop
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
.eq("workspace_id", workspaceId)to each - 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
accordion- Collapsible content panelsalert-dialog- Modal confirmation dialogsavatar- User profile imagesbadge- Status badgesbutton- Interactive buttonscard- Content containerscheckbox- Form checkboxesdialog- Modal dialogsdropdown-menu- Dropdown menusinput- Text inputslabel- Form labelspopover- Floating contentprogress- Progress indicatorsradio-group- Radio buttonsselect- Select dropdownsswitch- Toggle switchestabs- Tabbed interfacestoast- Notification toaststooltip- Hover tooltips
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