Part 8 • Pink Slips NSW

Building a Multi-Technician Calendar System for Pink Slips NSW

Pink Slips NSW

Building multi-technician calendar with drag-and-drop scheduling. 5 bugs and how we fixed them.

Dec 7, 2025
18 min

Building With AI?

Learn how to build features like this yourself. I offer 1-on-1 AI web development coaching to help you ship faster with tools like Claude, Cursor, and ChatGPT.

Pink Slips NSW started with one technician. But the business is expanding to six regions. Each technician needs their own calendar view without breaking the drag-and-drop scheduling.

From Single Technician to Multi-Region

Pink Slips NSW is expanding. One calendar showing all technicians creates chaos. We needed multi-technician support without breaking the drag-and-drop scheduling.

Guy Walker has been the sole technician for Pink Slips NSW, operating out of the Sutherland Shire. But the business is growing. New technicians are being onboarded in Gosford, Newcastle, Lismore, Tweed Heads, Dubbo, and Canberra. Each one needs their own calendar view, their own job management, their own schedule.

The existing single-calendar system showed all events overlapping on one view. With multiple technicians, it became completely unusable. You couldn't tell whose job was whose. The visual was a mess of overlapping colored blocks.

Download

Single Calendar Doesn't Scale

Problem

One calendar showing all technicians = overlapping events, confusion, unusable

Solution

Each technician needs their own filtered view while sharing the same system

JJM

The Expansion

Sydney
Sutherland Shire
Primary • 108 events
Gosford
Central Coast
2 events
Newcastle
Hunter Region
3 events
Tweed Heads
Gold Coast Border
4 events
Dubbo
Central West NSW
1 event
Canberra
ACT
2 events
Download

6 Regions, 1 System

  • Sydney - Sutherland Shire (Primary)

  • Gosford - Central Coast

  • Newcastle - Hunter Region

  • Tweed Heads - Gold Coast Border

  • Dubbo - Central West NSW

  • Canberra - ACT

JJM

The challenge wasn't just adding a filter dropdown. Each technician has different work hours, different home addresses (for travel time calculations), and different capacity limits. We needed a complete technician management system that integrates with the existing calendar and dashboard.

What We Built

1

Technician Management Page

Full CRUD operations for technicians. Name, email, phone, home address, work hours, max jobs per day, max travel time, active status.

2

Calendar Filtering

Dropdown to select technician. Events filtered by technicianId. Auto-selects Guy Walker as default. Dropped leads auto-assigned to selected technician.

3

Dashboard Filtering

Technician dropdown in dashboard header. Filters pipeline stages by calendar event assignments. Shows only leads assigned to selected technician.

4

Integrated Unscheduled Panel

Moved unscheduled leads inside the calendar card. Compact lead cards with customer name, vehicle, urgency, suburb.

5

Smart Conflict Detection

Refined overlap detection. Only triggers shift modal for actual time conflicts, not just events later in the day.

Download

Multi-Technician Features

  • Technician management page with full CRUD

  • Calendar filtering by technician

  • Dashboard filtering by technician

  • Auto-assignment on drag-and-drop

  • Smart conflict detection

JJM

The technician management page uses the same card-based UI pattern as the rest of the admin interface. Each technician record includes everything needed for intelligent scheduling: home address for travel time calculations, work hours for availability windows, and capacity limits to prevent overbooking.

Technician Record

  • Name, email, phone
  • Home address (travel calc)
  • Work hours (start/end)
  • Max jobs per day
  • Max travel minutes
  • Active/inactive status

Calendar Features

  • Dropdown filter in header
  • Events filtered by technicianId
  • Auto-assign on drag-drop
  • Smart conflict detection
  • Integrated unscheduled panel
  • No more overlap chaos

The Five Bugs We Encountered

Building features is the easy part. Debugging is where the real time goes. 90 minutes of our 4-hour session was spent fixing these five issues.

1

Google Places Autocomplete Closing Dialogs

What happened:

When adding a new technician, clicking on an address suggestion from the Google Places autocomplete dropdown would close the entire "Add Technician" dialog, losing all entered data.

Why it happened:

The Google Places dropdown (.pac-container) renders outside the dialog's DOM tree, directly on document.body. Radix UI's "click outside to close" behavior was triggered.

Solution:

<DialogContent 
  onPointerDownOutside={(e) => {
    const target = e.target as HTMLElement;
    if (target.closest('.pac-container')) {
      e.preventDefault();
    }
  }}
