admin: category drag-and-drop reorder + modal-based license key management
- Add sortOrder column to ProclamationCategory with migration - Implement drag-and-drop reorder in AdminTable with persist-to-DB - Refactor AdminTable to use $derived + dragOverride (no $effect sync) - Replace window.location.reload with invalidateAll for reorder - Convert license key create to modal with subscription level dropdown - Add LicenseKeyForm.svelte following the CategoryForm pattern - Add replace action (delete + generate new key with same level) - Remove deactivateOld checkbox; each key has its own level - Use use:enhance with update() to refresh list after create/replace
This commit is contained in:
@@ -8,7 +8,20 @@ import { error } from '@sveltejs/kit';
|
||||
const config = getModelConfig('license-keys')!;
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
return loadRecords(locals, config);
|
||||
const records = await loadRecords(locals, config);
|
||||
|
||||
const subscriptionLevels = await prisma.subscriptionLevel.findMany({
|
||||
select: { id: true, name: true },
|
||||
orderBy: { name: 'asc' }
|
||||
});
|
||||
|
||||
return {
|
||||
...records,
|
||||
subscriptionLevels: subscriptionLevels.map((s: { id: number; name: string }) => ({
|
||||
label: s.name,
|
||||
value: s.name
|
||||
}))
|
||||
};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
@@ -17,45 +30,39 @@ export const actions = {
|
||||
|
||||
const formData = await request.formData();
|
||||
const email = formData.get('email')?.toString();
|
||||
const deactivateOld = formData.get('deactivateOld') === 'on';
|
||||
const level = formData.get('level')?.toString();
|
||||
|
||||
if (!email) {
|
||||
return { success: false, error: 'Email is required' };
|
||||
}
|
||||
if (!level) {
|
||||
return { success: false, error: 'Subscription level is required' };
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { email } });
|
||||
if (!user) {
|
||||
return { success: false, error: 'No user found with that email' };
|
||||
}
|
||||
|
||||
// Deactivate old keys if requested
|
||||
if (deactivateOld) {
|
||||
await prisma.licenseKey.updateMany({
|
||||
where: { userId: user.id, active: true },
|
||||
data: { active: false }
|
||||
});
|
||||
}
|
||||
|
||||
// Generate 16-char hex UUID grouped as 4-4-4-4
|
||||
const hex = crypto.randomUUID().replace(/-/g, '').slice(0, 16).toUpperCase();
|
||||
const key = `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`;
|
||||
|
||||
const licenseKey = await prisma.licenseKey.create({
|
||||
await prisma.licenseKey.create({
|
||||
data: {
|
||||
key,
|
||||
active: true,
|
||||
level: 'standard',
|
||||
level,
|
||||
userId: user.id
|
||||
}
|
||||
});
|
||||
|
||||
// Send email with license key
|
||||
try {
|
||||
const profile = await prisma.profile.findUnique({ where: { userId: user.id } });
|
||||
await sendMail({
|
||||
to: email,
|
||||
subject: 'Your License Key',
|
||||
data: {
|
||||
firstName: user.name ?? 'Valued Customer',
|
||||
firstName: profile?.firstName ?? user.name ?? 'Valued Customer',
|
||||
licenseKey: key
|
||||
}
|
||||
});
|
||||
@@ -63,6 +70,32 @@ export const actions = {
|
||||
console.error('Failed to send license key email:', e);
|
||||
}
|
||||
|
||||
return { success: true, error: undefined };
|
||||
},
|
||||
replace: async ({ request, locals }) => {
|
||||
if (!locals.user) throw error(401, 'Unauthorized');
|
||||
|
||||
const formData = await request.formData();
|
||||
const keyId = formData.get('id')?.toString();
|
||||
if (!keyId) return { success: false, error: 'Missing key id' };
|
||||
|
||||
const existing = await prisma.licenseKey.findUnique({ where: { id: keyId } });
|
||||
if (!existing) return { success: false, error: 'License key not found' };
|
||||
|
||||
await prisma.licenseKey.delete({ where: { id: keyId } });
|
||||
|
||||
const hex = crypto.randomUUID().replace(/-/g, '').slice(0, 16).toUpperCase();
|
||||
const key = `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`;
|
||||
|
||||
await prisma.licenseKey.create({
|
||||
data: {
|
||||
key,
|
||||
active: true,
|
||||
level: existing.level,
|
||||
userId: existing.userId
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true, error: undefined };
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user