177 lines
4.7 KiB
TypeScript
177 lines
4.7 KiB
TypeScript
'use server';
|
|
|
|
import { db } from '@/lib/db';
|
|
import {
|
|
releases,
|
|
stepTemplates,
|
|
customerSteps,
|
|
customers,
|
|
type ReleaseType,
|
|
type ReleaseStatus,
|
|
} from '@/lib/db/schema';
|
|
import { eq, and, desc } from 'drizzle-orm';
|
|
import { revalidatePath } from 'next/cache';
|
|
|
|
export type ReleaseInput = {
|
|
name: string;
|
|
type: ReleaseType;
|
|
versionNumber?: string;
|
|
releaseDate?: Date;
|
|
description?: string;
|
|
};
|
|
|
|
export async function createRelease(data: ReleaseInput) {
|
|
const [release] = await db.insert(releases).values({
|
|
...data,
|
|
releaseDate: data.releaseDate || null,
|
|
status: 'draft',
|
|
}).returning();
|
|
revalidatePath('/releases');
|
|
return release;
|
|
}
|
|
|
|
export async function updateRelease(id: number, data: Partial<ReleaseInput>) {
|
|
const [release] = await db
|
|
.update(releases)
|
|
.set({ ...data, updatedAt: new Date() })
|
|
.where(eq(releases.id, id))
|
|
.returning();
|
|
revalidatePath('/releases');
|
|
revalidatePath(`/releases/${id}`);
|
|
return release;
|
|
}
|
|
|
|
export async function activateRelease(id: number) {
|
|
const release = await db.query.releases.findFirst({
|
|
where: eq(releases.id, id),
|
|
with: { templates: true },
|
|
});
|
|
|
|
if (!release) throw new Error('Release not found');
|
|
if (release.status !== 'draft') throw new Error('Release is not in draft status');
|
|
|
|
const activeCustomers = await db.query.customers.findMany({
|
|
where: eq(customers.isActive, true),
|
|
});
|
|
|
|
// Create customer steps from templates
|
|
const customerStepsToInsert = activeCustomers.flatMap(customer =>
|
|
release.templates.map(template => ({
|
|
releaseId: id,
|
|
customerId: customer.id,
|
|
templateId: template.id,
|
|
name: template.name,
|
|
category: template.category,
|
|
type: template.type,
|
|
content: template.content,
|
|
orderIndex: template.orderIndex,
|
|
status: 'pending' as const,
|
|
isCustom: false,
|
|
isOverridden: false,
|
|
}))
|
|
);
|
|
|
|
if (customerStepsToInsert.length > 0) {
|
|
await db.insert(customerSteps).values(customerStepsToInsert);
|
|
}
|
|
|
|
await db.update(releases)
|
|
.set({ status: 'active', updatedAt: new Date() })
|
|
.where(eq(releases.id, id));
|
|
|
|
revalidatePath('/releases');
|
|
revalidatePath(`/releases/${id}`);
|
|
}
|
|
|
|
export async function archiveRelease(id: number) {
|
|
await db.update(releases)
|
|
.set({ status: 'archived', updatedAt: new Date() })
|
|
.where(eq(releases.id, id));
|
|
revalidatePath('/releases');
|
|
revalidatePath(`/releases/${id}`);
|
|
}
|
|
|
|
export async function deleteRelease(id: number) {
|
|
await db.delete(releases).where(eq(releases.id, id));
|
|
revalidatePath('/releases');
|
|
}
|
|
|
|
export async function listReleases() {
|
|
return db.query.releases.findMany({
|
|
orderBy: desc(releases.createdAt),
|
|
});
|
|
}
|
|
|
|
export async function getReleaseById(id: number) {
|
|
return db.query.releases.findFirst({
|
|
where: eq(releases.id, id),
|
|
with: {
|
|
templates: {
|
|
orderBy: [stepTemplates.category, stepTemplates.orderIndex],
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function getActiveReleases() {
|
|
return db.query.releases.findMany({
|
|
where: eq(releases.status, 'active'),
|
|
orderBy: desc(releases.createdAt),
|
|
});
|
|
}
|
|
|
|
export async function cloneRelease(id: number, newName: string) {
|
|
const original = await db.query.releases.findFirst({
|
|
where: eq(releases.id, id),
|
|
with: { templates: true },
|
|
});
|
|
|
|
if (!original) throw new Error('Release not found');
|
|
|
|
// Create new release
|
|
const [newRelease] = await db.insert(releases).values({
|
|
name: newName,
|
|
type: original.type,
|
|
status: 'draft',
|
|
versionNumber: original.versionNumber,
|
|
description: `Cloned from: ${original.name}\n\n${original.description || ''}`,
|
|
}).returning();
|
|
|
|
// Clone templates
|
|
if (original.templates.length > 0) {
|
|
await db.insert(stepTemplates).values(
|
|
original.templates.map(t => ({
|
|
releaseId: newRelease.id,
|
|
name: t.name,
|
|
category: t.category,
|
|
type: t.type,
|
|
content: t.content,
|
|
orderIndex: t.orderIndex,
|
|
description: t.description,
|
|
}))
|
|
);
|
|
}
|
|
|
|
revalidatePath('/releases');
|
|
return newRelease;
|
|
}
|
|
|
|
export async function getReleaseStats() {
|
|
const allReleases = await db.query.releases.findMany();
|
|
const activeReleases = allReleases.filter(r => r.status === 'active');
|
|
|
|
// Count total customer steps by status
|
|
const allSteps = await db.query.customerSteps.findMany();
|
|
const pendingSteps = allSteps.filter(s => s.status === 'pending').length;
|
|
const doneSteps = allSteps.filter(s => s.status === 'done').length;
|
|
const skippedSteps = allSteps.filter(s => s.status === 'skipped').length;
|
|
|
|
return {
|
|
totalReleases: allReleases.length,
|
|
activeReleases: activeReleases.length,
|
|
pendingSteps,
|
|
doneSteps,
|
|
skippedSteps,
|
|
};
|
|
}
|