feat(isac): add specialization data management

This commit is contained in:
JB
2026-07-26 16:20:04 -04:00
parent ed31748102
commit 6722665f5c
25 changed files with 3369 additions and 25 deletions
@@ -10,11 +10,59 @@ import type { Actions, PageServerLoad } from './$types';
import { createNotification } from '$lib/server/notifications';
const statuses = ['pending', 'approved', 'rejected', 'failed'];
const modules = ['configuration', 'attributes', 'talents', 'brands', 'gear', 'weapons', 'weaponmods', 'gearmods', 'protocols', 'skillchips'];
const modules = ['configuration', 'attributes', 'talents', 'brands', 'gear', 'weapons', 'weaponmods', 'gearmods', 'protocols', 'skillchips', 'specializations'];
type SerializedEntry =
| { name: string; kind: 'text'; value: string }
| { name: string; kind: 'file'; value: string; fileName: string; type: string };
async function specializationProposalSnapshot(database: any, proposal: any) {
const id = String(proposal.entity_id ?? '');
if (proposal.action === 'saveProgression') {
return {
record: await (await database.prepare("SELECT * FROM specialization_progression_config WHERE id='default'")).get(),
rankProfiles: await (await database.prepare('SELECT * FROM specialization_rank_cost_profiles ORDER BY rank')).all()
};
}
if (!id) return null;
if (proposal.action === 'saveSpecialization') {
return { record: await (await database.prepare('SELECT * FROM specializations WHERE id=?')).get(id) ?? null };
}
if (proposal.action === 'saveFocus') {
return { record: await (await database.prepare('SELECT * FROM specialization_focuses WHERE id=?')).get(id) ?? null };
}
if (proposal.action === 'saveAbility') {
return {
record: await (await database.prepare('SELECT * FROM specialization_abilities WHERE id=?')).get(id) ?? null,
effects: await (await database.prepare(
'SELECT * FROM specialization_ability_effects WHERE ability_id=? ORDER BY effect_order'
)).all(id)
};
}
if (proposal.action === 'saveFocusAbilities') {
return {
record: await (await database.prepare('SELECT * FROM specialization_focuses WHERE id=?')).get(id) ?? null,
abilities: await (await database.prepare(
'SELECT ability_slot,ability_id FROM specialization_focus_abilities WHERE focus_id=? ORDER BY ability_slot'
)).all(id)
};
}
if (proposal.action === 'saveNode') {
return {
record: await (await database.prepare('SELECT * FROM specialization_tree_nodes WHERE id=?')).get(id) ?? null,
ranks: await (await database.prepare(
'SELECT * FROM specialization_tree_node_ranks WHERE node_id=? ORDER BY rank'
)).all(id),
effects: await (await database.prepare(
'SELECT * FROM specialization_tree_rank_effects WHERE node_id=? ORDER BY rank,effect_order'
)).all(id),
costs: await (await database.prepare(
'SELECT * FROM specialization_tree_rank_costs WHERE node_id=? ORDER BY rank,resource_id'
)).all(id)
};
}
return null;
}
export const load: PageServerLoad = async ({ cookies, url }) => {
await requirePermission(await getSessionUser(cookies), 'data.approve');
const database = await getDatabase();
@@ -40,7 +88,9 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
...proposal,
current_snapshot: proposal.module === 'attributes' && proposal.entity_id
? await (await database.prepare('SELECT * FROM stat_attributes WHERE id=?')).get(String(proposal.entity_id)) ?? null
: null
: proposal.module === 'specializations'
? await specializationProposalSnapshot(database, proposal)
: null
})));
const summaryRows = await (await database.prepare('SELECT status,COUNT(*) AS count FROM data_change_proposals GROUP BY status')).all();
return { proposals: proposalsWithSnapshots, pagination, status, module, search, summary: Object.fromEntries(summaryRows.map((row) => [String(row.status), Number(row.count)])), modules };
+1 -1
View File
@@ -55,7 +55,7 @@
<div class="dashboard-row moderation-row table-heading"><span>CHANGE</span><span>CONTRIBUTOR</span><span>SUBMITTED</span><span>STATUS</span><span></span></div>
{#each data.proposals as proposal}
<div class="dashboard-row moderation-row mobile-detail-row">
<div class="change-identity"><span>{proposal.module === 'skillchips' ? 'CHIP' : proposal.module.slice(0,4).toUpperCase()}</span><div><strong>{proposal.summary}</strong><small>{moduleLabel(proposal.module)} / {operationLabel(proposal.action)}</small></div></div>
<div class="change-identity"><span>{proposal.module === 'skillchips' ? 'CHIP' : proposal.module === 'specializations' ? 'CLASS' : proposal.module.slice(0,4).toUpperCase()}</span><div><strong>{proposal.summary}</strong><small>{moduleLabel(proposal.module)} / {operationLabel(proposal.action)}</small></div></div>
<div><strong>{proposal.proposer_name}</strong><small>AGENT {String(proposal.proposed_by).slice(0,8).toUpperCase()}</small></div>
<span>{date(proposal.created_at)}</span>
<span class="proposal-state" class:pending={proposal.status === 'pending'} class:approved={proposal.status === 'approved'} class:declined={proposal.status === 'rejected' || proposal.status === 'failed'}><i></i>{proposal.status.toUpperCase()}</span>