feat(isac): add specialization data management
This commit is contained in:
@@ -35,6 +35,70 @@
|
||||
};
|
||||
return proposed[names[key] ?? key];
|
||||
};
|
||||
const specializationKind = $derived(
|
||||
action === 'saveProgression' ? 'PROGRESSION PROFILE'
|
||||
: action === 'saveSpecialization' ? 'SPECIALIZATION'
|
||||
: action === 'saveFocus' ? 'FOCUS'
|
||||
: action === 'saveAbility' ? 'CLASS ABILITY'
|
||||
: action === 'saveFocusAbilities' ? 'FOCUS ABILITY SET'
|
||||
: 'TREE NODE'
|
||||
);
|
||||
const specializationRecord = $derived((currentSnapshot?.record ?? null) as Record<string, unknown> | null);
|
||||
const specializationFields = $derived(
|
||||
action === 'saveProgression'
|
||||
? [
|
||||
{ label: 'XP POINTS', current: specializationRecord?.xp_points, proposed: proposed.xpPoints },
|
||||
{ label: 'SHD TECH CACHE POINTS', current: specializationRecord?.shd_cache_points, proposed: proposed.cachePoints }
|
||||
]
|
||||
: action === 'saveSpecialization'
|
||||
? [
|
||||
{ label: 'NAME', current: specializationRecord?.name, proposed: proposed.name },
|
||||
{ label: 'DESCRIPTION', current: specializationRecord?.description, proposed: proposed.description }
|
||||
]
|
||||
: action === 'saveFocus'
|
||||
? [
|
||||
{ label: 'NAME', current: specializationRecord?.name, proposed: proposed.name },
|
||||
{ label: 'SPECIALIZATION', current: specializationRecord?.specialization_id, proposed: proposed.specializationId },
|
||||
{ label: 'PRIMARY ATTRIBUTE', current: specializationRecord?.primary_attribute, proposed: proposed.primaryAttribute },
|
||||
{ label: 'DISPLAY ORDER', current: specializationRecord?.display_order, proposed: proposed.displayOrder },
|
||||
{ label: 'DESCRIPTION', current: specializationRecord?.description, proposed: proposed.description }
|
||||
]
|
||||
: action === 'saveAbility'
|
||||
? [
|
||||
{ label: 'NAME', current: specializationRecord?.name, proposed: proposed.name },
|
||||
{ label: 'SPECIALIZATION', current: specializationRecord?.specialization_id, proposed: proposed.specializationId },
|
||||
{ label: 'DURATION · SECONDS', current: specializationRecord?.duration_seconds, proposed: proposed.durationSeconds },
|
||||
{ label: 'COOLDOWN · SECONDS', current: specializationRecord?.cooldown_seconds, proposed: proposed.cooldownSeconds },
|
||||
{ label: 'DESCRIPTION', current: specializationRecord?.description, proposed: proposed.description }
|
||||
]
|
||||
: action === 'saveNode'
|
||||
? [
|
||||
{ label: 'NAME', current: specializationRecord?.name, proposed: proposed.name },
|
||||
{ label: 'FOCUS', current: specializationRecord?.focus_id, proposed: proposed.focusId },
|
||||
{ label: 'ROW', current: specializationRecord?.row_number, proposed: proposed.rowNumber },
|
||||
{ label: 'COLUMN', current: specializationRecord?.column_number, proposed: proposed.columnNumber },
|
||||
{ label: 'MAXIMUM RANK', current: specializationRecord?.max_rank, proposed: proposed.maxRank },
|
||||
{ label: 'DESCRIPTION TEMPLATE', current: specializationRecord?.description_template, proposed: proposed.descriptionTemplate }
|
||||
]
|
||||
: [
|
||||
{ label: 'FOCUS', current: specializationRecord?.name ?? specializationRecord?.id, proposed: proposed.id },
|
||||
{ label: 'ABILITY SLOT 1 · TREE', current: (currentSnapshot?.abilities as any[])?.find((item) => Number(item.ability_slot) === 1)?.ability_id, proposed: proposed.abilityId1 },
|
||||
{ label: 'ABILITY SLOT 2 · COMBAT SKILLS', current: (currentSnapshot?.abilities as any[])?.find((item) => Number(item.ability_slot) === 2)?.ability_id, proposed: proposed.abilityId2 },
|
||||
{ label: 'ABILITY SLOT 3 · TREE', current: (currentSnapshot?.abilities as any[])?.find((item) => Number(item.ability_slot) === 3)?.ability_id, proposed: proposed.abilityId3 }
|
||||
]
|
||||
);
|
||||
const proposedEffects = $derived((jsonValue(proposed.effectsJson) ?? []) as any[]);
|
||||
const proposedRanks = $derived((jsonValue(proposed.ranksJson) ?? []) as any[]);
|
||||
const currentAbilityEffects = $derived((currentSnapshot?.effects ?? []) as any[]);
|
||||
const currentNodeRanks = $derived((currentSnapshot?.ranks ?? []) as any[]);
|
||||
const currentNodeEffects = $derived((currentSnapshot?.effects ?? []) as any[]);
|
||||
const currentNodeCosts = $derived((currentSnapshot?.costs ?? []) as any[]);
|
||||
const currentRankProfiles = $derived((currentSnapshot?.rankProfiles ?? []) as any[]);
|
||||
const effectSummary = (effect: any) => {
|
||||
const target = effect.statId || effect.stat_attribute_id || effect.abilityId || effect.ability_id || effect.label || 'CUSTOM EFFECT';
|
||||
const value = effect.value === null || effect.value === undefined || effect.value === '' ? '' : `${effect.value}${effect.unit ? ` ${effect.unit}` : ''}`;
|
||||
return `${target}${value ? ` · ${value}` : ''}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="proposal-change-preview" aria-label="Proposed data comparison">
|
||||
@@ -50,6 +114,7 @@
|
||||
{#if currentSnapshot}
|
||||
<div class="proposal-attribute-grid">
|
||||
<div class="preview-field identity"><span>NAME</span><strong>{shown(attributeValue('current', 'name'))}</strong></div>
|
||||
<div class="preview-field description"><span>DESCRIPTION · OPTIONAL</span><strong>{shown(attributeValue('current', 'description'))}</strong></div>
|
||||
{#each [['type','TYPE'],['category','CATEGORY'],['calculation_type','CALCULATION'],['unit','UNIT']] as field}
|
||||
<div class="preview-field"><span>{field[1]}</span><strong>{shown(attributeValue('current', field[0]))}</strong></div>
|
||||
{/each}
|
||||
@@ -66,6 +131,7 @@
|
||||
<header><span>PROPOSED DEFINITION</span><b>AFTER</b></header>
|
||||
<div class="proposal-attribute-grid">
|
||||
<div class="preview-field identity"><span>NAME</span><strong>{shown(attributeValue('proposed', 'name'))}</strong></div>
|
||||
<div class="preview-field description"><span>DESCRIPTION · OPTIONAL</span><strong>{shown(attributeValue('proposed', 'description'))}</strong></div>
|
||||
{#each [['type','TYPE'],['category','CATEGORY'],['calculation_type','CALCULATION'],['unit','UNIT']] as field}
|
||||
<div class="preview-field"><span>{field[1]}</span><strong>{shown(attributeValue('proposed', field[0]))}</strong></div>
|
||||
{/each}
|
||||
@@ -76,6 +142,70 @@
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
{:else if module === 'specializations' && ['saveProgression','saveSpecialization','saveFocus','saveAbility','saveFocusAbilities','saveNode'].includes(action)}
|
||||
<div class="proposal-compare-grid specialization-proposal-preview">
|
||||
<article class="proposal-editor-preview before">
|
||||
<header><span>CURRENT LIVE · {specializationKind}</span><b>{specializationRecord ? 'BEFORE' : 'NO LIVE RECORD'}</b></header>
|
||||
{#if specializationRecord}
|
||||
<div class="proposal-field-summary specialization-fields">
|
||||
{#each specializationFields as field}
|
||||
<div><span>{field.label}</span><strong>{shown(field.current)}</strong></div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if action === 'saveAbility'}
|
||||
<div class="proposal-structured-list">
|
||||
<header><span>STRUCTURED EFFECTS</span><b>{currentAbilityEffects.length}</b></header>
|
||||
{#each currentAbilityEffects as effect}<p>{effectSummary(effect)}</p>{:else}<p>NO STRUCTURED EFFECTS</p>{/each}
|
||||
</div>
|
||||
{:else if action === 'saveNode'}
|
||||
<div class="proposal-rank-preview">
|
||||
{#each currentNodeRanks as rank}
|
||||
<section><header><span>RANK {rank.rank}</span><b>{currentNodeEffects.filter((effect) => Number(effect.rank) === Number(rank.rank)).length} EFFECTS</b></header><p>{rank.description}</p><small>{currentNodeCosts.filter((cost) => Number(cost.rank) === Number(rank.rank)).map((cost) => `${cost.resource_id} × ${cost.quantity}`).join(' · ') || 'CREDITS ONLY'}</small></section>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if action === 'saveProgression'}
|
||||
<div class="proposal-rank-preview">
|
||||
{#each currentRankProfiles as profile}
|
||||
<section><header><span>RANK {profile.rank}</span><b>{profile.point_cost} POINTS</b></header><p>{Number(profile.credit_cost).toLocaleString()} CREDITS</p></section>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="proposal-empty-state"><b>NEW {specializationKind}</b><span>No live registry record will be replaced.</span></div>
|
||||
{/if}
|
||||
</article>
|
||||
<article class="proposal-editor-preview after">
|
||||
<header><span>PROPOSED · {specializationKind}</span><b>AFTER</b></header>
|
||||
<div class="proposal-field-summary specialization-fields">
|
||||
{#each specializationFields as field}
|
||||
<div><span>{field.label}</span><strong>{shown(field.proposed)}</strong></div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if action === 'saveAbility'}
|
||||
<div class="proposal-structured-list">
|
||||
<header><span>STRUCTURED EFFECTS</span><b>{proposedEffects.length}</b></header>
|
||||
{#each proposedEffects as effect}<p>{effectSummary(effect)}</p>{:else}<p>NO STRUCTURED EFFECTS</p>{/each}
|
||||
</div>
|
||||
{:else if action === 'saveNode'}
|
||||
<div class="proposal-rank-preview">
|
||||
{#each proposedRanks as rank}
|
||||
<section>
|
||||
<header><span>RANK {rank.rank}</span><b>{rank.effects?.length ?? 0} EFFECTS</b></header>
|
||||
<p>{rank.description || 'NO RANK DESCRIPTION'}</p>
|
||||
{#each rank.effects ?? [] as effect}<small>{effectSummary(effect)}</small>{/each}
|
||||
<small>{Object.entries(rank.costs ?? {}).filter(([, quantity]) => Number(quantity) > 0).map(([resource, quantity]) => `${resource} × ${quantity}`).join(' · ') || 'CREDITS ONLY'}</small>
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if action === 'saveProgression'}
|
||||
<div class="proposal-rank-preview">
|
||||
{#each [1,2,3] as rank}
|
||||
<section><header><span>RANK {rank}</span><b>{shown(proposed[`rank${rank}PointCost`])} POINTS</b></header><p>{Number(proposed[`rank${rank}CreditCost`] || 0).toLocaleString()} CREDITS</p></section>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</article>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="proposal-editor-preview generic after">
|
||||
<header><span>PROPOSED {label(module)} RECORD</span><b>AFTER</b></header>
|
||||
|
||||
Reference in New Issue
Block a user