feat(02-01): add CRUD form actions
- Add create action with content validation (title optional, type defaults to thought) - Add update action with field-by-field updates and validation - Add delete action with existence check - Add toggleComplete action to toggle between open/done - Use getOrdered() in load function for tasks-first ordering - Update page component to use new data structure
This commit is contained in:
@@ -1,24 +1,129 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import type { PageServerLoad, Actions } from './$types';
|
||||
import { fail } from '@sveltejs/kit';
|
||||
import { entryRepository } from '$lib/server/db/repository';
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
const count = entryRepository.count();
|
||||
|
||||
// Create a test entry if none exist (for verification)
|
||||
let testEntry = null;
|
||||
if (count === 0) {
|
||||
testEntry = entryRepository.create({
|
||||
title: 'Foundation Test',
|
||||
content: 'This entry was created to verify the foundation is working.',
|
||||
type: 'thought'
|
||||
});
|
||||
}
|
||||
|
||||
const entries = entryRepository.getAll({ limit: 5 });
|
||||
const entries = entryRepository.getOrdered({ showCompleted: false });
|
||||
|
||||
return {
|
||||
dbStatus: 'connected',
|
||||
entryCount: testEntry ? count + 1 : count,
|
||||
recentEntries: entries
|
||||
entries
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
create: async ({ request }) => {
|
||||
const formData = await request.formData();
|
||||
const title = formData.get('title')?.toString().trim() || undefined;
|
||||
const content = formData.get('content')?.toString().trim() || '';
|
||||
const type = formData.get('type')?.toString() as 'task' | 'thought' | undefined;
|
||||
|
||||
// Validate content is not empty
|
||||
if (!content) {
|
||||
return fail(400, {
|
||||
error: 'Content is required',
|
||||
title: title || '',
|
||||
content: '',
|
||||
type: type || 'thought'
|
||||
});
|
||||
}
|
||||
|
||||
const entry = entryRepository.create({
|
||||
title,
|
||||
content,
|
||||
type: type || 'thought'
|
||||
});
|
||||
|
||||
return { success: true, entryId: entry.id };
|
||||
},
|
||||
|
||||
update: async ({ request }) => {
|
||||
const formData = await request.formData();
|
||||
const id = formData.get('id')?.toString();
|
||||
|
||||
if (!id) {
|
||||
return fail(400, { error: 'Entry ID is required' });
|
||||
}
|
||||
|
||||
const existing = entryRepository.getById(id);
|
||||
if (!existing) {
|
||||
return fail(404, { error: 'Entry not found' });
|
||||
}
|
||||
|
||||
// Collect fields to update
|
||||
const updates: Record<string, string | boolean | null> = {};
|
||||
|
||||
const title = formData.get('title');
|
||||
if (title !== null) {
|
||||
updates.title = title.toString().trim() || null;
|
||||
}
|
||||
|
||||
const content = formData.get('content');
|
||||
if (content !== null) {
|
||||
const contentStr = content.toString().trim();
|
||||
if (contentStr === '') {
|
||||
return fail(400, { error: 'Content cannot be empty' });
|
||||
}
|
||||
updates.content = contentStr;
|
||||
}
|
||||
|
||||
const type = formData.get('type');
|
||||
if (type !== null) {
|
||||
const typeStr = type.toString();
|
||||
if (typeStr !== 'task' && typeStr !== 'thought') {
|
||||
return fail(400, { error: 'Invalid type' });
|
||||
}
|
||||
updates.type = typeStr;
|
||||
}
|
||||
|
||||
const status = formData.get('status');
|
||||
if (status !== null) {
|
||||
const statusStr = status.toString();
|
||||
if (statusStr !== 'open' && statusStr !== 'done' && statusStr !== 'archived') {
|
||||
return fail(400, { error: 'Invalid status' });
|
||||
}
|
||||
updates.status = statusStr;
|
||||
}
|
||||
|
||||
entryRepository.update(id, updates);
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
delete: async ({ request }) => {
|
||||
const formData = await request.formData();
|
||||
const id = formData.get('id')?.toString();
|
||||
|
||||
if (!id) {
|
||||
return fail(400, { error: 'Entry ID is required' });
|
||||
}
|
||||
|
||||
const existing = entryRepository.getById(id);
|
||||
if (!existing) {
|
||||
return fail(404, { error: 'Entry not found' });
|
||||
}
|
||||
|
||||
entryRepository.delete(id);
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
toggleComplete: async ({ request }) => {
|
||||
const formData = await request.formData();
|
||||
const id = formData.get('id')?.toString();
|
||||
|
||||
if (!id) {
|
||||
return fail(400, { error: 'Entry ID is required' });
|
||||
}
|
||||
|
||||
const existing = entryRepository.getById(id);
|
||||
if (!existing) {
|
||||
return fail(404, { error: 'Entry not found' });
|
||||
}
|
||||
|
||||
// Toggle status between 'open' and 'done'
|
||||
const newStatus = existing.status === 'done' ? 'open' : 'done';
|
||||
entryRepository.update(id, { status: newStatus });
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user