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:
spliceboti
2025-07-09 23:54:32 +00:00
parent 2869d657c8
commit 89946fcef9
106 changed files with 20207 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
import { AppLayout } from "@/components/layout/AppLayout";
import { AvailabilityCalendar } from "@/components/availability/AvailabilityCalendar";
export default function Availability() {
return (
<AppLayout>
<AvailabilityCalendar />
</AppLayout>
);
}
+34
View File
@@ -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>
);
}
+22
View File
@@ -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>
);
}
+174
View File
@@ -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>
);
}
+123
View File
@@ -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>
);
}
+156
View File
@@ -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>
);
}
+139
View File
@@ -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>
);
}
+10
View File
@@ -0,0 +1,10 @@
import { AppLayout } from "@/components/layout/AppLayout";
import { AdminDashboard } from "@/components/admin/AdminDashboard";
export default function AdminDashboardPage() {
return (
<AppLayout>
<AdminDashboard />
</AppLayout>
);
}
+17
View File
@@ -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>
);
}
+10
View File
@@ -0,0 +1,10 @@
import { AppLayout } from "@/components/layout/AppLayout";
import { EventTypeManagement } from "@/components/admin/EventTypeManagement";
export default function EventTypes() {
return (
<AppLayout>
<EventTypeManagement />
</AppLayout>
);
}
+10
View File
@@ -0,0 +1,10 @@
import { AppLayout } from "@/components/layout/AppLayout";
import { DJManagement } from "@/components/admin/DJManagement";
export default function ManageDJs() {
return (
<AppLayout>
<DJManagement />
</AppLayout>
);
}
+10
View File
@@ -0,0 +1,10 @@
import { AppLayout } from "@/components/layout/AppLayout";
import { ScheduleTemplates } from "@/components/admin/ScheduleTemplates";
export default function Templates() {
return (
<AppLayout>
<ScheduleTemplates />
</AppLayout>
);
}
+21
View File
@@ -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>
);
}