>
Download

Dialog Closing on Address Select

Problem

Clicking Google Places suggestion closed the entire Add Technician dialog

Solution

Added onPointerDownOutside handler to check for .pac-container clicks

JJM

This is a common issue with any third-party dropdown that renders to document.bodyrather than inside the parent component. Google Places, date pickers, rich text editors—they all have this problem with modal dialogs. The fix is always the same: intercept the click event and check if it's inside the third-party element.

2

"Failed to Fetch" When Dropping Leads

What happened:

Dragging an unscheduled lead onto the Newcastle calendar resulted in a "Failed to fetch" error. The event wasn't being created.

Why it happened:

The event creation was sending technicianId: nullwhen no technician was selected. Server-side validation rejected it.

Key insight: Sending field: null is different from not sending the field at all. When optional, omit rather than null.

3

"Failed to Update Calendar Event" Error

After creating events with technician assignments, updating them (like changing the time) would fail.

The updateCalendarEventSchema in shared/schema.ts didn't include technicianId as an allowed field. We'd added it to the insert schema but forgot the update schema.

4

Dashboard Technician Filter Not Working

After selecting Sydney in the dashboard filter, no leads appeared even though Sydney has 108 calendar events.

Wrong approach

Checking lead_purchases.assigned_technician_id

All 3,205 leads had NULL in this field

Correct approach

Checking calendar_events.technician_id

This is where the assignment actually lives

Download

Dashboard Filter Not Working

Before

Checking lead_purchases.assigned_technician_id (all NULL)

After

Checking calendar_events.technician_id (correct data)

JJM

Lesson learned: Always verify where data actually lives in the database before building filters. Don't assume based on table names or field names. Check the actual data.

5

Shift Modal Triggering Unnecessarily

Dropping a lead into an empty slot would still trigger the "Shift events?" confirmation modal if any events existed later in the day.

The original logic was checking if any events started at or after the drop time, rather than checking for actual overlap. So if you dropped at 9am and there was an event at 3pm, it would ask if you wanted to shift.

