From 89946fcef9638dd6d363481addd047092b2bdc47 Mon Sep 17 00:00:00 2001 From: spliceboti <44727389-spliceboti@users.noreply.replit.com> Date: Wed, 9 Jul 2025 23:54:32 +0000 Subject: [PATCH] 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 --- .gitignore | 6 + .replit | 36 + client/index.html | 13 + client/src/App.tsx | 110 + .../src/components/admin/AdminDashboard.tsx | 90 + .../src/components/admin/AssignmentTool.tsx | 170 + client/src/components/admin/DJManagement.tsx | 263 + .../components/admin/EventTypeManagement.tsx | 251 + .../src/components/admin/PendingRequests.tsx | 123 + .../components/admin/ScheduleTemplates.tsx | 352 + .../availability/AvailabilityCalendar.tsx | 268 + .../components/dashboard/CalendarWidget.tsx | 107 + .../components/dashboard/DashboardStats.tsx | 70 + .../src/components/dashboard/QuickActions.tsx | 58 + .../components/dashboard/UpcomingEvents.tsx | 116 + client/src/components/events/EventList.tsx | 198 + client/src/components/events/EventModal.tsx | 256 + client/src/components/layout/AppLayout.tsx | 58 + client/src/components/layout/Sidebar.tsx | 117 + .../src/components/profile/ProfileModal.tsx | 326 + client/src/components/ui/accordion.tsx | 56 + client/src/components/ui/alert-dialog.tsx | 139 + client/src/components/ui/alert.tsx | 59 + client/src/components/ui/aspect-ratio.tsx | 5 + client/src/components/ui/avatar.tsx | 50 + client/src/components/ui/badge.tsx | 36 + client/src/components/ui/breadcrumb.tsx | 115 + client/src/components/ui/button.tsx | 56 + client/src/components/ui/calendar.tsx | 68 + client/src/components/ui/card.tsx | 79 + client/src/components/ui/carousel.tsx | 260 + client/src/components/ui/chart.tsx | 365 + client/src/components/ui/checkbox.tsx | 28 + client/src/components/ui/collapsible.tsx | 11 + client/src/components/ui/command.tsx | 151 + client/src/components/ui/context-menu.tsx | 198 + client/src/components/ui/data-table.tsx | 160 + client/src/components/ui/dialog.tsx | 122 + client/src/components/ui/drawer.tsx | 118 + client/src/components/ui/dropdown-menu.tsx | 198 + client/src/components/ui/form.tsx | 178 + client/src/components/ui/hover-card.tsx | 29 + client/src/components/ui/image-cropper.tsx | 164 + client/src/components/ui/input-otp.tsx | 69 + client/src/components/ui/input.tsx | 22 + client/src/components/ui/label.tsx | 24 + client/src/components/ui/menubar.tsx | 256 + client/src/components/ui/navigation-menu.tsx | 128 + client/src/components/ui/pagination.tsx | 117 + client/src/components/ui/popover.tsx | 29 + client/src/components/ui/progress.tsx | 28 + client/src/components/ui/radio-group.tsx | 42 + client/src/components/ui/resizable.tsx | 45 + client/src/components/ui/scroll-area.tsx | 46 + client/src/components/ui/select.tsx | 160 + client/src/components/ui/separator.tsx | 29 + client/src/components/ui/sheet.tsx | 140 + client/src/components/ui/sidebar.tsx | 75 + client/src/components/ui/skeleton.tsx | 15 + client/src/components/ui/slider.tsx | 26 + client/src/components/ui/switch.tsx | 27 + client/src/components/ui/table.tsx | 117 + client/src/components/ui/tabs.tsx | 53 + client/src/components/ui/textarea.tsx | 22 + client/src/components/ui/toast.tsx | 127 + client/src/components/ui/toaster.tsx | 33 + client/src/components/ui/toggle-group.tsx | 61 + client/src/components/ui/toggle.tsx | 43 + client/src/components/ui/tooltip.tsx | 30 + client/src/hooks/use-mobile.tsx | 19 + client/src/hooks/use-toast.ts | 191 + client/src/hooks/useAuth.ts | 14 + client/src/index.css | 221 + client/src/lib/authUtils.ts | 3 + client/src/lib/queryClient.ts | 57 + client/src/lib/utils.ts | 6 + client/src/main.tsx | 5 + client/src/pages/Availability.tsx | 10 + client/src/pages/Events.tsx | 34 + client/src/pages/Home.tsx | 22 + client/src/pages/Landing.tsx | 174 + client/src/pages/Profile.tsx | 123 + client/src/pages/PublicDJProfile.tsx | 156 + client/src/pages/Schedule.tsx | 139 + client/src/pages/admin/AdminDashboard.tsx | 10 + client/src/pages/admin/AssignmentTool.tsx | 17 + client/src/pages/admin/EventTypes.tsx | 10 + client/src/pages/admin/ManageDJs.tsx | 10 + client/src/pages/admin/Templates.tsx | 10 + client/src/pages/not-found.tsx | 21 + components.json | 20 + drizzle.config.ts | 14 + package-lock.json | 9334 +++++++++++++++++ package.json | 109 + postcss.config.js | 6 + replit.md | 136 + server/db.ts | 15 + server/index.ts | 70 + server/replitAuth.ts | 157 + server/routes.ts | 566 + server/storage.ts | 558 + server/vite.ts | 85 + shared/schema.ts | 338 + tailwind.config.ts | 90 + tsconfig.json | 23 + vite.config.ts | 37 + 106 files changed, 20207 insertions(+) create mode 100644 .gitignore create mode 100644 client/index.html create mode 100644 client/src/App.tsx create mode 100644 client/src/components/admin/AdminDashboard.tsx create mode 100644 client/src/components/admin/AssignmentTool.tsx create mode 100644 client/src/components/admin/DJManagement.tsx create mode 100644 client/src/components/admin/EventTypeManagement.tsx create mode 100644 client/src/components/admin/PendingRequests.tsx create mode 100644 client/src/components/admin/ScheduleTemplates.tsx create mode 100644 client/src/components/availability/AvailabilityCalendar.tsx create mode 100644 client/src/components/dashboard/CalendarWidget.tsx create mode 100644 client/src/components/dashboard/DashboardStats.tsx create mode 100644 client/src/components/dashboard/QuickActions.tsx create mode 100644 client/src/components/dashboard/UpcomingEvents.tsx create mode 100644 client/src/components/events/EventList.tsx create mode 100644 client/src/components/events/EventModal.tsx create mode 100644 client/src/components/layout/AppLayout.tsx create mode 100644 client/src/components/layout/Sidebar.tsx create mode 100644 client/src/components/profile/ProfileModal.tsx create mode 100644 client/src/components/ui/accordion.tsx create mode 100644 client/src/components/ui/alert-dialog.tsx create mode 100644 client/src/components/ui/alert.tsx create mode 100644 client/src/components/ui/aspect-ratio.tsx create mode 100644 client/src/components/ui/avatar.tsx create mode 100644 client/src/components/ui/badge.tsx create mode 100644 client/src/components/ui/breadcrumb.tsx create mode 100644 client/src/components/ui/button.tsx create mode 100644 client/src/components/ui/calendar.tsx create mode 100644 client/src/components/ui/card.tsx create mode 100644 client/src/components/ui/carousel.tsx create mode 100644 client/src/components/ui/chart.tsx create mode 100644 client/src/components/ui/checkbox.tsx create mode 100644 client/src/components/ui/collapsible.tsx create mode 100644 client/src/components/ui/command.tsx create mode 100644 client/src/components/ui/context-menu.tsx create mode 100644 client/src/components/ui/data-table.tsx create mode 100644 client/src/components/ui/dialog.tsx create mode 100644 client/src/components/ui/drawer.tsx create mode 100644 client/src/components/ui/dropdown-menu.tsx create mode 100644 client/src/components/ui/form.tsx create mode 100644 client/src/components/ui/hover-card.tsx create mode 100644 client/src/components/ui/image-cropper.tsx create mode 100644 client/src/components/ui/input-otp.tsx create mode 100644 client/src/components/ui/input.tsx create mode 100644 client/src/components/ui/label.tsx create mode 100644 client/src/components/ui/menubar.tsx create mode 100644 client/src/components/ui/navigation-menu.tsx create mode 100644 client/src/components/ui/pagination.tsx create mode 100644 client/src/components/ui/popover.tsx create mode 100644 client/src/components/ui/progress.tsx create mode 100644 client/src/components/ui/radio-group.tsx create mode 100644 client/src/components/ui/resizable.tsx create mode 100644 client/src/components/ui/scroll-area.tsx create mode 100644 client/src/components/ui/select.tsx create mode 100644 client/src/components/ui/separator.tsx create mode 100644 client/src/components/ui/sheet.tsx create mode 100644 client/src/components/ui/sidebar.tsx create mode 100644 client/src/components/ui/skeleton.tsx create mode 100644 client/src/components/ui/slider.tsx create mode 100644 client/src/components/ui/switch.tsx create mode 100644 client/src/components/ui/table.tsx create mode 100644 client/src/components/ui/tabs.tsx create mode 100644 client/src/components/ui/textarea.tsx create mode 100644 client/src/components/ui/toast.tsx create mode 100644 client/src/components/ui/toaster.tsx create mode 100644 client/src/components/ui/toggle-group.tsx create mode 100644 client/src/components/ui/toggle.tsx create mode 100644 client/src/components/ui/tooltip.tsx create mode 100644 client/src/hooks/use-mobile.tsx create mode 100644 client/src/hooks/use-toast.ts create mode 100644 client/src/hooks/useAuth.ts create mode 100644 client/src/index.css create mode 100644 client/src/lib/authUtils.ts create mode 100644 client/src/lib/queryClient.ts create mode 100644 client/src/lib/utils.ts create mode 100644 client/src/main.tsx create mode 100644 client/src/pages/Availability.tsx create mode 100644 client/src/pages/Events.tsx create mode 100644 client/src/pages/Home.tsx create mode 100644 client/src/pages/Landing.tsx create mode 100644 client/src/pages/Profile.tsx create mode 100644 client/src/pages/PublicDJProfile.tsx create mode 100644 client/src/pages/Schedule.tsx create mode 100644 client/src/pages/admin/AdminDashboard.tsx create mode 100644 client/src/pages/admin/AssignmentTool.tsx create mode 100644 client/src/pages/admin/EventTypes.tsx create mode 100644 client/src/pages/admin/ManageDJs.tsx create mode 100644 client/src/pages/admin/Templates.tsx create mode 100644 client/src/pages/not-found.tsx create mode 100644 components.json create mode 100644 drizzle.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 replit.md create mode 100644 server/db.ts create mode 100644 server/index.ts create mode 100644 server/replitAuth.ts create mode 100644 server/routes.ts create mode 100644 server/storage.ts create mode 100644 server/vite.ts create mode 100644 shared/schema.ts create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9ba7f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.DS_Store +server/public +vite.config.ts.* +*.tar.gz \ No newline at end of file diff --git a/.replit b/.replit index e69de29..51f344f 100644 --- a/.replit +++ b/.replit @@ -0,0 +1,36 @@ +modules = ["nodejs-20", "web", "postgresql-16"] +run = "npm run dev" +hidden = [".config", ".git", "generated-icon.png", "node_modules", "dist"] + +[nix] +channel = "stable-24_05" + +[deployment] +deploymentTarget = "autoscale" +build = ["npm", "run", "build"] +run = ["npm", "run", "start"] + +[[ports]] +localPort = 5000 +externalPort = 80 + +[workflows] +runButton = "Project" + +[[workflows.workflow]] +name = "Project" +mode = "parallel" +author = "agent" + +[[workflows.workflow.tasks]] +task = "workflow.run" +args = "Start application" + +[[workflows.workflow]] +name = "Start application" +author = "agent" + +[[workflows.workflow.tasks]] +task = "shell.exec" +args = "npm run dev" +waitForPort = 5000 diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..4b4d09e --- /dev/null +++ b/client/index.html @@ -0,0 +1,13 @@ + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..61e53b1 --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,110 @@ +import { Switch, Route } from "wouter"; +import { queryClient } from "./lib/queryClient"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { Toaster } from "@/components/ui/toaster"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { useAuth } from "@/hooks/useAuth"; +import { useEffect } from "react"; +import { useToast } from "@/hooks/use-toast"; +import { isUnauthorizedError } from "@/lib/authUtils"; + +// Pages +import NotFound from "@/pages/not-found"; +import Landing from "@/pages/Landing"; +import Home from "@/pages/Home"; +import Profile from "@/pages/Profile"; +import Events from "@/pages/Events"; +import Schedule from "@/pages/Schedule"; +import Availability from "@/pages/Availability"; +import AdminDashboard from "@/pages/admin/AdminDashboard"; +import ManageDJs from "@/pages/admin/ManageDJs"; +import EventTypes from "@/pages/admin/EventTypes"; +import Templates from "@/pages/admin/Templates"; +import AssignmentTool from "@/pages/admin/AssignmentTool"; +import PublicDJProfile from "@/pages/PublicDJProfile"; + +function ProtectedRoute({ children }: { children: React.ReactNode }) { + const { toast } = useToast(); + const { isAuthenticated, isLoading } = useAuth(); + + useEffect(() => { + if (!isLoading && !isAuthenticated) { + toast({ + title: "Unauthorized", + description: "You are logged out. Logging in again...", + variant: "destructive", + }); + setTimeout(() => { + window.location.href = "/api/login"; + }, 500); + return; + } + }, [isAuthenticated, isLoading, toast]); + + if (isLoading) { + return ( +
+
+
+ ); + } + + if (!isAuthenticated) { + return null; + } + + return <>{children}; +} + +function Router() { + const { isAuthenticated, isLoading } = useAuth(); + + if (isLoading) { + return ( +
+
+
+ ); + } + + return ( + + {/* Public routes */} + + + {/* Protected routes */} + {isAuthenticated ? ( + <> + + + + + + + + + + + + ) : ( + + )} + + {/* Fallback */} + + + ); +} + +function App() { + return ( + + + + + + + ); +} + +export default App; diff --git a/client/src/components/admin/AdminDashboard.tsx b/client/src/components/admin/AdminDashboard.tsx new file mode 100644 index 0000000..220722e --- /dev/null +++ b/client/src/components/admin/AdminDashboard.tsx @@ -0,0 +1,90 @@ +import { useQuery } from "@tanstack/react-query"; +import { Card, CardContent } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { PendingRequests } from "./PendingRequests"; +import { AssignmentTool } from "./AssignmentTool"; + +export function AdminDashboard() { + const { data: stats, isLoading } = useQuery({ + queryKey: ["/api/stats/admin"], + }); + + if (isLoading) { + return ( +
+
+ {[...Array(4)].map((_, i) => ( + + + + + + ))} +
+
+ + +
+
+ ); + } + + const statCards = [ + { + title: "Total DJs", + value: stats?.totalDJs || 0, + icon: "fas fa-users", + color: "blue", + }, + { + title: "Active Events", + value: stats?.activeEvents || 0, + icon: "fas fa-calendar-check", + color: "green", + }, + { + title: "Pending Requests", + value: stats?.pendingRequests || 0, + icon: "fas fa-exclamation-triangle", + color: "yellow", + }, + { + title: "This Month", + value: stats?.thisMonth || 0, + icon: "fas fa-chart-bar", + color: "purple", + }, + ]; + + return ( +
+
+

Admin Dashboard

+

Manage DJs, events, and system settings

+
+ +
+ {statCards.map((stat) => ( + + +
+
+

{stat.title}

+

{stat.value}

+
+
+ +
+
+
+
+ ))} +
+ +
+ + +
+
+ ); +} diff --git a/client/src/components/admin/AssignmentTool.tsx b/client/src/components/admin/AssignmentTool.tsx new file mode 100644 index 0000000..6c948b3 --- /dev/null +++ b/client/src/components/admin/AssignmentTool.tsx @@ -0,0 +1,170 @@ +import { useState } from "react"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Checkbox } from "@/components/ui/checkbox"; +import { useToast } from "@/hooks/use-toast"; +import { apiRequest } from "@/lib/queryClient"; +import { Wand2 } from "lucide-react"; + +export function AssignmentTool() { + const { toast } = useToast(); + const queryClient = useQueryClient(); + const [selectedTemplate, setSelectedTemplate] = useState(""); + const [startDate, setStartDate] = useState(""); + const [endDate, setEndDate] = useState(""); + const [randomize, setRandomize] = useState(true); + const [respectFrequency, setRespectFrequency] = useState(true); + const [checkAvailability, setCheckAvailability] = useState(true); + + const { data: templates } = useQuery({ + queryKey: ["/api/schedule-templates"], + }); + + const generateAssignmentsMutation = useMutation({ + mutationFn: async (data: any) => { + // TODO: Implement assignment generation logic + await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate API call + return { success: true, message: "Assignments generated successfully" }; + }, + onSuccess: () => { + toast({ + title: "Assignments generated", + description: "DJ assignments have been generated successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/events"] }); + }, + onError: (error) => { + toast({ + title: "Error", + description: "Failed to generate assignments", + variant: "destructive", + }); + }, + }); + + const handleGenerateAssignments = () => { + if (!selectedTemplate || !startDate || !endDate) { + toast({ + title: "Validation Error", + description: "Please fill in all required fields", + variant: "destructive", + }); + return; + } + + const data = { + templateId: parseInt(selectedTemplate), + startDate, + endDate, + options: { + randomize, + respectFrequency, + checkAvailability, + }, + }; + + generateAssignmentsMutation.mutate(data); + }; + + return ( + + + Quick Assignment Tool +

Assign DJs to schedule templates

+
+ +
+
+ + +
+ +
+
+ + setStartDate(e.target.value)} + /> +
+
+ + setEndDate(e.target.value)} + /> +
+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+
+
+
+ ); +} diff --git a/client/src/components/admin/DJManagement.tsx b/client/src/components/admin/DJManagement.tsx new file mode 100644 index 0000000..db00391 --- /dev/null +++ b/client/src/components/admin/DJManagement.tsx @@ -0,0 +1,263 @@ +import { useState } from "react"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Badge } from "@/components/ui/badge"; +import { DataTable } from "@/components/ui/data-table"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { useToast } from "@/hooks/use-toast"; +import { apiRequest } from "@/lib/queryClient"; +import { UserPlus, UserCheck, UserX, Shield, ShieldOff } from "lucide-react"; + +export function DJManagement() { + const { toast } = useToast(); + const queryClient = useQueryClient(); + const [showInviteModal, setShowInviteModal] = useState(false); + const [inviteName, setInviteName] = useState(""); + const [inviteEmail, setInviteEmail] = useState(""); + + const { data: djs, isLoading } = useQuery({ + queryKey: ["/api/users"], + }); + + const { data: invitations } = useQuery({ + queryKey: ["/api/invitations"], + }); + + const inviteDJMutation = useMutation({ + mutationFn: async (data: { name: string; email: string }) => { + await apiRequest("POST", "/api/invitations", data); + }, + onSuccess: () => { + toast({ + title: "Invitation sent", + description: "DJ invitation has been sent successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/invitations"] }); + setShowInviteModal(false); + setInviteName(""); + setInviteEmail(""); + }, + onError: (error) => { + toast({ + title: "Error", + description: error.message, + variant: "destructive", + }); + }, + }); + + const toggleUserStatusMutation = useMutation({ + mutationFn: async (data: { userId: string; action: "activate" | "deactivate" }) => { + const endpoint = data.action === "activate" ? "reactivate" : "deactivate"; + await apiRequest("POST", `/api/users/${data.userId}/${endpoint}`); + }, + onSuccess: () => { + toast({ + title: "Status updated", + description: "User status has been updated successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/users"] }); + }, + onError: (error) => { + toast({ + title: "Error", + description: error.message, + variant: "destructive", + }); + }, + }); + + const toggleAdminMutation = useMutation({ + mutationFn: async (data: { userId: string; action: "make-admin" | "remove-admin" }) => { + await apiRequest("POST", `/api/users/${data.userId}/${data.action}`); + }, + onSuccess: () => { + toast({ + title: "Admin status updated", + description: "User admin status has been updated successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/users"] }); + }, + onError: (error) => { + toast({ + title: "Error", + description: error.message, + variant: "destructive", + }); + }, + }); + + const handleInvite = () => { + if (!inviteName || !inviteEmail) { + toast({ + title: "Validation Error", + description: "Please fill in all fields", + variant: "destructive", + }); + return; + } + + inviteDJMutation.mutate({ name: inviteName, email: inviteEmail }); + }; + + const columns = [ + { + key: "displayName" as const, + header: "DJ Name", + cell: (dj: any) => ( +
+ {dj.displayName} +
+
{dj.displayName || dj.firstName}
+
{dj.email}
+
+
+ ), + }, + { + key: "role" as const, + header: "Role", + cell: (dj: any) => ( + + {dj.role} + + ), + }, + { + key: "isActive" as const, + header: "Status", + cell: (dj: any) => ( + + {dj.isActive ? "Active" : "Inactive"} + + ), + }, + { + key: "maxEventsPerMonth" as const, + header: "Max Events/Month", + }, + ]; + + const actions = (dj: any) => ( +
+ + + +
+ ); + + return ( +
+
+
+

DJ Management

+

Manage DJ accounts and invitations

+
+ + + + + + + + Invite New DJ + +
+
+ + setInviteName(e.target.value)} + placeholder="Enter DJ name" + /> +
+
+ + setInviteEmail(e.target.value)} + placeholder="Enter email address" + /> +
+
+ + +
+
+
+
+
+ + + + Active DJs + + + + + + + {invitations && invitations.length > 0 && ( + + + Pending Invitations + + +
+ {invitations.map((invitation: any) => ( +
+
+
{invitation.name}
+
{invitation.email}
+
+ Pending +
+ ))} +
+
+
+ )} +
+ ); +} diff --git a/client/src/components/admin/EventTypeManagement.tsx b/client/src/components/admin/EventTypeManagement.tsx new file mode 100644 index 0000000..fbae876 --- /dev/null +++ b/client/src/components/admin/EventTypeManagement.tsx @@ -0,0 +1,251 @@ +import { useState } from "react"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Badge } from "@/components/ui/badge"; +import { DataTable } from "@/components/ui/data-table"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { useToast } from "@/hooks/use-toast"; +import { apiRequest } from "@/lib/queryClient"; +import { Plus, Edit, Trash2 } from "lucide-react"; + +export function EventTypeManagement() { + const { toast } = useToast(); + const queryClient = useQueryClient(); + const [showModal, setShowModal] = useState(false); + const [selectedEventType, setSelectedEventType] = useState(null); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + + const { data: eventTypes, isLoading } = useQuery({ + queryKey: ["/api/event-types"], + }); + + const createEventTypeMutation = useMutation({ + mutationFn: async (data: { name: string; description: string }) => { + await apiRequest("POST", "/api/event-types", data); + }, + onSuccess: () => { + toast({ + title: "Event type created", + description: "Event type has been created successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/event-types"] }); + handleCloseModal(); + }, + onError: (error) => { + toast({ + title: "Error", + description: error.message, + variant: "destructive", + }); + }, + }); + + const updateEventTypeMutation = useMutation({ + mutationFn: async (data: { id: number; name: string; description: string }) => { + await apiRequest("PATCH", `/api/event-types/${data.id}`, { name: data.name, description: data.description }); + }, + onSuccess: () => { + toast({ + title: "Event type updated", + description: "Event type has been updated successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/event-types"] }); + handleCloseModal(); + }, + onError: (error) => { + toast({ + title: "Error", + description: error.message, + variant: "destructive", + }); + }, + }); + + const deleteEventTypeMutation = useMutation({ + mutationFn: async (id: number) => { + await apiRequest("DELETE", `/api/event-types/${id}`); + }, + onSuccess: () => { + toast({ + title: "Event type deleted", + description: "Event type has been deleted successfully.", + }); + queryClient.invalidateQueries({ queryKey: ["/api/event-types"] }); + }, + onError: (error) => { + toast({ + title: "Error", + description: error.message, + variant: "destructive", + }); + }, + }); + + const handleOpenModal = (eventType?: any) => { + setSelectedEventType(eventType); + setName(eventType?.name || ""); + setDescription(eventType?.description || ""); + setShowModal(true); + }; + + const handleCloseModal = () => { + setShowModal(false); + setSelectedEventType(null); + setName(""); + setDescription(""); + }; + + const handleSubmit = () => { + if (!name) { + toast({ + title: "Validation Error", + description: "Event type name is required", + variant: "destructive", + }); + return; + } + + if (selectedEventType) { + updateEventTypeMutation.mutate({ + id: selectedEventType.id, + name, + description, + }); + } else { + createEventTypeMutation.mutate({ name, description }); + } + }; + + const handleDelete = (id: number) => { + if (window.confirm("Are you sure you want to delete this event type?")) { + deleteEventTypeMutation.mutate(id); + } + }; + + const columns = [ + { + key: "name" as const, + header: "Name", + cell: (eventType: any) => ( +
{eventType.name}
+ ), + }, + { + key: "description" as const, + header: "Description", + cell: (eventType: any) => ( +
{eventType.description || "-"}
+ ), + }, + { + key: "isActive" as const, + header: "Status", + cell: (eventType: any) => ( + + {eventType.isActive ? "Active" : "Inactive"} + + ), + }, + ]; + + const actions = (eventType: any) => ( +
+ + +
+ ); + + return ( +
+
+
+

Event Types

+

Manage event categories and types

+
+ + + + + + + + + {selectedEventType ? "Edit Event Type" : "Create Event Type"} + + +
+
+ + setName(e.target.value)} + placeholder="Enter event type name" + /> +
+
+ +