Set up the basic structure and functionality for the DJ management system
Initializes project structure, adds core components, and configures essential dependencies. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 3a22ac80-cd1d-4441-9e36-f24fc2f4c3de Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3478f7c3-db8c-4fca-9165-3adbdf1b5829/e8da43e7-d99c-4328-9fdc-485bdeecffc1.jpg
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { AvailabilityCalendar } from "@/components/availability/AvailabilityCalendar";
|
||||
|
||||
export default function Availability() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<AvailabilityCalendar />
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { useState } from "react";
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { EventList } from "@/components/events/EventList";
|
||||
import { EventModal } from "@/components/events/EventModal";
|
||||
import { Plus } from "lucide-react";
|
||||
|
||||
export default function Events() {
|
||||
const [showEventModal, setShowEventModal] = useState(false);
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">My Events</h1>
|
||||
<p className="text-slate-600">Manage your upcoming and past events</p>
|
||||
</div>
|
||||
<Button onClick={() => setShowEventModal(true)}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Add Event
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<EventList />
|
||||
</div>
|
||||
|
||||
<EventModal
|
||||
isOpen={showEventModal}
|
||||
onClose={() => setShowEventModal(false)}
|
||||
/>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { DashboardStats } from "@/components/dashboard/DashboardStats";
|
||||
import { UpcomingEvents } from "@/components/dashboard/UpcomingEvents";
|
||||
import { QuickActions } from "@/components/dashboard/QuickActions";
|
||||
import { CalendarWidget } from "@/components/dashboard/CalendarWidget";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<DashboardStats />
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<UpcomingEvents />
|
||||
|
||||
<div className="space-y-6">
|
||||
<QuickActions />
|
||||
<CalendarWidget />
|
||||
</div>
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { Link } from "wouter";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Music, Calendar, Users, Settings } from "lucide-react";
|
||||
|
||||
export default function Landing() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm border-b border-slate-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
<div className="flex items-center">
|
||||
<Music className="w-8 h-8 text-primary-600 mr-3" />
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-slate-800">DJ Management</h1>
|
||||
<p className="text-sm text-slate-500">Professional Dashboard</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<Button
|
||||
onClick={() => window.location.href = "/api/login"}
|
||||
className="bg-primary-600 hover:bg-primary-700"
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-4xl font-bold text-slate-800 mb-6">
|
||||
Professional DJ Management System
|
||||
</h2>
|
||||
<p className="text-xl text-slate-600 mb-8">
|
||||
Streamline your DJ operations with our comprehensive management platform.
|
||||
Handle schedules, events, and team coordination all in one place.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Button
|
||||
size="lg"
|
||||
onClick={() => window.location.href = "/api/login"}
|
||||
className="bg-primary-600 hover:bg-primary-700"
|
||||
>
|
||||
Get Started
|
||||
</Button>
|
||||
<Button
|
||||
size="lg"
|
||||
variant="outline"
|
||||
>
|
||||
Learn More
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section className="py-16 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-12">
|
||||
<h3 className="text-3xl font-bold text-slate-800 mb-4">
|
||||
Everything You Need to Manage Your DJ Team
|
||||
</h3>
|
||||
<p className="text-lg text-slate-600">
|
||||
Powerful tools designed for modern DJ management
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Calendar className="w-8 h-8 text-primary-600 mb-2" />
|
||||
<CardTitle>Event Management</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-slate-600">
|
||||
Create, manage, and track all your events with intuitive scheduling tools and automated assignments.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Users className="w-8 h-8 text-primary-600 mb-2" />
|
||||
<CardTitle>DJ Profiles</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-slate-600">
|
||||
Comprehensive DJ profiles with social links, availability management, and performance tracking.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Settings className="w-8 h-8 text-primary-600 mb-2" />
|
||||
<CardTitle>Admin Tools</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-slate-600">
|
||||
Advanced admin controls for user management, schedule templates, and automated DJ assignments.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats Section */}
|
||||
<section className="py-16 bg-slate-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 text-center">
|
||||
<div>
|
||||
<div className="text-3xl font-bold text-primary-600 mb-2">500+</div>
|
||||
<div className="text-slate-600">Events Managed</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold text-primary-600 mb-2">50+</div>
|
||||
<div className="text-slate-600">Active DJs</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl font-bold text-primary-600 mb-2">99.9%</div>
|
||||
<div className="text-slate-600">Uptime</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-16 bg-primary-600">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<h3 className="text-3xl font-bold text-white mb-4">
|
||||
Ready to Transform Your DJ Management?
|
||||
</h3>
|
||||
<p className="text-primary-100 mb-8">
|
||||
Join hundreds of event organizers who trust our platform to manage their DJ operations.
|
||||
</p>
|
||||
<Button
|
||||
size="lg"
|
||||
variant="secondary"
|
||||
onClick={() => window.location.href = "/api/login"}
|
||||
>
|
||||
Start Managing Today
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-slate-800 text-white py-12">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center">
|
||||
<div className="flex items-center mb-4 md:mb-0">
|
||||
<Music className="w-6 h-6 mr-2" />
|
||||
<span className="text-lg font-semibold">DJ Management System</span>
|
||||
</div>
|
||||
<div className="text-slate-400">
|
||||
© 2024 DJ Management System. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ProfileModal } from "@/components/profile/ProfileModal";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
import { Edit, ExternalLink } from "lucide-react";
|
||||
|
||||
export default function Profile() {
|
||||
const { user } = useAuth();
|
||||
const [showProfileModal, setShowProfileModal] = useState(false);
|
||||
|
||||
const { data: socialLinks } = useQuery({
|
||||
queryKey: ["/api/social-links"],
|
||||
});
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">My Profile</h1>
|
||||
<p className="text-slate-600">Manage your DJ profile and settings</p>
|
||||
</div>
|
||||
<Button onClick={() => setShowProfileModal(true)}>
|
||||
<Edit className="w-4 h-4 mr-2" />
|
||||
Edit Profile
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Profile Info */}
|
||||
<Card className="lg:col-span-2">
|
||||
<CardHeader>
|
||||
<CardTitle>Profile Information</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-start space-x-6">
|
||||
<div className="flex-shrink-0">
|
||||
<img
|
||||
src={user?.profileImageUrl || "https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?ixlib=rb-4.0.3&auto=format&fit=crop&w=128&h=128"}
|
||||
alt={user?.displayName || "Profile"}
|
||||
className="w-24 h-24 rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600">Display Name</label>
|
||||
<p className="text-slate-800">{user?.displayName || "Not set"}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600">Email</label>
|
||||
<p className="text-slate-800">{user?.email}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600">Role</label>
|
||||
<Badge variant={user?.role === "admin" ? "default" : "secondary"}>
|
||||
{user?.role}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600">Status</label>
|
||||
<Badge variant={user?.isActive ? "default" : "destructive"}>
|
||||
{user?.isActive ? "Active" : "Inactive"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium text-slate-600">Max Events per Month</label>
|
||||
<p className="text-slate-800">{user?.maxEventsPerMonth || 2}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Social Links */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Social Links</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
{socialLinks?.map((link: any) => (
|
||||
<div key={link.id} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
|
||||
<div>
|
||||
<div className="font-medium text-sm">{link.label}</div>
|
||||
<div className="text-xs text-slate-500 truncate">{link.url}</div>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer">
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{(!socialLinks || socialLinks.length === 0) && (
|
||||
<div className="text-center py-8 text-slate-500">
|
||||
<p>No social links added yet</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ProfileModal
|
||||
isOpen={showProfileModal}
|
||||
onClose={() => setShowProfileModal(false)}
|
||||
/>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useRoute } from "wouter";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ExternalLink, ArrowLeft } from "lucide-react";
|
||||
|
||||
export default function PublicDJProfile() {
|
||||
const [, params] = useRoute("/dj/:djId");
|
||||
const djId = params?.djId;
|
||||
|
||||
const { data: dj, isLoading } = useQuery({
|
||||
queryKey: ["/api/users", djId],
|
||||
enabled: !!djId,
|
||||
});
|
||||
|
||||
const { data: socialLinks } = useQuery({
|
||||
queryKey: ["/api/social-links", djId],
|
||||
enabled: !!djId,
|
||||
});
|
||||
|
||||
const { data: upcomingEvents } = useQuery({
|
||||
queryKey: ["/api/events", "dj", djId],
|
||||
enabled: !!djId,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!dj) {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-bold text-slate-800 mb-2">DJ Not Found</h1>
|
||||
<p className="text-slate-600">The DJ profile you're looking for doesn't exist.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm border-b border-slate-200">
|
||||
<div className="max-w-4xl mx-auto px-4 py-6">
|
||||
<Button variant="ghost" onClick={() => window.history.back()}>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Profile Info */}
|
||||
<Card className="lg:col-span-2">
|
||||
<CardHeader>
|
||||
<CardTitle>DJ Profile</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-start space-x-6">
|
||||
<div className="flex-shrink-0">
|
||||
<img
|
||||
src={dj.profileImageUrl || "https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?ixlib=rb-4.0.3&auto=format&fit=crop&w=128&h=128"}
|
||||
alt={dj.displayName || "DJ Profile"}
|
||||
className="w-32 h-32 rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<h1 className="text-2xl font-bold text-slate-800 mb-2">
|
||||
{dj.displayName || dj.firstName || "DJ"}
|
||||
</h1>
|
||||
<Badge variant="secondary">Professional DJ</Badge>
|
||||
|
||||
<div className="mt-4 space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm font-medium text-slate-600">Status:</span>
|
||||
<Badge variant={dj.isActive ? "default" : "destructive"}>
|
||||
{dj.isActive ? "Active" : "Inactive"}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Social Links */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Connect</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
{socialLinks?.map((link: any) => (
|
||||
<Button
|
||||
key={link.id}
|
||||
variant="outline"
|
||||
className="w-full justify-between"
|
||||
asChild
|
||||
>
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer">
|
||||
<span>{link.label}</span>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
</Button>
|
||||
))}
|
||||
|
||||
{(!socialLinks || socialLinks.length === 0) && (
|
||||
<div className="text-center py-8 text-slate-500">
|
||||
<p>No social links available</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Upcoming Events */}
|
||||
<Card className="lg:col-span-3">
|
||||
<CardHeader>
|
||||
<CardTitle>Upcoming Events</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
{upcomingEvents?.slice(0, 5).map((event: any) => (
|
||||
<div key={event.id} className="flex items-center justify-between p-4 bg-slate-50 rounded-lg">
|
||||
<div>
|
||||
<h3 className="font-medium text-slate-800">{event.name}</h3>
|
||||
<p className="text-sm text-slate-600">
|
||||
{new Date(event.date).toLocaleDateString()} • {event.startTime} - {event.endTime}
|
||||
</p>
|
||||
<p className="text-sm text-slate-500">{event.locationName}</p>
|
||||
</div>
|
||||
<Badge variant="outline">{event.eventType?.name}</Badge>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{(!upcomingEvents || upcomingEvents.length === 0) && (
|
||||
<div className="text-center py-8 text-slate-500">
|
||||
<p>No upcoming events</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Calendar, Clock, MapPin } from "lucide-react";
|
||||
|
||||
export default function Schedule() {
|
||||
const { data: events, isLoading } = useQuery({
|
||||
queryKey: ["/api/events"],
|
||||
});
|
||||
|
||||
const { data: eventTypes } = useQuery({
|
||||
queryKey: ["/api/event-types"],
|
||||
});
|
||||
|
||||
const getEventTypeName = (eventTypeId: number) => {
|
||||
return eventTypes?.find((type: any) => type.id === eventTypeId)?.name || "Unknown";
|
||||
};
|
||||
|
||||
const getEventsByMonth = () => {
|
||||
if (!events) return {};
|
||||
|
||||
const eventsByMonth: { [key: string]: any[] } = {};
|
||||
|
||||
events.forEach((event: any) => {
|
||||
const date = new Date(event.date);
|
||||
const monthKey = `${date.getFullYear()}-${date.getMonth()}`;
|
||||
|
||||
if (!eventsByMonth[monthKey]) {
|
||||
eventsByMonth[monthKey] = [];
|
||||
}
|
||||
|
||||
eventsByMonth[monthKey].push(event);
|
||||
});
|
||||
|
||||
return eventsByMonth;
|
||||
};
|
||||
|
||||
const formatMonthYear = (monthKey: string) => {
|
||||
const [year, month] = monthKey.split('-');
|
||||
const date = new Date(parseInt(year), parseInt(month));
|
||||
return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
|
||||
};
|
||||
|
||||
const eventsByMonth = getEventsByMonth();
|
||||
const monthKeys = Object.keys(eventsByMonth).sort().reverse();
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">My Schedule</h1>
|
||||
<p className="text-slate-600">View your upcoming and past events</p>
|
||||
</div>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="space-y-4">
|
||||
{[...Array(3)].map((_, i) => (
|
||||
<Card key={i}>
|
||||
<CardContent className="p-6">
|
||||
<div className="animate-pulse">
|
||||
<div className="h-6 bg-slate-200 rounded mb-4"></div>
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 bg-slate-200 rounded w-3/4"></div>
|
||||
<div className="h-4 bg-slate-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-8">
|
||||
{monthKeys.map(monthKey => (
|
||||
<div key={monthKey}>
|
||||
<h2 className="text-xl font-semibold text-slate-800 mb-4">
|
||||
{formatMonthYear(monthKey)}
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
{eventsByMonth[monthKey].map((event: any) => (
|
||||
<Card key={event.id}>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center space-x-3 mb-2">
|
||||
<h3 className="text-lg font-medium text-slate-800">{event.name}</h3>
|
||||
<Badge variant={event.isAssignedByAdmin ? "default" : "secondary"}>
|
||||
{event.isAssignedByAdmin ? "Admin Assigned" : "Self-Added"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm text-slate-600">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span>{new Date(event.date).toLocaleDateString()}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span>{event.startTime} - {event.endTime}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<MapPin className="w-4 h-4" />
|
||||
<span>{event.locationName}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 flex items-center space-x-4">
|
||||
<Badge variant="outline">{getEventTypeName(event.eventTypeId)}</Badge>
|
||||
{event.description && (
|
||||
<p className="text-sm text-slate-600">{event.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{monthKeys.length === 0 && (
|
||||
<Card>
|
||||
<CardContent className="p-12 text-center">
|
||||
<Calendar className="w-12 h-12 mx-auto mb-4 text-slate-300" />
|
||||
<h3 className="text-lg font-medium text-slate-800 mb-2">No events scheduled</h3>
|
||||
<p className="text-slate-600">Your events will appear here once they're added.</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { AdminDashboard } from "@/components/admin/AdminDashboard";
|
||||
|
||||
export default function AdminDashboardPage() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<AdminDashboard />
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { AssignmentTool } from "@/components/admin/AssignmentTool";
|
||||
|
||||
export default function AssignmentToolPage() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold text-slate-800">DJ Assignment Tool</h1>
|
||||
<p className="text-slate-600">Advanced tools for assigning DJs to schedule templates</p>
|
||||
</div>
|
||||
|
||||
<AssignmentTool />
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { EventTypeManagement } from "@/components/admin/EventTypeManagement";
|
||||
|
||||
export default function EventTypes() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<EventTypeManagement />
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { DJManagement } from "@/components/admin/DJManagement";
|
||||
|
||||
export default function ManageDJs() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<DJManagement />
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { ScheduleTemplates } from "@/components/admin/ScheduleTemplates";
|
||||
|
||||
export default function Templates() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<ScheduleTemplates />
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="min-h-screen w-full flex items-center justify-center bg-gray-50">
|
||||
<Card className="w-full max-w-md mx-4">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex mb-4 gap-2">
|
||||
<AlertCircle className="h-8 w-8 text-red-500" />
|
||||
<h1 className="text-2xl font-bold text-gray-900">404 Page Not Found</h1>
|
||||
</div>
|
||||
|
||||
<p className="mt-4 text-sm text-gray-600">
|
||||
Did you forget to add the page to the router?
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user