Sofondo Framework - Example Applications
Complete examples demonstrating how to build real-world admin panels with the Sofondo Framework.
Table of Contents
E-commerce Admin Panel
A complete e-commerce admin panel for managing products, orders, customers, and inventory.
Overview
Features:
- Product management (CRUD operations)
- Order tracking and fulfillment
- Customer management
- Inventory monitoring
- Sales analytics dashboard
- Real-time notifications
Menu Structure
// app/(admin)/layout.tsx
import { AdminLayout } from '@sofondo/next';
import {
LayoutDashboard,
Package,
ShoppingCart,
Users,
Warehouse,
BarChart3,
Settings,
Tag,
} from 'lucide-react';
const menuItems = [
{
icon: LayoutDashboard,
label: 'Dashboard',
href: '/dashboard',
exact: true,
},
{
icon: Package,
label: 'Products',
href: '/products',
},
{
icon: ShoppingCart,
label: 'Orders',
href: '/orders',
},
{
icon: Users,
label: 'Customers',
href: '/customers',
},
{
icon: Warehouse,
label: 'Inventory',
href: '/inventory',
},
{
icon: Tag,
label: 'Categories',
href: '/categories',
},
{
icon: BarChart3,
label: 'Analytics',
href: '/analytics',
},
{
icon: Settings,
label: 'Settings',
href: '/settings',
},
];
export default function EcommerceLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<AdminLayout
menuItems={menuItems}
user={{
name: 'Store Admin',
email: 'admin@store.com',
}}
>
{children}
</AdminLayout>
);
}
Dashboard Page
// app/(admin)/dashboard/page.tsx
import {
PageHeader,
StatCard,
StatGrid,
DataGrid,
ProgressBar,
} from '@sofondo/react';
import {
DollarSign,
ShoppingCart,
Users,
TrendingUp,
Package,
AlertCircle,
} from 'lucide-react';
import { getStorageStatus, getStorageColor } from '@sofondo/core';
// Mock data
const recentOrders = [
{
id: 'ORD-001',
customer: 'Alice Johnson',
amount: '$234.50',
status: 'Shipped',
date: '2025-12-01',
},
{
id: 'ORD-002',
customer: 'Bob Smith',
amount: '$156.00',
status: 'Processing',
date: '2025-12-02',
},
{
id: 'ORD-003',
customer: 'Carol White',
amount: '$89.99',
status: 'Delivered',
date: '2025-12-03',
},
];
const lowStockProducts = [
{ id: 1, name: 'Wireless Mouse', stock: 5, reorderPoint: 20 },
{ id: 2, name: 'USB Cable', stock: 8, reorderPoint: 50 },
{ id: 3, name: 'Laptop Stand', stock: 3, reorderPoint: 15 },
];
export default function EcommerceDashboard() {
return (
<>
<PageHeader
title="E-commerce Dashboard"
subtitle="Overview of your store performance"
/>
{/* Key Metrics */}
<StatGrid>
<StatCard
icon={DollarSign}
label="Total Revenue"
value="$45,234"
change="+12% from last month"
changeClassName="positive"
/>
<StatCard
icon={ShoppingCart}
label="Total Orders"
value="1,234"
change="+8% from last month"
changeClassName="positive"
/>
<StatCard
icon={Users}
label="Active Customers"
value="567"
change="+15% from last month"
changeClassName="positive"
/>
<StatCard
icon={TrendingUp}
label="Conversion Rate"
value="3.2%"
change="+0.5% from last month"
changeClassName="positive"
/>
</StatGrid>
<div style={{ marginTop: '30px' }}>
<h3>Recent Orders</h3>
<DataGrid
data={recentOrders}
keyField="id"
columns={[
{ key: 'id', header: 'Order ID', width: '1fr' },
{ key: 'customer', header: 'Customer', width: '2fr' },
{ key: 'amount', header: 'Amount', width: '1fr' },
{ key: 'status', header: 'Status', width: '1fr' },
{ key: 'date', header: 'Date', width: '1fr' },
]}
/>
</div>
<div style={{ marginTop: '30px' }}>
<h3>
<AlertCircle size={20} style={{ verticalAlign: 'middle' }} />
{' '}Low Stock Alert
</h3>
<DataGrid
data={lowStockProducts}
keyField="id"
columns={[
{ key: 'name', header: 'Product', width: '2fr' },
{ key: 'stock', header: 'Current Stock', width: '1fr' },
{ key: 'reorderPoint', header: 'Reorder Point', width: '1fr' },
{
key: 'status',
header: 'Status',
width: '2fr',
render: (item) => {
const status = getStorageStatus(item.stock, item.reorderPoint, true);
const color = getStorageColor(status);
return (
<div>
<ProgressBar
value={item.stock}
max={item.reorderPoint}
color={color}
height={8}
/>
</div>
);
},
},
]}
/>
</div>
</>
);
}
Products Page
// app/(admin)/products/page.tsx
'use client';
import { useState } from 'react';
import { PageHeader, DataGrid, Skeleton } from '@sofondo/react';
import { useToast } from '@sofondo/react';
import { Plus, Edit, Trash2, Image } from 'lucide-react';
interface Product {
id: number;
name: string;
category: string;
price: string;
stock: number;
status: 'Active' | 'Draft' | 'Out of Stock';
}
const mockProducts: Product[] = [
{ id: 1, name: 'Wireless Mouse', category: 'Electronics', price: '$29.99', stock: 45, status: 'Active' },
{ id: 2, name: 'USB Cable', category: 'Accessories', price: '$9.99', stock: 120, status: 'Active' },
{ id: 3, name: 'Laptop Stand', category: 'Furniture', price: '$49.99', stock: 0, status: 'Out of Stock' },
{ id: 4, name: 'Keyboard', category: 'Electronics', price: '$79.99', stock: 30, status: 'Active' },
{ id: 5, name: 'Monitor Arm', category: 'Furniture', price: '$129.99', stock: 15, status: 'Active' },
];
export default function ProductsPage() {
const [products, setProducts] = useState<Product[]>(mockProducts);
const [loading, setLoading] = useState(false);
const { addToast } = useToast();
const handleEdit = (product: Product) => {
addToast(`Editing ${product.name}`, 'info');
};
const handleDelete = (product: Product) => {
setProducts(products.filter(p => p.id !== product.id));
addToast(`${product.name} deleted`, 'success');
};
const handleAddProduct = () => {
addToast('Add product form would open here', 'info');
};
return (
<>
<PageHeader
title="Products"
subtitle="Manage your product catalog"
actions={
<button
onClick={handleAddProduct}
style={{
padding: '8px 16px',
backgroundColor: 'var(--primary)',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
>
<Plus size={16} />
Add Product
</button>
}
/>
{loading ? (
<>
<Skeleton width="100%" height={50} />
<Skeleton width="100%" height={50} />
<Skeleton width="100%" height={50} />
</>
) : (
<DataGrid
data={products}
keyField="id"
columns={[
{
key: 'name',
header: 'Product Name',
width: '2fr',
render: (item) => (
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<Image size={20} />
{item.name}
</div>
),
},
{ key: 'category', header: 'Category', width: '1fr' },
{ key: 'price', header: 'Price', width: '1fr' },
{
key: 'stock',
header: 'Stock',
width: '1fr',
render: (item) => (
<span style={{ color: item.stock === 0 ? 'red' : 'inherit' }}>
{item.stock}
</span>
),
},
{
key: 'status',
header: 'Status',
width: '1fr',
render: (item) => (
<span
style={{
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
backgroundColor:
item.status === 'Active'
? '#e6f7ed'
: item.status === 'Out of Stock'
? '#ffe6e6'
: '#f0f0f0',
color:
item.status === 'Active'
? '#0f9d58'
: item.status === 'Out of Stock'
? '#ea4335'
: '#666',
}}
>
{item.status}
</span>
),
},
{
key: 'actions',
header: 'Actions',
width: '1fr',
render: (item) => (
<div style={{ display: 'flex', gap: '8px' }}>
<button
onClick={() => handleEdit(item)}
style={{
padding: '4px 8px',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
background: 'white',
}}
>
<Edit size={14} />
</button>
<button
onClick={() => handleDelete(item)}
style={{
padding: '4px 8px',
border: '1px solid #ea4335',
borderRadius: '4px',
cursor: 'pointer',
background: 'white',
color: '#ea4335',
}}
>
<Trash2 size={14} />
</button>
</div>
),
},
]}
/>
)}
</>
);
}
Orders Page
// app/(admin)/orders/page.tsx
'use client';
import { useState } from 'react';
import { PageHeader, DataGrid } from '@sofondo/react';
import { Filter, Download } from 'lucide-react';
interface Order {
id: string;
customer: string;
email: string;
amount: string;
status: 'Processing' | 'Shipped' | 'Delivered' | 'Cancelled';
date: string;
}
const mockOrders: Order[] = [
{ id: 'ORD-001', customer: 'Alice Johnson', email: 'alice@email.com', amount: '$234.50', status: 'Shipped', date: '2025-12-01' },
{ id: 'ORD-002', customer: 'Bob Smith', email: 'bob@email.com', amount: '$156.00', status: 'Processing', date: '2025-12-02' },
{ id: 'ORD-003', customer: 'Carol White', email: 'carol@email.com', amount: '$89.99', status: 'Delivered', date: '2025-12-03' },
{ id: 'ORD-004', customer: 'David Brown', email: 'david@email.com', amount: '$445.00', status: 'Processing', date: '2025-12-03' },
{ id: 'ORD-005', customer: 'Eve Davis', email: 'eve@email.com', amount: '$299.99', status: 'Shipped', date: '2025-12-04' },
];
export default function OrdersPage() {
const [orders] = useState<Order[]>(mockOrders);
const [filter, setFilter] = useState<string>('All');
const filteredOrders = filter === 'All'
? orders
: orders.filter(order => order.status === filter);
const handleExport = () => {
console.log('Exporting orders...');
};
return (
<>
<PageHeader
title="Orders"
subtitle={`${filteredOrders.length} orders`}
actions={
<div style={{ display: 'flex', gap: '10px' }}>
<select
value={filter}
onChange={(e) => setFilter(e.target.value)}
style={{
padding: '8px 16px',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
}}
>
<option value="All">All Orders</option>
<option value="Processing">Processing</option>
<option value="Shipped">Shipped</option>
<option value="Delivered">Delivered</option>
<option value="Cancelled">Cancelled</option>
</select>
<button
onClick={handleExport}
style={{
padding: '8px 16px',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
>
<Download size={16} />
Export
</button>
</div>
}
/>
<DataGrid
data={filteredOrders}
keyField="id"
columns={[
{ key: 'id', header: 'Order ID', width: '1fr' },
{ key: 'customer', header: 'Customer', width: '2fr' },
{ key: 'email', header: 'Email', width: '2fr' },
{ key: 'amount', header: 'Amount', width: '1fr' },
{
key: 'status',
header: 'Status',
width: '1fr',
render: (item) => {
const statusColors = {
Processing: { bg: '#fff3cd', color: '#856404' },
Shipped: { bg: '#d1ecf1', color: '#0c5460' },
Delivered: { bg: '#d4edda', color: '#155724' },
Cancelled: { bg: '#f8d7da', color: '#721c24' },
};
const colors = statusColors[item.status];
return (
<span
style={{
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
backgroundColor: colors.bg,
color: colors.color,
}}
>
{item.status}
</span>
);
},
},
{ key: 'date', header: 'Date', width: '1fr' },
]}
/>
</>
);
}
Educational Platform Dashboard
A comprehensive educational platform for managing courses, students, assignments, and analytics.
Overview
Features:
- Course management
- Student enrollment tracking
- Assignment submission and grading
- Progress monitoring
- Analytics and reporting
- Communication tools
Menu Structure
// app/(admin)/layout.tsx
import { AdminLayout } from '@sofondo/next';
import {
LayoutDashboard,
BookOpen,
Users,
ClipboardList,
BarChart3,
MessageSquare,
Calendar,
Settings,
Award,
} from 'lucide-react';
const menuItems = [
{
icon: LayoutDashboard,
label: 'Dashboard',
href: '/dashboard',
exact: true,
},
{
icon: BookOpen,
label: 'Courses',
href: '/courses',
},
{
icon: Users,
label: 'Students',
href: '/students',
},
{
icon: ClipboardList,
label: 'Assignments',
href: '/assignments',
},
{
icon: Award,
label: 'Grades',
href: '/grades',
},
{
icon: Calendar,
label: 'Schedule',
href: '/schedule',
},
{
icon: MessageSquare,
label: 'Messages',
href: '/messages',
},
{
icon: BarChart3,
label: 'Analytics',
href: '/analytics',
},
{
icon: Settings,
label: 'Settings',
href: '/settings',
},
];
export default function EducationLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<AdminLayout
menuItems={menuItems}
user={{
name: 'Prof. Smith',
email: 'smith@university.edu',
}}
>
{children}
</AdminLayout>
);
}
Dashboard Page
// app/(admin)/dashboard/page.tsx
import {
PageHeader,
StatCard,
StatGrid,
DataGrid,
ProgressBar,
} from '@sofondo/react';
import {
BookOpen,
Users,
ClipboardList,
TrendingUp,
Award,
Clock,
} from 'lucide-react';
import { getStorageStatus, getStorageColor } from '@sofondo/core';
const upcomingAssignments = [
{
id: 1,
course: 'Introduction to React',
assignment: 'Build a Todo App',
dueDate: '2025-12-10',
submissions: 23,
totalStudents: 30,
},
{
id: 2,
course: 'Advanced TypeScript',
assignment: 'Type System Project',
dueDate: '2025-12-12',
submissions: 18,
totalStudents: 25,
},
{
id: 3,
course: 'Web Design Principles',
assignment: 'Portfolio Website',
dueDate: '2025-12-15',
submissions: 12,
totalStudents: 28,
},
];
const recentActivity = [
{ id: 1, student: 'Alice Johnson', action: 'Submitted assignment', course: 'React Basics', time: '5 min ago' },
{ id: 2, student: 'Bob Smith', action: 'Completed course', course: 'TypeScript', time: '15 min ago' },
{ id: 3, student: 'Carol White', action: 'Asked question', course: 'Web Design', time: '1 hour ago' },
];
export default function EducationDashboard() {
return (
<>
<PageHeader
title="Education Dashboard"
subtitle="Overview of your courses and students"
/>
{/* Key Metrics */}
<StatGrid>
<StatCard
icon={BookOpen}
label="Active Courses"
value="12"
change="2 new this semester"
changeClassName="positive"
/>
<StatCard
icon={Users}
label="Total Students"
value="234"
change="+18 from last month"
changeClassName="positive"
/>
<StatCard
icon={ClipboardList}
label="Pending Assignments"
value="45"
change="15 due this week"
changeClassName="warning"
/>
<StatCard
icon={TrendingUp}
label="Completion Rate"
value="87%"
change="+3% from last semester"
changeClassName="positive"
/>
</StatGrid>
<div style={{ marginTop: '30px' }}>
<h3>
<Clock size={20} style={{ verticalAlign: 'middle' }} />
{' '}Upcoming Assignments
</h3>
<DataGrid
data={upcomingAssignments}
keyField="id"
columns={[
{ key: 'course', header: 'Course', width: '2fr' },
{ key: 'assignment', header: 'Assignment', width: '2fr' },
{ key: 'dueDate', header: 'Due Date', width: '1fr' },
{
key: 'progress',
header: 'Submissions',
width: '2fr',
render: (item) => {
const percentage = (item.submissions / item.totalStudents) * 100;
const status = getStorageStatus(percentage, 100, true);
const color = getStorageColor(status);
return (
<div>
<div>{item.submissions} / {item.totalStudents}</div>
<ProgressBar
value={item.submissions}
max={item.totalStudents}
color={color}
height={6}
/>
</div>
);
},
},
]}
/>
</div>
<div style={{ marginTop: '30px' }}>
<h3>Recent Activity</h3>
<DataGrid
data={recentActivity}
keyField="id"
columns={[
{ key: 'student', header: 'Student', width: '2fr' },
{ key: 'action', header: 'Action', width: '2fr' },
{ key: 'course', header: 'Course', width: '2fr' },
{ key: 'time', header: 'Time', width: '1fr' },
]}
/>
</div>
</>
);
}
Courses Page
// app/(admin)/courses/page.tsx
'use client';
import { useState } from 'react';
import { PageHeader, DataGrid } from '@sofondo/react';
import { useToast } from '@sofondo/react';
import { Plus, Edit, Users, BarChart } from 'lucide-react';
interface Course {
id: number;
title: string;
instructor: string;
students: number;
completion: number;
status: 'Active' | 'Archived' | 'Draft';
}
const mockCourses: Course[] = [
{ id: 1, title: 'Introduction to React', instructor: 'Prof. Smith', students: 30, completion: 75, status: 'Active' },
{ id: 2, title: 'Advanced TypeScript', instructor: 'Dr. Johnson', students: 25, completion: 60, status: 'Active' },
{ id: 3, title: 'Web Design Principles', instructor: 'Prof. Brown', students: 28, completion: 85, status: 'Active' },
{ id: 4, title: 'Database Systems', instructor: 'Dr. Davis', students: 22, completion: 45, status: 'Active' },
{ id: 5, title: 'Python for Beginners', instructor: 'Prof. Wilson', students: 35, completion: 90, status: 'Active' },
];
export default function CoursesPage() {
const [courses] = useState<Course[]>(mockCourses);
const { addToast } = useToast();
const handleEdit = (course: Course) => {
addToast(`Editing ${course.title}`, 'info');
};
const handleViewStudents = (course: Course) => {
addToast(`Viewing students for ${course.title}`, 'info');
};
const handleViewAnalytics = (course: Course) => {
addToast(`Viewing analytics for ${course.title}`, 'info');
};
return (
<>
<PageHeader
title="Courses"
subtitle="Manage your courses"
actions={
<button
onClick={() => addToast('Create course form would open', 'info')}
style={{
padding: '8px 16px',
backgroundColor: 'var(--primary)',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
>
<Plus size={16} />
Create Course
</button>
}
/>
<DataGrid
data={courses}
keyField="id"
columns={[
{ key: 'title', header: 'Course Title', width: '3fr' },
{ key: 'instructor', header: 'Instructor', width: '2fr' },
{
key: 'students',
header: 'Students',
width: '1fr',
render: (item) => (
<span style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
<Users size={14} />
{item.students}
</span>
),
},
{
key: 'completion',
header: 'Avg. Completion',
width: '1fr',
render: (item) => `${item.completion}%`,
},
{
key: 'status',
header: 'Status',
width: '1fr',
render: (item) => (
<span
style={{
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
backgroundColor: item.status === 'Active' ? '#e6f7ed' : '#f0f0f0',
color: item.status === 'Active' ? '#0f9d58' : '#666',
}}
>
{item.status}
</span>
),
},
{
key: 'actions',
header: 'Actions',
width: '2fr',
render: (item) => (
<div style={{ display: 'flex', gap: '8px' }}>
<button
onClick={() => handleEdit(item)}
style={{
padding: '4px 8px',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
background: 'white',
}}
>
<Edit size={14} />
</button>
<button
onClick={() => handleViewStudents(item)}
style={{
padding: '4px 8px',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
background: 'white',
}}
>
<Users size={14} />
</button>
<button
onClick={() => handleViewAnalytics(item)}
style={{
padding: '4px 8px',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
background: 'white',
}}
>
<BarChart size={14} />
</button>
</div>
),
},
]}
/>
</>
);
}
Students Page
// app/(admin)/students/page.tsx
'use client';
import { useState } from 'react';
import { PageHeader, DataGrid, ProgressBar } from '@sofondo/react';
import { Search, Mail } from 'lucide-react';
import { getStorageStatus, getStorageColor } from '@sofondo/core';
interface Student {
id: number;
name: string;
email: string;
enrolledCourses: number;
completedCourses: number;
overallProgress: number;
lastActive: string;
}
const mockStudents: Student[] = [
{ id: 1, name: 'Alice Johnson', email: 'alice@university.edu', enrolledCourses: 4, completedCourses: 2, overallProgress: 78, lastActive: '2 hours ago' },
{ id: 2, name: 'Bob Smith', email: 'bob@university.edu', enrolledCourses: 3, completedCourses: 1, overallProgress: 65, lastActive: '1 day ago' },
{ id: 3, name: 'Carol White', email: 'carol@university.edu', enrolledCourses: 5, completedCourses: 3, overallProgress: 92, lastActive: '30 min ago' },
{ id: 4, name: 'David Brown', email: 'david@university.edu', enrolledCourses: 3, completedCourses: 0, overallProgress: 45, lastActive: '3 days ago' },
{ id: 5, name: 'Eve Davis', email: 'eve@university.edu', enrolledCourses: 4, completedCourses: 2, overallProgress: 88, lastActive: '1 hour ago' },
];
export default function StudentsPage() {
const [students] = useState<Student[]>(mockStudents);
const [searchTerm, setSearchTerm] = useState('');
const filteredStudents = students.filter(student =>
student.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
student.email.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<>
<PageHeader
title="Students"
subtitle={`${filteredStudents.length} students enrolled`}
actions={
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Search size={16} />
<input
type="text"
placeholder="Search students..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
style={{
padding: '8px 12px',
border: '1px solid #ccc',
borderRadius: '4px',
width: '250px',
}}
/>
</div>
}
/>
<DataGrid
data={filteredStudents}
keyField="id"
columns={[
{ key: 'name', header: 'Student Name', width: '2fr' },
{
key: 'email',
header: 'Email',
width: '2fr',
render: (item) => (
<a
href={`mailto:${item.email}`}
style={{ color: 'var(--primary)', textDecoration: 'none' }}
>
{item.email}
</a>
),
},
{
key: 'courses',
header: 'Courses',
width: '1fr',
render: (item) => `${item.completedCourses}/${item.enrolledCourses}`,
},
{
key: 'progress',
header: 'Overall Progress',
width: '2fr',
render: (item) => {
const status = getStorageStatus(item.overallProgress, 100, true);
const color = getStorageColor(status);
return (
<div>
<div style={{ marginBottom: '4px' }}>{item.overallProgress}%</div>
<ProgressBar
value={item.overallProgress}
max={100}
color={color}
height={6}
/>
</div>
);
},
},
{ key: 'lastActive', header: 'Last Active', width: '1fr' },
]}
/>
</>
);
}
Common Patterns
Pattern 1: Data Fetching with Loading States
'use client';
import { useState, useEffect } from 'react';
import { DataGrid, Skeleton } from '@sofondo/react';
export function DataWithLoading() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData().then(result => {
setData(result);
setLoading(false);
});
}, []);
if (loading) {
return (
<>
<Skeleton width="100%" height={50} />
<Skeleton width="100%" height={50} />
<Skeleton width="100%" height={50} />
</>
);
}
return (
<DataGrid
data={data}
keyField="id"
columns={[/* ... */]}
/>
);
}
Pattern 2: Form with Toast Notifications
'use client';
import { useState } from 'react';
import { useToast } from '@sofondo/react';
export function FormWithToast() {
const [formData, setFormData] = useState({ name: '', email: '' });
const { addToast } = useToast();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
await saveData(formData);
addToast('Data saved successfully!', 'success');
setFormData({ name: '', email: '' });
} catch (error) {
addToast('Failed to save data', 'error');
}
};
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
<button type="submit">Save</button>
</form>
);
}
Pattern 3: Status Indicators with Progress
import { ProgressBar } from '@sofondo/react';
import { getStorageStatus, getStorageColor } from '@sofondo/core';
export function StatusIndicator({ current, max }: { current: number; max: number }) {
const status = getStorageStatus(current, max, true);
const color = getStorageColor(status);
const percentage = (current / max) * 100;
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>
<span>{current} / {max}</span>
<span>{percentage.toFixed(0)}%</span>
</div>
<ProgressBar value={current} max={max} color={color} height={8} />
</div>
);
}
Pattern 4: Responsive Actions
'use client';
import { useMediaQuery } from '@sofondo/react';
import { MoreVertical } from 'lucide-react';
export function ResponsiveActions() {
const isMobile = useMediaQuery('(max-width: 768px)');
if (isMobile) {
return (
<button>
<MoreVertical size={16} />
</button>
);
}
return (
<div style={{ display: 'flex', gap: '8px' }}>
<button>Edit</button>
<button>Delete</button>
<button>Share</button>
</div>
);
}
Integration Examples
With React Query
'use client';
import { useQuery } from '@tanstack/react-query';
import { DataGrid, Skeleton } from '@sofondo/react';
export function ProductList() {
const { data, isLoading, error } = useQuery({
queryKey: ['products'],
queryFn: fetchProducts,
});
if (isLoading) {
return (
<>
<Skeleton width="100%" height={50} />
<Skeleton width="100%" height={50} />
</>
);
}
if (error) {
return <div>Error loading products</div>;
}
return (
<DataGrid
data={data}
keyField="id"
columns={[/* ... */]}
/>
);
}
With Form Libraries (React Hook Form)
'use client';
import { useForm } from 'react-hook-form';
import { useToast } from '@sofondo/react';
export function ProductForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const { addToast } = useToast();
const onSubmit = async (data) => {
try {
await saveProduct(data);
addToast('Product saved!', 'success');
} catch (error) {
addToast('Failed to save product', 'error');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name', { required: true })} />
{errors.name && <span>Name is required</span>}
<button type="submit">Save</button>
</form>
);
}
With State Management (Zustand)
'use client';
import { create } from 'zustand';
import { useToast } from '@sofondo/react';
const useStore = create((set) => ({
products: [],
addProduct: (product) => set((state) => ({
products: [...state.products, product]
})),
}));
export function ProductManager() {
const { products, addProduct } = useStore();
const { addToast } = useToast();
const handleAdd = () => {
addProduct({ id: Date.now(), name: 'New Product' });
addToast('Product added!', 'success');
};
return (
<div>
<button onClick={handleAdd}>Add Product</button>
{/* Render products */}
</div>
);
}
Summary
These examples demonstrate:
✅ E-commerce Admin - Complete product, order, and customer management ✅ Educational Platform - Course, student, and assignment tracking ✅ Common Patterns - Reusable patterns for loading, forms, status, and responsive design ✅ Integrations - Working with popular React libraries
All examples use Sofondo Framework components with best practices for building production-ready admin panels.
Next Steps
- Copy and customize these examples for your use case
- Refer to the API Reference for complete component documentation
- Follow the Migration Strategy for integrating into existing projects
- Check the Configuration Guide for customization options