feat(isac): link skill chips to abilities
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
let { data, form } = $props();
|
||||
type TalentEffect = { stat_attribute_id: string; min_value: number | string; max_value: number | string };
|
||||
const blankTalent = () => ({ id: '', name: '', description_template: '', triggerIds: [], effects: [{ stat_attribute_id: '', min_value: '', max_value: '' }] });
|
||||
const blank = () => ({ id: '', name: '', iconUrl: '', active: 1, twoPiece: { stat_attribute_id: '', min_value: '', max_value: '' }, primaryStatIds: [], secondaryStatIds: [], talent: blankTalent() });
|
||||
const blank = () => ({ id: '', name: '', abilityId: '', iconUrl: '', active: 1, twoPiece: { stat_attribute_id: '', min_value: '', max_value: '' }, primaryStatIds: [], secondaryStatIds: [], talent: blankTalent() });
|
||||
let editing = $state<any | null>(null);
|
||||
let highEndCount = $state('0');
|
||||
let handledResultToken = $state('');
|
||||
@@ -22,6 +22,7 @@
|
||||
{ value: '3', label: '3 HIGH-END' }
|
||||
];
|
||||
const stat = (id: string) => data.stats.find((item: any) => item.id === id);
|
||||
const ability = (id: string) => data.abilities.find((item: any) => item.id === id);
|
||||
const format = (value: number | string, statId: string) => {
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric)) return '—';
|
||||
@@ -37,6 +38,7 @@
|
||||
const edit = (item: any) => {
|
||||
editing = {
|
||||
...item,
|
||||
abilityId: item.specialization_ability_id ?? '',
|
||||
twoPiece: item.twoPiece ? { ...item.twoPiece } : { stat_attribute_id: '', min_value: '', max_value: '' },
|
||||
primaryStatIds: [...item.primaryStatIds],
|
||||
secondaryStatIds: [...item.secondaryStatIds],
|
||||
@@ -66,7 +68,8 @@
|
||||
<KpiFilters active={data.filter} items={[
|
||||
{value:'',label:'TOTAL SETS',count:data.summary.total,description:'Show all definitions'},
|
||||
{value:'active',label:'ACTIVE',count:data.summary.active,description:'Available to builder'},
|
||||
{value:'configured',label:'CONFIGURED',count:data.summary.configured,description:'Both bonuses defined'},
|
||||
{value:'assigned',label:'ABILITY LINKED',count:data.summary.assigned,description:'Assigned to a class ability'},
|
||||
{value:'unassigned',label:'UNASSIGNED',count:data.summary.unassigned,description:'Legacy records needing an ability'},
|
||||
{value:'archived',label:'ARCHIVED',count:data.summary.archived,description:'Hidden definitions'}
|
||||
]}/>
|
||||
|
||||
@@ -77,6 +80,10 @@
|
||||
<input type="hidden" name="id" value={editing.id}/>
|
||||
<input type="hidden" name="talentEffects" value={JSON.stringify(editing.talent.effects.map((effect: TalentEffect) => ({ statId: effect.stat_attribute_id, minValue: effect.min_value, maxValue: effect.max_value })))}/>
|
||||
<div class="skill-chip-identity"><IconUploadField currentUrl={editing.iconUrl} label="SKILL CHIP ICON" resetToken={iconResetToken}/><label class="field">CHIP SET NAME<input name="name" bind:value={editing.name} maxlength="80" required/></label><div class="quality-scope"><span>SUPPORTED QUALITY</span><div><b class="quality-superior">SUPERIOR</b><b class="quality-highend">HIGH-END</b></div><small>Only these qualities participate in chip scaling.</small></div></div>
|
||||
<section class="skill-chip-ability-assignment">
|
||||
<div class="selection-head"><div><span>SPECIALIZATION ABILITY</span><small>EXCLUSIVE RELATIONSHIP · THREE ACTIVE SKILL CHIPS PER ABILITY</small></div><b>{editing.abilityId ? `${ability(editing.abilityId)?.chipCount ?? 0} / 3 ASSIGNED` : 'REQUIRED'}</b></div>
|
||||
<SearchableAssignment items={data.abilities} fieldName="abilityId" mode="single" compact subjectLabel="SPECIALIZATION ABILITY" searchPlaceholder="Search abilities or specializations…" emptySelectionLabel="NO ABILITY ASSIGNED" bind:value={editing.abilityId}/>
|
||||
</section>
|
||||
|
||||
<section class="skill-chip-bonus">
|
||||
<div class="selection-head"><div><span>2-PIECE BONUS</span><small>MINIMUM AT ZERO HIGH-END · LINEAR TO MAXIMUM AT THREE HIGH-END</small></div><b>{format(scaled(editing.twoPiece.min_value, editing.twoPiece.max_value), editing.twoPiece.stat_attribute_id)}</b></div>
|
||||
@@ -98,15 +105,15 @@
|
||||
</div>
|
||||
</section>
|
||||
{#if form?.skillChipError}<p class="error">{form.skillChipError}</p>{/if}
|
||||
<div class="form-actions split-actions"><div>{#if editing.id}<button class="danger" formaction={Number(editing.active) ? '?/archive' : '?/unarchive'} name="id" value={editing.id}>{Number(editing.active) ? 'ARCHIVE CHIP SET' : 'UNARCHIVE CHIP SET'}</button>{/if}</div><div><button type="button" class="secondary" onclick={() => editing = null}>CANCEL</button><button class="primary" disabled={!editing.primaryStatIds.length || editing.secondaryStatIds.length < 2 || !data.rollStats.length || !data.triggers.length}>SAVE SKILL CHIP →</button></div></div>
|
||||
<div class="form-actions split-actions"><div>{#if editing.id}<button class="danger" formaction={Number(editing.active) ? '?/archive' : '?/unarchive'} name="id" value={editing.id}>{Number(editing.active) ? 'ARCHIVE CHIP SET' : 'UNARCHIVE CHIP SET'}</button>{/if}</div><div><button type="button" class="secondary" onclick={() => editing = null}>CANCEL</button><button class="primary" disabled={!editing.abilityId || !editing.primaryStatIds.length || editing.secondaryStatIds.length < 2 || !data.rollStats.length || !data.triggers.length}>SAVE SKILL CHIP →</button></div></div>
|
||||
</PendingForm>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<RegistryPagination pagination={data.pagination} search={data.search}/>
|
||||
<div class="dashboard-table">
|
||||
<div class="dashboard-row skill-chip-row table-heading"><span>SKILL CHIP</span><span>QUALITY</span><span>2-PIECE BONUS</span><span>3-PIECE TALENT</span><span>ROLL POOLS</span><span>STATE</span><span></span></div>
|
||||
<div class="dashboard-row skill-chip-row table-heading"><span>SKILL CHIP</span><span>ABILITY</span><span>QUALITY</span><span>2-PIECE BONUS</span><span>3-PIECE TALENT</span><span>ROLL POOLS</span><span>STATE</span><span></span></div>
|
||||
{#each data.sets as item}
|
||||
<div class="dashboard-row skill-chip-row" class:archived={Number(item.active) !== 1} class:pending-record={item.pending}><div class="registry-icon-identity"><ItemIcon src={item.iconUrl} alt=""/><div><strong>{item.name}</strong><small>{item.pending ? `PROPOSED BY ${item.proposerName}` : 'SUPERIOR · HIGH-END'}</small></div></div><span class="skill-chip-quality-pair"><b class="quality-superior">SUP</b><b class="quality-highend">HE</b></span><span>{item.twoPiece ? `${stat(item.twoPiece.stat_attribute_id)?.name ?? 'UNKNOWN'} · ${format(item.twoPiece.min_value, item.twoPiece.stat_attribute_id)}–${format(item.twoPiece.max_value, item.twoPiece.stat_attribute_id)}` : 'NOT CONFIGURED'}</span><div><strong>{item.talent?.name ?? 'NOT CONFIGURED'}</strong><small>{item.talent?.effects.length ?? 0} SCALED EFFECTS</small></div><span>{item.primaryStatIds.length} PRIMARY · {item.secondaryStatIds.length} SECONDARY</span><b class:pending={item.pending} class="state-dot">{item.pending ? 'PENDING' : Number(item.active) ? 'ACTIVE' : 'ARCHIVED'}</b>{#if item.pending}<span></span>{:else}<button onclick={() => edit(item)}>EDIT</button>{/if}</div>
|
||||
<div class="dashboard-row skill-chip-row" class:archived={Number(item.active) !== 1} class:pending-record={item.pending}><div class="registry-icon-identity"><ItemIcon src={item.iconUrl} alt=""/><div><strong>{item.name}</strong><small>{item.pending ? `PROPOSED BY ${item.proposerName}` : 'SUPERIOR · HIGH-END'}</small></div></div><div class="skill-chip-ability-cell"><strong>{ability(String(item.specialization_ability_id ?? ''))?.name ?? 'UNASSIGNED'}</strong><small>{ability(String(item.specialization_ability_id ?? '')) ? 'EXCLUSIVE ABILITY' : 'LEGACY RECORD'}</small></div><span class="skill-chip-quality-pair"><b class="quality-superior">SUP</b><b class="quality-highend">HE</b></span><span>{item.twoPiece ? `${stat(item.twoPiece.stat_attribute_id)?.name ?? 'UNKNOWN'} · ${format(item.twoPiece.min_value, item.twoPiece.stat_attribute_id)}–${format(item.twoPiece.max_value, item.twoPiece.stat_attribute_id)}` : 'NOT CONFIGURED'}</span><div><strong>{item.talent?.name ?? 'NOT CONFIGURED'}</strong><small>{item.talent?.effects.length ?? 0} SCALED EFFECTS</small></div><span>{item.primaryStatIds.length} PRIMARY · {item.secondaryStatIds.length} SECONDARY</span><b class:pending={item.pending} class="state-dot">{item.pending ? 'PENDING' : Number(item.active) ? 'ACTIVE' : 'ARCHIVED'}</b>{#if item.pending}<span></span>{:else}<button onclick={() => edit(item)}>EDIT</button>{/if}</div>
|
||||
{:else}<div class="empty-dashboard"><strong>NO SKILL CHIP SETS REGISTERED</strong><span>Add the first Superior and High-end Skill Chip definition.</span></div>{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user