Build an interactive employee directory with searchable profiles, org chart visualization, and department management
* Preview is for reference only. Actual results may vary depending on the AI model, variable values, and tools used.
You are an expert full-stack developer building a Company Directory & Org Chart web application for a Malaysian SME. The goal is to replace scattered WhatsApp contact lists and paper org charts with a professional, searchable digital directory and interactive organization chart.
## Role & Context
- Client: Malaysian Chinese SME owner with {{employee_count}} staff across {{office_locations}}
- Company: {{company_name}}
- Departments: {{departments}}
- Users: All staff — they look up colleagues daily, managers view team structures
- Pain point: No central place for contacts, org chart is a static PowerPoint nobody updates
- Success metric: Staff can find any colleague in under 10 seconds
## Tech Stack
- Next.js 14 (App Router) + TypeScript
- Tailwind CSS + shadcn/ui (new-york style, slate base, yellow #FCD34D accent)
- Supabase (Postgres + Auth + RLS + Storage for profile photos)
- react-organizational-chart for org tree (or D3.js for advanced layouts)
- next/image for optimized profile photos
- react-to-print or jsPDF for PDF export
- vcf package for vCard export
## Database Schema
Create the following Supabase tables:
sql
-- Departments table
CREATE TABLE departments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
name text NOT NULL,
name_zh text,
description text,
color text DEFAULT '#FCD34D',
head_id uuid REFERENCES employees(id) ON DELETE SET NULL,
parent_department_id uuid REFERENCES departments(id) ON DELETE SET NULL,
created_at timestamptz DEFAULT now()
);
CREATE INDEX idx_departments_head ON departments(head_id);
CREATE INDEX idx_departments_parent ON departments(parent_department_id);
-- Office locations table
CREATE TABLE office_locations (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
name text NOT NULL,
address text,
city text,
state text,
is_headquarters boolean DEFAULT false
);
-- Employees table
CREATE TABLE employees (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
full_name text NOT NULL,
preferred_name text,
position text NOT NULL,
department_id uuid REFERENCES departments(id) ON DELETE SET NULL,
office_location_id uuid REFERENCES office_locations(id) ON DELETE SET NULL,
email text UNIQUE NOT NULL,
phone text,
photo_url text,
reports_to uuid REFERENCES employees(id) ON DELETE SET NULL,
hire_date date,
birthday date,
bio text,
skills text[] DEFAULT '{}',
status text DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'on_leave')),
is_new_hire boolean DEFAULT false,
linkedin_url text,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now()
);
CREATE INDEX idx_employees_department ON employees(department_id);
CREATE INDEX idx_employees_reports_to ON employees(reports_to);
CREATE INDEX idx_employees_status ON employees(status);
CREATE INDEX idx_employees_office ON employees(office_location_id);
CREATE INDEX idx_employees_hire_date ON employees(hire_date);
CREATE INDEX idx_employees_search ON employees USING gin(
to_tsvector('simple', coalesce(full_name,'') || ' ' || coalesce(position,'') || ' ' || coalesce(bio,''))
);
Enable RLS: authenticated users can SELECT all active employees; only admin role can INSERT/UPDATE/DELETE.
## File Structure
src/app/
page.tsx # Redirect to /directory
directory/
page.tsx # Main directory grid with search + filters
[id]/page.tsx # Employee profile detail page
org-chart/
page.tsx # Interactive org chart tree
departments/
[id]/page.tsx # Department page with all members
admin/
employees/page.tsx # CRUD employee management
employees/[id]/edit/page.tsx
src/components/
employee-card.tsx # Profile card for grid view
employee-profile-modal.tsx # Quick-view modal on card click
org-chart-tree.tsx # react-organizational-chart wrapper
org-chart-node.tsx # Individual node component
department-badge.tsx # Colored pill badge per department
directory-filters.tsx # Search bar + department + location tabs
birthday-banner.tsx # Upcoming birthdays/anniversaries strip
new-hire-highlight.tsx # New employee spotlight card
export-button.tsx # PDF + vCard export actions
stats-bar.tsx # Headcount stats across top
## Core Features to Build
### 1. Searchable Employee Directory
- Card grid layout: photo avatar, full name, preferred name in parentheses, position, department badge (colored pill), email (mailto link), phone (tel link, +60 format), office location
- Search input queries name, position, skills, bio using Postgres full-text search
- Filter tabs: All Departments + one tab per department
- Secondary filter: office location dropdown
- Sort options: Name A-Z, Department, Newest First, Work Anniversary Soon
- Show total count: "Showing 38 of 45 employees"
- Highlight new hires with a subtle yellow badge "New" for first 90 days
### 2. Interactive Org Chart
- Tree visualization starting from CEO/MD at root
- Each node shows: photo thumbnail, name, position, department color indicator
- Expand/collapse branches by clicking department head nodes
- Zoom in/out and pan (use transform-origin CSS or D3 zoom)
- Color-code nodes by department using department.color
- Click any node to open employee profile modal
- Toggle between horizontal and vertical tree layouts
- Highlight reporting chain when hovering an employee
### 3. Employee Profile Page
- Full-width header: large photo, name, position, department, status badge
- Contact section: email button, phone button, LinkedIn link
- About: bio paragraph, skills as pill tags
- Reports to: mini card linking to manager's profile
- Direct reports: horizontal scroll of mini cards for team members
- Work info: hire date, work anniversary countdown, office location with map link
- Activity: birthday (month/day only, no year), languages spoken
### 4. Department Pages
- Department name, description, head's profile card
- All members grid (same employee cards)
- Sub-departments listed if any
- Department headcount and average tenure
### 5. Birthday & Anniversary Reminders
- Sticky banner at top of directory: "This week: 2 birthdays, 1 work anniversary"
- Show within 7-day rolling window
- Click to jump to that employee's card
- Admin can toggle reminders on/off
### 6. New Employee Onboarding Highlight
- Dedicated "Welcome" section above the main grid for staff hired in last 30 days
- Shows 1-3 new hire cards with a yellow highlight border
- Disappears automatically after 30 days
### 7. Export Options
- Export current filtered view as PDF: formatted table with photo, name, position, email, phone
- Export individual employee as vCard (.vcf) file for phone import
- Export full directory as CSV for HR use
## UI/UX Design
- Top nav: dark navy (#0F172A) with logo left, search bar center, profile icon right
- Stats bar below nav: Total Employees, Departments, New This Month, Upcoming Anniversaries — all as pill-style stat cards
- Directory tab and Org Chart tab as main navigation
- White card backgrounds, subtle slate border (#E2E8F0), 8px border radius
- Yellow (#FCD34D) used only for: new hire badge, CTA buttons, department head crown icon, active filter pill
- Avatar: use initials fallback with department color background when no photo uploaded
- Mobile: single column card stack, sticky search bar, bottom sheet for filters
- Loading: skeleton cards while data fetches
## Business Rules
- reports_to column drives the org chart hierarchy — every employee except the CEO has a manager
- Department head (departments.head_id) gets a crown icon on their card
- Multi-location: filter by office shows only that location's staff
- Status filter: default shows only active employees; admin can view all including inactive
- Phone numbers stored in +60 format (Malaysian), displayed as 012-345 6789
- Photos stored in Supabase Storage bucket employees-photos with RLS allowing authenticated reads
## Dashboard Stats Component
Display at the top of the directory page:
- Total headcount (active employees)
- Number of departments
- New hires this month (hire_date within current calendar month)
- Birthdays this week
- Work anniversaries this week (anniversary of hire_date)
- Fetch all five counts in a single parallel Promise.all call from a Server Component
## Admin Panel
- Table view of all employees with Edit / Deactivate / Delete actions
- Employee form: all fields, photo upload to Supabase Storage, department select, reports-to select (autocomplete)
- Bulk import via CSV upload (map columns to schema)
- Department management: create, rename, assign head, set color
Build the directory page and org chart first. Seed with realistic Malaysian employee names across the departments listed above. Make it production-ready with proper TypeScript types, error boundaries, loading states, and empty states.