Files
DJ-Management-Tool/server/storage.ts
T
spliceboti 89946fcef9 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
2025-07-09 23:54:32 +00:00

559 lines
18 KiB
TypeScript

import {
users,
events,
eventTypes,
scheduleTemplates,
scheduleTemplateSlots,
socialLinks,
availability,
slotEligibility,
removalRequests,
invitations,
type User,
type UpsertUser,
type InsertUser,
type UpdateUser,
type Event,
type InsertEvent,
type UpdateEvent,
type EventType,
type InsertEventType,
type UpdateEventType,
type ScheduleTemplate,
type InsertScheduleTemplate,
type UpdateScheduleTemplate,
type ScheduleTemplateSlot,
type InsertScheduleTemplateSlot,
type SocialLink,
type InsertSocialLink,
type Availability,
type InsertAvailability,
type SlotEligibility,
type InsertSlotEligibility,
type RemovalRequest,
type InsertRemovalRequest,
type UpdateRemovalRequest,
type Invitation,
type InsertInvitation,
} from "@shared/schema";
import { db } from "./db";
import { eq, and, gte, lte, desc, asc, sql, inArray } from "drizzle-orm";
export interface IStorage {
// User operations (IMPORTANT: mandatory for Replit Auth)
getUser(id: string): Promise<User | undefined>;
upsertUser(user: UpsertUser): Promise<User>;
updateUser(id: string, updates: UpdateUser): Promise<User>;
getAllDJs(): Promise<User[]>;
deactivateUser(id: string): Promise<void>;
reactivateUser(id: string): Promise<void>;
makeAdmin(id: string): Promise<void>;
removeAdmin(id: string): Promise<void>;
// Event operations
createEvent(event: InsertEvent): Promise<Event>;
getEvent(id: number): Promise<Event | undefined>;
getEventsByDJ(djId: string): Promise<Event[]>;
getUpcomingEvents(limit?: number): Promise<Event[]>;
getAllEvents(): Promise<Event[]>;
updateEvent(id: number, updates: UpdateEvent): Promise<Event>;
deleteEvent(id: number): Promise<void>;
getEventsByDateRange(startDate: string, endDate: string): Promise<Event[]>;
getEventsByMonth(year: number, month: number): Promise<Event[]>;
// Event type operations
createEventType(eventType: InsertEventType): Promise<EventType>;
getEventType(id: number): Promise<EventType | undefined>;
getAllEventTypes(): Promise<EventType[]>;
updateEventType(id: number, updates: UpdateEventType): Promise<EventType>;
deleteEventType(id: number): Promise<void>;
// Schedule template operations
createScheduleTemplate(template: InsertScheduleTemplate): Promise<ScheduleTemplate>;
getScheduleTemplate(id: number): Promise<ScheduleTemplate | undefined>;
getAllScheduleTemplates(): Promise<ScheduleTemplate[]>;
updateScheduleTemplate(id: number, updates: UpdateScheduleTemplate): Promise<ScheduleTemplate>;
deleteScheduleTemplate(id: number): Promise<void>;
// Schedule template slot operations
createScheduleTemplateSlot(slot: InsertScheduleTemplateSlot): Promise<ScheduleTemplateSlot>;
getScheduleTemplateSlots(templateId: number): Promise<ScheduleTemplateSlot[]>;
deleteScheduleTemplateSlot(id: number): Promise<void>;
// Social link operations
createSocialLink(link: InsertSocialLink): Promise<SocialLink>;
getSocialLinksByDJ(djId: string): Promise<SocialLink[]>;
updateSocialLink(id: number, updates: Partial<InsertSocialLink>): Promise<SocialLink>;
deleteSocialLink(id: number): Promise<void>;
// Availability operations
createAvailability(availability: InsertAvailability): Promise<Availability>;
getAvailabilityByDJ(djId: string): Promise<Availability[]>;
deleteAvailability(id: number): Promise<void>;
isAvailable(djId: string, date: string): Promise<boolean>;
// Slot eligibility operations
createSlotEligibility(eligibility: InsertSlotEligibility): Promise<SlotEligibility>;
getSlotEligibilityByDJ(djId: string): Promise<SlotEligibility[]>;
updateSlotEligibility(id: number, updates: Partial<InsertSlotEligibility>): Promise<SlotEligibility>;
deleteSlotEligibility(id: number): Promise<void>;
// Removal request operations
createRemovalRequest(request: InsertRemovalRequest): Promise<RemovalRequest>;
getRemovalRequest(id: number): Promise<RemovalRequest | undefined>;
getPendingRemovalRequests(): Promise<RemovalRequest[]>;
updateRemovalRequest(id: number, updates: UpdateRemovalRequest): Promise<RemovalRequest>;
approveRemovalRequest(id: number, reviewedBy: string): Promise<void>;
denyRemovalRequest(id: number, reviewedBy: string): Promise<void>;
// Invitation operations
createInvitation(invitation: InsertInvitation): Promise<Invitation>;
getInvitationByToken(token: string): Promise<Invitation | undefined>;
markInvitationAsUsed(id: number): Promise<void>;
getActiveInvitations(): Promise<Invitation[]>;
// Statistics
getDashboardStats(djId: string): Promise<{
upcomingEvents: number;
thisMonth: number;
pendingRequests: number;
totalEvents: number;
}>;
getAdminStats(): Promise<{
totalDJs: number;
activeEvents: number;
pendingRequests: number;
thisMonth: number;
}>;
}
export class DatabaseStorage implements IStorage {
// User operations
async getUser(id: string): Promise<User | undefined> {
const [user] = await db.select().from(users).where(eq(users.id, id));
return user;
}
async upsertUser(userData: UpsertUser): Promise<User> {
const [user] = await db
.insert(users)
.values(userData)
.onConflictDoUpdate({
target: users.id,
set: {
...userData,
updatedAt: new Date(),
},
})
.returning();
return user;
}
async updateUser(id: string, updates: UpdateUser): Promise<User> {
const [user] = await db
.update(users)
.set({ ...updates, updatedAt: new Date() })
.where(eq(users.id, id))
.returning();
return user;
}
async getAllDJs(): Promise<User[]> {
return await db.select().from(users).where(eq(users.role, "dj")).orderBy(asc(users.displayName));
}
async deactivateUser(id: string): Promise<void> {
await db.update(users).set({ isActive: false, updatedAt: new Date() }).where(eq(users.id, id));
}
async reactivateUser(id: string): Promise<void> {
await db.update(users).set({ isActive: true, updatedAt: new Date() }).where(eq(users.id, id));
}
async makeAdmin(id: string): Promise<void> {
await db.update(users).set({ role: "admin", updatedAt: new Date() }).where(eq(users.id, id));
}
async removeAdmin(id: string): Promise<void> {
await db.update(users).set({ role: "dj", updatedAt: new Date() }).where(eq(users.id, id));
}
// Event operations
async createEvent(event: InsertEvent): Promise<Event> {
const [newEvent] = await db.insert(events).values(event).returning();
return newEvent;
}
async getEvent(id: number): Promise<Event | undefined> {
const [event] = await db.select().from(events).where(eq(events.id, id));
return event;
}
async getEventsByDJ(djId: string): Promise<Event[]> {
return await db
.select()
.from(events)
.where(eq(events.djId, djId))
.orderBy(asc(events.date), asc(events.startTime));
}
async getUpcomingEvents(limit = 50): Promise<Event[]> {
const today = new Date().toISOString().split('T')[0];
return await db
.select()
.from(events)
.where(gte(events.date, today))
.orderBy(asc(events.date), asc(events.startTime))
.limit(limit);
}
async getAllEvents(): Promise<Event[]> {
return await db
.select()
.from(events)
.orderBy(desc(events.date), desc(events.startTime));
}
async updateEvent(id: number, updates: UpdateEvent): Promise<Event> {
const [event] = await db
.update(events)
.set({ ...updates, updatedAt: new Date() })
.where(eq(events.id, id))
.returning();
return event;
}
async deleteEvent(id: number): Promise<void> {
await db.delete(events).where(eq(events.id, id));
}
async getEventsByDateRange(startDate: string, endDate: string): Promise<Event[]> {
return await db
.select()
.from(events)
.where(and(gte(events.date, startDate), lte(events.date, endDate)))
.orderBy(asc(events.date), asc(events.startTime));
}
async getEventsByMonth(year: number, month: number): Promise<Event[]> {
const startDate = `${year}-${month.toString().padStart(2, '0')}-01`;
const endDate = `${year}-${month.toString().padStart(2, '0')}-31`;
return await this.getEventsByDateRange(startDate, endDate);
}
// Event type operations
async createEventType(eventType: InsertEventType): Promise<EventType> {
const [newEventType] = await db.insert(eventTypes).values(eventType).returning();
return newEventType;
}
async getEventType(id: number): Promise<EventType | undefined> {
const [eventType] = await db.select().from(eventTypes).where(eq(eventTypes.id, id));
return eventType;
}
async getAllEventTypes(): Promise<EventType[]> {
return await db.select().from(eventTypes).where(eq(eventTypes.isActive, true)).orderBy(asc(eventTypes.name));
}
async updateEventType(id: number, updates: UpdateEventType): Promise<EventType> {
const [eventType] = await db
.update(eventTypes)
.set(updates)
.where(eq(eventTypes.id, id))
.returning();
return eventType;
}
async deleteEventType(id: number): Promise<void> {
await db.update(eventTypes).set({ isActive: false }).where(eq(eventTypes.id, id));
}
// Schedule template operations
async createScheduleTemplate(template: InsertScheduleTemplate): Promise<ScheduleTemplate> {
const [newTemplate] = await db.insert(scheduleTemplates).values(template).returning();
return newTemplate;
}
async getScheduleTemplate(id: number): Promise<ScheduleTemplate | undefined> {
const [template] = await db.select().from(scheduleTemplates).where(eq(scheduleTemplates.id, id));
return template;
}
async getAllScheduleTemplates(): Promise<ScheduleTemplate[]> {
return await db.select().from(scheduleTemplates).where(eq(scheduleTemplates.isActive, true)).orderBy(asc(scheduleTemplates.name));
}
async updateScheduleTemplate(id: number, updates: UpdateScheduleTemplate): Promise<ScheduleTemplate> {
const [template] = await db
.update(scheduleTemplates)
.set(updates)
.where(eq(scheduleTemplates.id, id))
.returning();
return template;
}
async deleteScheduleTemplate(id: number): Promise<void> {
await db.update(scheduleTemplates).set({ isActive: false }).where(eq(scheduleTemplates.id, id));
}
// Schedule template slot operations
async createScheduleTemplateSlot(slot: InsertScheduleTemplateSlot): Promise<ScheduleTemplateSlot> {
const [newSlot] = await db.insert(scheduleTemplateSlots).values(slot).returning();
return newSlot;
}
async getScheduleTemplateSlots(templateId: number): Promise<ScheduleTemplateSlot[]> {
return await db
.select()
.from(scheduleTemplateSlots)
.where(eq(scheduleTemplateSlots.templateId, templateId))
.orderBy(asc(scheduleTemplateSlots.dayOfWeek), asc(scheduleTemplateSlots.startTime));
}
async deleteScheduleTemplateSlot(id: number): Promise<void> {
await db.delete(scheduleTemplateSlots).where(eq(scheduleTemplateSlots.id, id));
}
// Social link operations
async createSocialLink(link: InsertSocialLink): Promise<SocialLink> {
const [newLink] = await db.insert(socialLinks).values(link).returning();
return newLink;
}
async getSocialLinksByDJ(djId: string): Promise<SocialLink[]> {
return await db.select().from(socialLinks).where(eq(socialLinks.djId, djId));
}
async updateSocialLink(id: number, updates: Partial<InsertSocialLink>): Promise<SocialLink> {
const [link] = await db
.update(socialLinks)
.set(updates)
.where(eq(socialLinks.id, id))
.returning();
return link;
}
async deleteSocialLink(id: number): Promise<void> {
await db.delete(socialLinks).where(eq(socialLinks.id, id));
}
// Availability operations
async createAvailability(availabilityData: InsertAvailability): Promise<Availability> {
const [newAvailability] = await db.insert(availability).values(availabilityData).returning();
return newAvailability;
}
async getAvailabilityByDJ(djId: string): Promise<Availability[]> {
return await db
.select()
.from(availability)
.where(eq(availability.djId, djId))
.orderBy(asc(availability.startDate));
}
async deleteAvailability(id: number): Promise<void> {
await db.delete(availability).where(eq(availability.id, id));
}
async isAvailable(djId: string, date: string): Promise<boolean> {
const [unavailable] = await db
.select()
.from(availability)
.where(
and(
eq(availability.djId, djId),
lte(availability.startDate, date),
gte(availability.endDate, date)
)
)
.limit(1);
return !unavailable;
}
// Slot eligibility operations
async createSlotEligibility(eligibility: InsertSlotEligibility): Promise<SlotEligibility> {
const [newEligibility] = await db.insert(slotEligibility).values(eligibility).returning();
return newEligibility;
}
async getSlotEligibilityByDJ(djId: string): Promise<SlotEligibility[]> {
return await db.select().from(slotEligibility).where(eq(slotEligibility.djId, djId));
}
async updateSlotEligibility(id: number, updates: Partial<InsertSlotEligibility>): Promise<SlotEligibility> {
const [eligibility] = await db
.update(slotEligibility)
.set(updates)
.where(eq(slotEligibility.id, id))
.returning();
return eligibility;
}
async deleteSlotEligibility(id: number): Promise<void> {
await db.delete(slotEligibility).where(eq(slotEligibility.id, id));
}
// Removal request operations
async createRemovalRequest(request: InsertRemovalRequest): Promise<RemovalRequest> {
const [newRequest] = await db.insert(removalRequests).values(request).returning();
return newRequest;
}
async getRemovalRequest(id: number): Promise<RemovalRequest | undefined> {
const [request] = await db.select().from(removalRequests).where(eq(removalRequests.id, id));
return request;
}
async getPendingRemovalRequests(): Promise<RemovalRequest[]> {
return await db
.select()
.from(removalRequests)
.where(eq(removalRequests.status, "pending"))
.orderBy(asc(removalRequests.createdAt));
}
async updateRemovalRequest(id: number, updates: UpdateRemovalRequest): Promise<RemovalRequest> {
const [request] = await db
.update(removalRequests)
.set(updates)
.where(eq(removalRequests.id, id))
.returning();
return request;
}
async approveRemovalRequest(id: number, reviewedBy: string): Promise<void> {
await db
.update(removalRequests)
.set({
status: "approved",
reviewedBy,
reviewedAt: new Date(),
})
.where(eq(removalRequests.id, id));
}
async denyRemovalRequest(id: number, reviewedBy: string): Promise<void> {
await db
.update(removalRequests)
.set({
status: "denied",
reviewedBy,
reviewedAt: new Date(),
})
.where(eq(removalRequests.id, id));
}
// Invitation operations
async createInvitation(invitation: InsertInvitation): Promise<Invitation> {
const [newInvitation] = await db.insert(invitations).values(invitation).returning();
return newInvitation;
}
async getInvitationByToken(token: string): Promise<Invitation | undefined> {
const [invitation] = await db.select().from(invitations).where(eq(invitations.token, token));
return invitation;
}
async markInvitationAsUsed(id: number): Promise<void> {
await db.update(invitations).set({ isUsed: true }).where(eq(invitations.id, id));
}
async getActiveInvitations(): Promise<Invitation[]> {
return await db
.select()
.from(invitations)
.where(and(eq(invitations.isUsed, false), gte(invitations.expiresAt, new Date())))
.orderBy(desc(invitations.createdAt));
}
// Statistics
async getDashboardStats(djId: string): Promise<{
upcomingEvents: number;
thisMonth: number;
pendingRequests: number;
totalEvents: number;
}> {
const today = new Date().toISOString().split('T')[0];
const currentMonth = new Date().getMonth() + 1;
const currentYear = new Date().getFullYear();
const monthStart = `${currentYear}-${currentMonth.toString().padStart(2, '0')}-01`;
const monthEnd = `${currentYear}-${currentMonth.toString().padStart(2, '0')}-31`;
const [upcomingEvents] = await db
.select({ count: sql<number>`count(*)` })
.from(events)
.where(and(eq(events.djId, djId), gte(events.date, today)));
const [thisMonth] = await db
.select({ count: sql<number>`count(*)` })
.from(events)
.where(
and(
eq(events.djId, djId),
gte(events.date, monthStart),
lte(events.date, monthEnd)
)
);
const [pendingRequests] = await db
.select({ count: sql<number>`count(*)` })
.from(removalRequests)
.where(and(eq(removalRequests.djId, djId), eq(removalRequests.status, "pending")));
const [totalEvents] = await db
.select({ count: sql<number>`count(*)` })
.from(events)
.where(eq(events.djId, djId));
return {
upcomingEvents: upcomingEvents.count,
thisMonth: thisMonth.count,
pendingRequests: pendingRequests.count,
totalEvents: totalEvents.count,
};
}
async getAdminStats(): Promise<{
totalDJs: number;
activeEvents: number;
pendingRequests: number;
thisMonth: number;
}> {
const today = new Date().toISOString().split('T')[0];
const currentMonth = new Date().getMonth() + 1;
const currentYear = new Date().getFullYear();
const monthStart = `${currentYear}-${currentMonth.toString().padStart(2, '0')}-01`;
const monthEnd = `${currentYear}-${currentMonth.toString().padStart(2, '0')}-31`;
const [totalDJs] = await db
.select({ count: sql<number>`count(*)` })
.from(users)
.where(and(eq(users.role, "dj"), eq(users.isActive, true)));
const [activeEvents] = await db
.select({ count: sql<number>`count(*)` })
.from(events)
.where(gte(events.date, today));
const [pendingRequests] = await db
.select({ count: sql<number>`count(*)` })
.from(removalRequests)
.where(eq(removalRequests.status, "pending"));
const [thisMonth] = await db
.select({ count: sql<number>`count(*)` })
.from(events)
.where(and(gte(events.date, monthStart), lte(events.date, monthEnd)));
return {
totalDJs: totalDJs.count,
activeEvents: activeEvents.count,
pendingRequests: pendingRequests.count,
thisMonth: thisMonth.count,
};
}
}
export const storage = new DatabaseStorage();