Initial commit: SmoothSchedule multi-tenant scheduling platform
This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
85
frontend/src/components/PlatformSidebar.tsx
Normal file
85
frontend/src/components/PlatformSidebar.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { LayoutDashboard, Building2, MessageSquare, Settings, Users, Shield } from 'lucide-react';
|
||||
import { User } from '../types';
|
||||
import SmoothScheduleLogo from './SmoothScheduleLogo';
|
||||
|
||||
interface PlatformSidebarProps {
|
||||
user: User;
|
||||
isCollapsed: boolean;
|
||||
toggleCollapse: () => void;
|
||||
}
|
||||
|
||||
const PlatformSidebar: React.FC<PlatformSidebarProps> = ({ user, isCollapsed, toggleCollapse }) => {
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
|
||||
const getNavClass = (path: string) => {
|
||||
const isActive = location.pathname === path || (path !== '/' && location.pathname.startsWith(path));
|
||||
const baseClasses = `flex items-center gap-3 py-2 text-sm font-medium rounded-md transition-colors`;
|
||||
const collapsedClasses = isCollapsed ? 'px-3 justify-center' : 'px-3';
|
||||
const activeClasses = 'bg-gray-700 text-white';
|
||||
const inactiveClasses = 'text-gray-400 hover:text-white hover:bg-gray-800';
|
||||
return `${baseClasses} ${collapsedClasses} ${isActive ? activeClasses : inactiveClasses}`;
|
||||
};
|
||||
|
||||
const isSuperuser = user.role === 'superuser';
|
||||
const isManager = user.role === 'platform_manager';
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col h-full bg-gray-900 text-white shrink-0 border-r border-gray-800 transition-all duration-300 ${isCollapsed ? 'w-20' : 'w-64'}`}>
|
||||
<button
|
||||
onClick={toggleCollapse}
|
||||
className={`flex items-center gap-3 w-full text-left px-6 py-6 border-b border-gray-800 ${isCollapsed ? 'justify-center' : ''} hover:bg-gray-800 transition-colors focus:outline-none`}
|
||||
aria-label={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
>
|
||||
<SmoothScheduleLogo className="w-10 h-10 shrink-0" />
|
||||
{!isCollapsed && (
|
||||
<div className="overflow-hidden">
|
||||
<h1 className="font-bold text-sm tracking-wide uppercase text-gray-100 truncate">Smooth Schedule</h1>
|
||||
<p className="text-xs text-gray-500 capitalize truncate">{user.role.replace('_', ' ')}</p>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
||||
<nav className="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
|
||||
<p className={`text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2 ${isCollapsed ? 'text-center' : 'px-3'}`}>{isCollapsed ? 'Ops' : 'Operations'}</p>
|
||||
{(isSuperuser || isManager) && (
|
||||
<Link to="/platform/dashboard" className={getNavClass('/platform/dashboard')} title={t('nav.platformDashboard')}>
|
||||
<LayoutDashboard size={18} className="shrink-0" />
|
||||
{!isCollapsed && <span>{t('nav.dashboard')}</span>}
|
||||
</Link>
|
||||
)}
|
||||
<Link to="/platform/businesses" className={getNavClass("/platform/businesses")} title={t('nav.businesses')}>
|
||||
<Building2 size={18} className="shrink-0" />
|
||||
{!isCollapsed && <span>{t('nav.businesses')}</span>}
|
||||
</Link>
|
||||
<Link to="/platform/users" className={getNavClass('/platform/users')} title={t('nav.users')}>
|
||||
<Users size={18} className="shrink-0" />
|
||||
{!isCollapsed && <span>{t('nav.users')}</span>}
|
||||
</Link>
|
||||
<Link to="/platform/support" className={getNavClass('/platform/support')} title={t('nav.support')}>
|
||||
<MessageSquare size={18} className="shrink-0" />
|
||||
{!isCollapsed && <span>{t('nav.support')}</span>}
|
||||
</Link>
|
||||
|
||||
{isSuperuser && (
|
||||
<>
|
||||
<p className={`text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2 mt-8 ${isCollapsed ? 'text-center' : 'px-3'}`}>{isCollapsed ? 'Sys' : 'System'}</p>
|
||||
<Link to="/platform/staff" className={getNavClass('/platform/staff')} title={t('nav.staff')}>
|
||||
<Shield size={18} className="shrink-0" />
|
||||
{!isCollapsed && <span>{t('nav.staff')}</span>}
|
||||
</Link>
|
||||
<Link to="/platform/settings" className={getNavClass('/platform/settings')} title={t('nav.platformSettings')}>
|
||||
<Settings size={18} className="shrink-0" />
|
||||
{!isCollapsed && <span>{t('nav.platformSettings')}</span>}
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlatformSidebar;
|
||||
Reference in New Issue
Block a user