Solution: Refined conflict detection to check time-based overlap (does the new event's time range intersect with existing events?) and slot-based conflict (does an event already start in the exact slot?).

Database Changes

New Table: technicians

id
SERIAL PRIMARY KEY
name
TEXT NOT NULL
home_address
TEXT NOT NULL (for travel calc)
work_start_hour
INTEGER DEFAULT 8
max_jobs_per_day
INTEGER DEFAULT 8
is_active
BOOLEAN DEFAULT true

Modified Tables

calendar_events
Added: technician_id INTEGER REFERENCES technicians(id)
lead_purchases
Added: assigned_technician_id INTEGER REFERENCES technicians(id)
Download

New Technicians Table

  • name, email, phone - Contact info

  • home_address - For travel calculations

  • work_start_hour, work_end_hour - Availability

  • max_jobs_per_day - Capacity limit

  • max_travel_minutes - Distance constraint

JJM

The home_address field is required because it's essential for travel time calculations between jobs. The google_calendar_id field (not shown) is for future Google Calendar sync integration—two-way sync with technicians' personal calendars.

Current Metrics

108
Sydney Events
3,205
Total Leads
6
Technicians
5
Bugs Fixed
Download
6
Technicians Active

Sydney region leads with 108 events. System ready to scale to any number.

JJM

Lessons Learned

5 Key Takeaways

  • Track assignments where they happen - Technician assignment was on calendar_events, not lead_purchases. Always verify where data lives.
  • Radix UI + third-party dropdowns need special handling - Use onPointerDownOutside to prevent dialogs closing on external clicks.
  • Update ALL schemas when adding fields - Both insert AND update schemas. Missing one causes confusing errors.
  • Null vs undefined matters - Sending field: null is different from omitting the field. When optional, omit.
  • Test happy paths too - The shift modal bug wasn't caught because testing focused on conflicts. Test the no-conflict scenario.
Download

5 Lessons Learned

  • Track assignments where they happen

  • Radix UI + third-party dropdowns need special handling

  • Update ALL schemas when adding fields

  • Null vs undefined matters in validation

  • Test happy paths, not just edge cases

JJM

Time Investment

45m
Technician CRUD
30m
Calendar Filter
30m
Dashboard Filter
90m
Bug Fixes
4h
Total

The bug fixes took the most time (90 minutes for 5 bugs). The Google Places dialog issue alone took about 30 minutes to diagnose.

Download
4 hrs
Development Time

45m CRUD, 30m calendar filter, 30m dashboard filter, 90m bug fixes, 15m testing.

JJM

Ready to Scale

The multi-technician system is now live. Pink Slips NSW can scale to any number of technicians without the calendar becoming unusable. Each technician sees only their jobs, and the drag-and-drop scheduling still works perfectly.

Four hours of development. Five bugs squashed. One business ready to scale.

Get More Pink Slips Updates

Follow the journey as we build AI-powered systems for Pink Slips NSW. Weekly insights on automation, scheduling, and scaling service businesses.

Need a Custom Business System?

We build AI-powered business systems that scale with your growth. Calendar management, lead tracking, technician scheduling—custom-built for your specific workflow.

Custom calendar systems Multi-user management AI-powered scheduling
Let's Build Something Together →

Frequently Asked Questions

How does the technician filtering work?
Each calendar event has a technicianId field that links to the technicians table. When you select a technician from the dropdown, the calendar queries only events where technicianId matches. When you drag an unscheduled lead onto the calendar, it automatically assigns the currently selected technician to the new event.
Why did the Google Places autocomplete cause issues with dialogs?
Google Places renders its dropdown suggestions directly on document.body, outside the dialog's DOM tree. Radix UI's Dialog component has "click outside to close" behavior by default. When you click a suggestion, Radix sees it as a click outside the dialog and closes it. The fix is to intercept the click event and check if it's inside the .pac-container element.
What's the difference between sending null vs omitting a field?
When you send { technicianId: null }, the server receives the field with a null value and may validate it differently than if the field wasn't sent at all. Many validation schemas treat "field is null" differently from "field is missing." For optional fields, it's often safer to omit them entirely rather than sending null.
How do you handle travel time between jobs?
Each technician has a home_address field and a max_travel_minutes constraint. When scheduling jobs, the system can calculate travel time between the technician's current location (home or previous job) and the new job location. Jobs that would exceed the max travel time can be flagged or prevented. This feature uses the Google Maps Distance Matrix API.
Can technicians see each other's calendars?
Currently, the admin can see any technician's calendar by selecting them from the dropdown. Individual technicians would only see their own calendar when logged in with their own account. The "All Technicians" view was removed because it created too much visual overlap and confusion.

Social Media Carousel

8 cards • Click to download individually or as ZIP

Download
1 of 8

Building a Multi-Technician Calendar System

Pink Slips NSW Case Study

How we scaled from one technician to many

Brand
JJM
Download
2 of 8

The Problem

One calendar showing all technicians = overlapping events, confusion, unusable at scale.

Brand
JJM
Download
3 of 8

The Solution

  • 1

    Technician selector dropdown

  • 2

    Filtered calendar views

  • 3

    Drag-and-drop scheduling

  • 4

    Auto-assign on drop

Brand
JJM
Download
4 of 8
4hrs
Build Time

From single to multi-technician system

Brand
JJM
Download
5 of 8

Tech Challenges Solved

  • 1

    Google Places + Radix Dialog conflict

  • 2

    Null vs undefined field handling

  • 3

    Travel time constraints

  • 4

    Calendar event filtering

Brand
JJM
Download
6 of 8

Before vs After

Before

Single calendar, manual coordination

After

Per-technician views, automated assignment

Brand
JJM
Download
7 of 8
"

Each technician sees only their jobs. Admin sees everyone. Same database, different views.

— Jordan James

Brand
JJM
Download
8 of 8

See It Live

Book a Pink Slip and see the multi-tech system in action

Book Now
Brand
JJM

Share This Article

Spread the knowledge

Free Strategy Session

Stop Guessing.
Start Growing.

Get a custom strategy built around your goals, not generic advice. Real insights. Measurable results.

No obligation
30-min call
Custom strategy

Continue Your Learning Journey

Explore these related articles to deepen your understanding of web development

How to Build an Automated Facebook Posting System with React and Netlify

A complete guide to building a scheduled Facebook posting system that captures React components as high-quality images and posts them automatically - no third-party tools required.

12 min read
Read →

AI Dev Session: Carousel Cards & Admin Dashboard

Building social carousel cards, admin dashboard, and fixing a sneaky TOC bug—in 3 hours.

8 min read
Read →

Volume 7: Branding Our Own Blog Series

The meta moment when we built custom branding for the Pink Slips NSW blog series - pink gradients, dual logos, and conditional styling in 2 hours.

6 min read
Read →