ShipDesk
Task Manager
ShipDesk
Progress
06

UI Upgrade — shadcn/ui

Replace custom UI with healthcare-grade accessible components

Week 7–914 daysEstimated: 56h totalQuality Leap
Why this matters
This is where your app goes from 'migrated' to 'premium'. shadcn/ui gives you accessible, keyboard-navigable, beautifully animated components. Your doctors and patients will notice the quality difference immediately.
High-Priority Upgrades
Prescription tables → shadcn DataTable
2 days
All forms → shadcn Form + react-hook-form + zod
3 days
DigitalPrescription modal → shadcn Dialog
1 day
Custom toasts → Sonner <Toaster />
2 hrs
All <img> tags → next/image
1 day
Loading states → shadcn Skeleton
1 day
Mobile navigation → shadcn Sheet
2 hrs
Doctor search → shadcn Command + Popover
1 day
components/prescriptions/columns.jsx — DataTable columns
'use client'
import { ColumnDef } from '@tanstack/react-table'
import { Badge } from '@/components/ui/badge'

export const columns = [
  { accessorKey: 'patientName', header: 'Patient' },
  { accessorKey: 'medication', header: 'Medication' },
  { accessorKey: 'dosage', header: 'Dosage' },
  {
    accessorKey: 'status',
    header: 'Status',
    cell: ({ row }) => (
      <Badge variant={row.original.status === 'active' ? 'default' : 'secondary'}>
        {row.original.status}
      </Badge>
    ),
  },
]
Form migration pattern — useState → react-hook-form
// BEFORE: Manual useState for every field
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')

// AFTER: react-hook-form + zod
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Min 8 characters'),
})

const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: zodResolver(schema),
})
next/image — S3 URL replacement
// BEFORE: plain <img> tag
<img src={user.avatar} alt="Profile" className="w-12 h-12 rounded-full" />

// AFTER: next/image with S3 domain configured
import Image from 'next/image'

<Image
  src={user.avatar}
  alt="Profile"
  width={48}
  height={48}
  className="rounded-full"
/>

// Requires in next.config.js:
// images: { remotePatterns: [{ hostname: '*.amazonaws.com' }] }
Previous
Phase 05Dashboard Pages Migration
Next
Phase 07SEO, Performance & Accessibility