2025-03-25 15:25:04 +01:00
|
|
|
import type { PageServerLoad, Actions } from "./$types";
|
|
|
|
|
import type { SQLiteError } from "bun:sqlite";
|
|
|
|
|
|
|
|
|
|
import type { RecordEntry, EstimatesEntry } from "$lib/db_types";
|
2025-01-03 01:04:30 +01:00
|
|
|
|
|
|
|
|
import { fail, redirect } from '@sveltejs/kit';
|
|
|
|
|
|
2025-03-25 15:25:04 +01:00
|
|
|
import { MONTHS, toInt, parseDate, isTimeValidHHMM, month_of } from "$lib/util"
|
2025-01-03 01:04:30 +01:00
|
|
|
import { get_user, User } from "$lib/server/database";
|
2025-03-25 15:25:04 +01:00
|
|
|
import { getRecordFiles } from "$lib/server/docstore";
|
|
|
|
|
|
|
|
|
|
export const load: PageServerLoad = async ({ locals }) => {
|
|
|
|
|
let records: { entry: RecordEntry, parsedDate: Date } = locals.user.get_entries().map((v) => ({ entry: v, parsedDate: parseDate(v.date) }));
|
|
|
|
|
let estimates: EstimatesEntry = locals.user.get_estimates()
|
|
|
|
|
let documents = (await getRecordFiles(locals.user)).map((v) => { return { year: toInt(v.identifier.substring(0, 4)), month: toInt(v.identifier.substring(5, 7)), file: v }; });
|
|
|
|
|
|
|
|
|
|
let records_grouped = Map.groupBy(records, (v) => { return v.parsedDate.getFullYear() } );
|
|
|
|
|
|
|
|
|
|
records_grouped.forEach((value, key, map) => {
|
|
|
|
|
let m = Map.groupBy(value, (v) => v.parsedDate.getMonth());
|
|
|
|
|
// remove parsed date
|
|
|
|
|
m.forEach((value, key, map) => {
|
|
|
|
|
map.set(key, value.map((v) => v.entry));
|
|
|
|
|
});
|
|
|
|
|
map.set(key, m);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let estimates_grouped = Map.groupBy(estimates, (v) => { return v.year });
|
|
|
|
|
estimates_grouped.forEach((value, key, map) => {
|
|
|
|
|
let arr = value.map((v) => [
|
|
|
|
|
{ key: ((v.quarter - 1) * 3 + 2), value: v["estimate_2"] },
|
|
|
|
|
{ key: ((v.quarter - 1) * 3 + 1), value: v["estimate_1"] },
|
|
|
|
|
{ key: ((v.quarter - 1) * 3 + 0), value: v["estimate_0"] },
|
|
|
|
|
]).flat();
|
|
|
|
|
|
|
|
|
|
let m = Map.groupBy(arr, (e) => e.key);
|
|
|
|
|
m.forEach((value, key, map) => {
|
|
|
|
|
map.set(key, value[0].value);
|
|
|
|
|
})
|
|
|
|
|
map.set(key, m);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
let documents_grouped = Map.groupBy(documents, (v) => v.year );
|
|
|
|
|
documents_grouped.forEach((value, key, map) => {
|
|
|
|
|
let m = Map.groupBy(value, (v) => v.month);
|
|
|
|
|
m.forEach((value, key, map) => {
|
|
|
|
|
map.set(key, value.map((v) => v.file)[0]);
|
|
|
|
|
})
|
|
|
|
|
map.set(key, m);
|
|
|
|
|
})
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
return {
|
2025-03-25 15:25:04 +01:00
|
|
|
records: records_grouped,
|
|
|
|
|
estimates: estimates_grouped,
|
|
|
|
|
documents: documents_grouped
|
2025-01-03 01:04:30 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const actions = {
|
|
|
|
|
new_entry: async ({locals, request}) => {
|
|
|
|
|
|
|
|
|
|
const ok = "ok";
|
|
|
|
|
const missing = "missing";
|
|
|
|
|
const invalid = "invalid";
|
|
|
|
|
|
|
|
|
|
const data = await request.formData();
|
|
|
|
|
|
|
|
|
|
let date = data.get("date");
|
|
|
|
|
let start = data.get("start");
|
|
|
|
|
let end = data.get("end");
|
|
|
|
|
let comment = data.get("comment");
|
|
|
|
|
|
|
|
|
|
let return_obj = {
|
|
|
|
|
date : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: date
|
|
|
|
|
},
|
|
|
|
|
start : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: start
|
|
|
|
|
},
|
|
|
|
|
end : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: end
|
|
|
|
|
},
|
|
|
|
|
comment : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: comment
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (date == null) {
|
|
|
|
|
return_obj.date.status = missing;
|
|
|
|
|
} else if (parseDate(date) == null) {
|
|
|
|
|
return_obj.date.status = invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (start == null) {
|
|
|
|
|
return_obj.start.status = missing;
|
|
|
|
|
} else if (!isTimeValidHHMM(start)) {
|
|
|
|
|
return_obj.start.status = invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (end == null) {
|
|
|
|
|
return_obj.end.status = missing;
|
|
|
|
|
} else if (!isTimeValidHHMM(end)) {
|
|
|
|
|
return_obj.end.status = invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Object.keys(return_obj).some((v) => { return return_obj[v].status != ok; })) {
|
|
|
|
|
return fail(400, { new_entry: return_obj });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let res = locals.user.insert_entry(date, start, end, comment);
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
return fail(500, { })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { new_entry: { success: true } };
|
|
|
|
|
},
|
|
|
|
|
edit_entry: async ({locals, request}) => {
|
|
|
|
|
|
|
|
|
|
const ok = "ok";
|
|
|
|
|
const missing = "missing";
|
|
|
|
|
const invalid = "invalid";
|
|
|
|
|
|
|
|
|
|
const data = await request.formData();
|
|
|
|
|
|
|
|
|
|
let id = data.get("id");
|
|
|
|
|
let date = data.get("date");
|
|
|
|
|
let start = data.get("start");
|
|
|
|
|
let end = data.get("end");
|
|
|
|
|
let comment = data.get("comment");
|
|
|
|
|
|
|
|
|
|
let return_obj = {
|
|
|
|
|
id: {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: id,
|
|
|
|
|
},
|
|
|
|
|
date : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: date
|
|
|
|
|
},
|
|
|
|
|
start : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: start
|
|
|
|
|
},
|
|
|
|
|
end : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: end
|
|
|
|
|
},
|
|
|
|
|
comment : {
|
|
|
|
|
status: ok,
|
|
|
|
|
value: comment
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (id == null) {
|
|
|
|
|
return_obj.id.status = missing;
|
|
|
|
|
} else if (isNaN(toInt(id))) {
|
|
|
|
|
return_obj.id.status = invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (date == null) {
|
|
|
|
|
return_obj.date.status = missing;
|
|
|
|
|
} else if (parseDate(date) == null) {
|
|
|
|
|
return_obj.date.status = invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (start == null) {
|
|
|
|
|
return_obj.start.status = missing;
|
|
|
|
|
} else if (!isTimeValidHHMM(start)) {
|
|
|
|
|
return_obj.start.status = invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (end == null) {
|
|
|
|
|
return_obj.end.status = missing;
|
|
|
|
|
} else if (!isTimeValidHHMM(end)) {
|
|
|
|
|
return_obj.end.status = invalid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Object.keys(return_obj).some((v) => { return return_obj[v].status != ok; })) {
|
|
|
|
|
return fail(400, { edit_entry: return_obj });
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 15:25:04 +01:00
|
|
|
id = toInt(id);
|
|
|
|
|
|
2025-01-03 01:04:30 +01:00
|
|
|
let current = locals.user.get_entry(id);
|
|
|
|
|
|
|
|
|
|
if (!current) {
|
|
|
|
|
return fail(404, { new_entry: return_obj });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current.id == id && current.date == date && current.start == start && current.end == end && current.comment == comment) {
|
|
|
|
|
return { success: false, edit_entry: { return_obj } }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let res = false;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
res = locals.user.update_entry(id, date, start, end, comment);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (!(e instanceof SQLiteError)) {
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
return fail(500, { })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
redirect(303, '/');
|
|
|
|
|
return { success: true };
|
|
|
|
|
},
|
2025-03-25 15:25:04 +01:00
|
|
|
remove_entry: async ({ locals, request })=> {
|
|
|
|
|
const data = await request.formData();
|
|
|
|
|
|
|
|
|
|
let id = data.get("id");
|
|
|
|
|
|
|
|
|
|
if (id == null) {
|
|
|
|
|
return fail(400, { id: id });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
id = toInt(id);
|
|
|
|
|
if (isNaN(id)) {
|
|
|
|
|
return fail(400, { id: id });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let res = false;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
res = locals.user.remove_entry(id);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (!(e instanceof SQLiteError)) {
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
return fail(500, { });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { success: true }
|
|
|
|
|
}
|
|
|
|
|
} satisfies Actions;
|