Skip to content

Create RoleGuard Component #201

@respp

Description

@respp

🏷️ Context: Guest/Host Role System

We’re implementing a dual-role system where users can have two profiles:

  • Guest (Tenant): Users who search and book properties
  • Host: Users who list and manage properties
  • Dual: Users who can be both at the same time

Role Flow:

  1. All users start as Guest by default

  2. To become a Host, they must complete the “Become a Host” flow

  3. Dashboard access depends on role:

    • Guest Dashboard: Always accessible (bookings, profile, wallet)
    • Host Dashboard: Only if hostStatus === 'verified' and they have properties

Architecture:

  • Reuse 90% of the existing codebase (components, hooks, services)
  • Add role logic on top of the current infrastructure
  • Do not change existing dashboard components (they already work)

References:

  • Existing auth system: hooks/auth/use-auth.tsx
  • Current dashboards: /tenant-dashboard (guest) and /dashboard/host-dashboard (host)
  • Reusable components: components/dashboard/*

📌 Task: Create RoleGuard component to protect routes

Goal: Guard that protects routes based on the user’s role.

Files to create:

  • apps/web/src/components/guards/RoleGuard.tsx

Dependencies:

Implementation:

Note: The snippet below is a helper scaffold and should NOT be treated as the final implementation. Review redirect behavior, loading states, accessibility, and SSR concerns for Next.js.

// apps/web/src/components/guards/RoleGuard.tsx
'use client';

import { useUserRole } from '~/hooks/useUserRole';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import type { UserRole } from '~/types/roles';

interface RoleGuardProps {
  children: React.ReactNode;
  requiredRole: UserRole;
  fallbackPath?: string;
}

export function RoleGuard({
  children,
  requiredRole,
  fallbackPath = '/become-host',
}: RoleGuardProps) {
  const { role, canAccessHostDashboard } = useUserRole();
  const router = useRouter();

  useEffect(() => {
    if (requiredRole === 'host' && !canAccessHostDashboard) {
      router.push(fallbackPath);
    }
  }, [requiredRole, canAccessHostDashboard, router, fallbackPath]);

  if (requiredRole === 'host' && !canAccessHostDashboard) {
    return (
      <div className="flex flex-col items-center justify-center min-h-screen p-4">
        <div className="text-center max-w-md">
          <h2 className="text-2xl font-bold mb-4">Host Access Required</h2>
          <p className="text-gray-600 mb-6">
            You need to become a host to access this page.
          </p>
          <button
            onClick={() => router.push('/become-host')}
            className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700"
          >
            Become a Host
          </button>
        </div>
      </div>
    );
  }

  return <>{children}</>;
}

Acceptance Criteria:

  • Component created at components/guards/RoleGuard.tsx
  • Protects host routes correctly
  • Shows message when access is denied
  • Auto-redirects to fallbackPath
  • No TypeScript errors

Metadata

Metadata

Assignees

Labels

Stellar WaveIssues in the Stellar wave program

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions