DocsSection 10
10Section 10 of 10

Common Patterns

Common Patterns You'll See

10.1 Loading States

export default function DataPage() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  
  useEffect(() => {
    fetchData()
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);
  
  if (loading) return <Loading />;
  if (error) return <Error message={error} />;
  return <DataView data={data} />;
}

10.2 Form Handling

export default function CreateForm() {
  const [formData, setFormData] = useState({ name: '', email: '' });
  const [submitting, setSubmitting] = useState(false);
  
  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault();
    setSubmitting(true);
    
    try {
      await fetch('/api/clients', {
        method: 'POST',
        body: JSON.stringify(formData),
      });
      // Success - redirect or show message
    } catch (error) {
      // Show error
    } finally {
      setSubmitting(false);
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <Input 
        value={formData.name}
        onChange={e => setFormData({ ...formData, name: e.target.value })}
      />
      <Button type="submit" loading={submitting}>
        Create
      </Button>
    </form>
  );
}

10.3 Optimistic Updates

// Update UI immediately, rollback on error
const handleDelete = async (id: string) => {
  // Optimistic: remove from UI
  setLeads(leads.filter(l => l.id !== id));
  
  try {
    await fetch(`/api/sales/leads/${id}`, { method: 'DELETE' });
  } catch (error) {
    // Rollback: add back to UI
    setLeads(originalLeads);
    showError('Failed to delete');
  }
};

10.4 Debounced Search

const [search, setSearch] = useState('');
const [results, setResults] = useState([]);

useEffect(() => {
  const timer = setTimeout(() => {
    if (search) {
      fetch(`/api/search?q=${search}`)
        .then(r => r.json())
        .then(setResults);
    }
  }, 300);  // Wait 300ms after typing stops
  
  return () => clearTimeout(timer);
}, [search]);

Summary

You Now Understand:

Stack: Next.js 16 + TypeScript + Tailwind + SQLite
Structure: How files map to routes
Data Flow: Client vs server rendering
Auth: Session-based with role checks
Database: SQLite with parameterized queries
Frontend: Component hierarchy + Tailwind
APIs: RESTful routes with middleware
Security: Defense in depth
Patterns: Loading states, forms, optimistic updates

Key Takeaways:

  1. Next.js App Router = file-system routing
  2. Server components = faster initial load, better SEO
  3. Client components = interactivity, real-time updates
  4. Database queries = always parameterized
  5. Auth = middleware + session cookies + role checks
  6. Components = reusable UI primitives
  7. APIs = RESTful with proper error handling

Next Steps:

  1. Read lib/db/index.ts to understand database patterns
  2. Look at app/api/clients/route.ts for API patterns
  3. Study app/components/ui/ for component patterns
  4. Review middleware.ts for auth flow