APR 2026
THE ANDERSONS MANAGEMENT APP
Project Overview
The Andersons is a private household management platform built for a high-end estate household. It serves as an internal operations tool for household staff, a live-in chef, family members, and an administrator, each with their own tailored experience. The platform centralizes task scheduling, trip planning, dinner management, staff availability, and invoice tracking into a single cohesive application.
This was a collaborative team project built over roughly two months (February–April 2026) as part of a school assignment. My core contributions were the role-based access control infrastructure, role-specific dashboards, the dinner planner and meal management system, and a shared component library that gave the entire app visual consistency.
1. Role-Based Access Control (RBAC)
The platform has four distinct roles: admin, chef, staff, and family_member. I designed and implemented the entire access control layer from the database up.
Database layer: A roles table with a role_id foreign key on users. Simple. Role assignment is done by an admin through the user management panel.
Middleware: I wrote a custom checkRole middleware that reads the authenticated user's role and aborts with a 403 if the role does not match the route's requirement.
public function handle(Request $request, Closure $next, ...$roles): Response
{
if (!in_array(auth()->user()?->role?->name, $roles)) {
abort(403);
}
return $next($request);
}Route groups: All role-specific routes are grouped under their own middleware stack and URL prefix, making the access rules explicit and centrally readable in routes/web.php
Route::middleware(['auth', 'verified', 'role:chef'])->prefix('chef')->name('role.chef.')->group(function () {
Route::get('/dinner-planner', DinnerPlanner::class)->name('dinner-planner');
Route::get('/meals', MealsCreate::class)->name('meals');
});Inline guards: For component-level actions where a URL boundary alone isn't enough, I use inline role checks inside Livewire components, e.g. preventing a staff member from editing another user's task without adding a whole new route group.
2. Role-Specific Dashboards
The dashboard is the first thing every user sees after login. Rather than a single generic overview, I built four completely separate dashboards, each surfacing exactly what that role cares about.
Dynamic loading pattern: A single dashboard.blade.php view reads the authenticated user's role and dynamically includes the correct Livewire component.
<livewire:dashboard.{{ $role }}-dashboard />Admin Dashboard
Shows operational overview stats (tasks today, completion rate, active dinner plans, upcoming trips), a filterable task list that can be scoped to any user, an upcoming dinners widget with RSVP information, and an upcoming trips widget. The admin sees everything across all users.
Chef Dashboard
Focused entirely on the kitchen: today's task count, upcoming planned dinners with attendee lists, and critically, a per-attendee allergy summary. When the chef opens a dinner card, they can see exactly which attendees have dietary restrictions so they can plan accordingly.
Staff Dashboard
Task-centric: today's task count, completion progress, pending task invitations (accept/decline inline), and upcoming dinner plans to know if they're expected to serve.
Family Member Dashboard
Lifestyle-centric: tasks for today, upcoming personal trips, and dinner plans with a one-click RSVP flow.
Shared widget architecture: The dashboard is not a monolith, it's composed of independent Livewire sub-components. This means each widget refreshes independently and can be reused across dashboards.
TodaysTasks— configurable task list, accepts auserIdprop for scopingUpcomingDinners— RSVP-enabled dinner cardsUpcomingTrips— filterable trip list with status badgesTaskInvitations— accept/decline pending invitations inlineDashboardPanel— generic stat card (icon, label, value)
3. Dinner Planner & Planned Dinners
I built the entire dinner planning system: the data model, the Livewire component, the views, and the calendar integration.
Dinner Planner: Only the chef and admin roles can create and manage dinner plans.
Color + icon selection: Each dinner gets a visual identity, a Tailwind color class and a Boxicons icon slug. Both are chosen from curated dropdown lists in the UI, making every dinner card immediately recognisable on the schedule.
Past dinner templates: A search field lets the chef look up past (deactivated) dinners and pre-fill the create form with their name, description, and guest count. This prevents repetitive re-entry for recurring dinner types.
Soft deactivation: Dinners are deactivated rather than deleted, so they remain searchable as templates and retain their history.
Confirm-before-destroy: Every deactivation goes through a two-step confirmation modal
confirmDeactivate()→confirmDeactivateAction()) to prevent accidental data loss.Calendar integration: Planned dinners are surfaced as blue event tiles on the shared schedule calendar. Any user can click a dinner event to see details and RSVP with a guest count directly from the calendar.
4. Meal Management
Alongside dinner planning, I built the full meal management feature for the chef role.
Meals: Chefs create meals with a name, description, and allergy tags. Key details:
Allergy tagging: Meals are linked to the global
allergiesreference table via a many-to-many pivot. The chef can tag a meal with multiple allergens (nuts, gluten, dairy, etc.) at creation time.Inline allergy creation: If an allergen isn't in the system yet, the chef can create it inline from the meal form without leaving the page.
Meal-to-dinner linking: A meal can be assigned to a specific planned dinner. When a dinner is opened in the chef dashboard, its linked meal and the meal's allergen tags are visible alongside the attendee list.
5. Shared Component Library
One of my horizontal contributions across the whole project was building reusable Blade components that gave every page a consistent look and feel. Without these, each team member's pages would have drifted stylistically.
<x-panel>
The foundational layout card used on nearly every page. Accepts a title, optional subtitle, and a dark prop. White background with rounded corners and a shadow in light mode; glass-dark in dark mode.
<x-panel title="Upcoming Dinners" subtitle="Next 7 days">
{{-- content --}}
</x-panel><x-dashboard-panel>
A stat card built on top of <x-panel>. Accepts icon (Flux icon name), label, value, and iconBg (a Tailwind background class for the icon circle). Used on all four dashboards for at-a-glance KPIs.
<x-dashboard-panel
icon="check-circle"
label="Tasks Completed Today"
:value="$completedCount . '/' . $totalCount"
iconBg="bg-teal-100"
/><x-task-card>
A consistent task list item with status-aware styling: teal border for in-progress, amber for completed, white for pending. Shows assigned user, role badge, time range, location pin, and priority badge. Used in widgets, dashboards, and the schedule page.
<x-meal-card>
Displays a meal with its icon, name, linked dinner date, and a teal "View" button. Used in the chef dashboard and meal listing.
Form components: A set of <x-form.field>, <x-form.input>, <x-form.select>, <x-form.textarea>, and <x-form.file-upload> components wrap Flux inputs with consistent label/error layout, reducing repetition in every form across the app.
What I Learned
Designing RBAC from scratch in Laravel without reaching for a package, understanding where to enforce access (middleware, route groups, component-level guards) and why each layer matters.
Component-driven thinking in Blade/Livewire: identifying what should be a reusable component early saves significant rework when five team members are building five different pages simultaneously.
Composing dashboards from independent Livewire widgets rather than a single monolithic component, keeping each widget independently refreshable and independently testable.
Database design for soft state: using
is_activeinstead of hard-deleting dinners so historical data remains useful as templates.Communicating across Livewire components via events and Alpine.js listeners, including propagating live state changes (color, notifications) without full page reloads.