weapon sections: add name fields to metadata, move recruits to gacha

This commit is contained in:
Justin Edmund 2025-12-15 13:08:17 -08:00
parent 0812e3b2d4
commit 70c0881bb4
2 changed files with 90 additions and 95 deletions

View file

@ -7,6 +7,7 @@
import MultiSelect from '$lib/components/ui/MultiSelect.svelte'
import CharacterTypeahead from '$lib/components/ui/CharacterTypeahead.svelte'
import { PROMOTION_NAMES, getPromotionNames } from '$lib/types/enums'
import { getCharacterImage } from '$lib/utils/images'
interface Props {
weapon: any
@ -40,56 +41,81 @@
if (!promotions || promotions.length === 0) return '—'
return getPromotionNames(promotions).join(', ')
}
// Format recruits for display
function formatRecruitsDisplay(recruits: any): string {
if (!recruits) return '—'
if (typeof recruits === 'string') return recruits
return recruits.name?.en || recruits.granblueId || '—'
}
// Check if we should show the section in view mode
const hasGachaData = $derived.by(() => {
if (editMode) return true
const hasPromotions = weapon?.promotions && weapon.promotions.length > 0
const hasRecruits = weapon?.recruits
return hasPromotions || hasRecruits
})
</script>
{#if hasGachaData}
<DetailsContainer title="Gacha">
{#if editMode}
<DetailItem label="Promotions" sublabel="Gacha pools where this weapon appears" editable={true}>
<MultiSelect
size="medium"
options={promotionOptions}
bind:value={editData.promotions}
placeholder="Select promotions"
contained
/>
</DetailItem>
<DetailItem label="Recruits" sublabel="Character recruited by this weapon" editable={true}>
<CharacterTypeahead
bind:value={editData.recruits}
initialCharacter={weapon.recruits ? { id: weapon.recruits.id, name: weapon.recruits.name?.en || weapon.recruits.granblueId, granblueId: weapon.recruits.granblueId } : null}
placeholder="Search for character..."
contained
/>
</DetailItem>
{:else}
<DetailItem
label="Promotions"
sublabel="Gacha pools where this weapon appears"
value={formatPromotionsDisplay(weapon.promotions)}
<DetailsContainer title="Gacha">
{#if editMode}
<DetailItem label="Promotions" sublabel="Gacha pools where this weapon appears" editable={true}>
<MultiSelect
size="medium"
options={promotionOptions}
bind:value={editData.promotions}
placeholder="Select promotions"
contained
/>
</DetailItem>
<DetailItem label="Recruits" sublabel="Character recruited by this weapon" editable={true}>
<CharacterTypeahead
bind:value={editData.recruits}
initialCharacter={weapon.recruits ? { id: weapon.recruits.id, name: weapon.recruits.name?.en || weapon.recruits.granblueId, granblueId: weapon.recruits.granblueId } : null}
placeholder="Search for character..."
contained
/>
</DetailItem>
{:else}
<DetailItem
label="Promotions"
sublabel="Gacha pools where this weapon appears"
value={formatPromotionsDisplay(weapon.promotions)}
/>
<DetailItem label="Recruits" sublabel="Character recruited by this weapon">
{#if weapon.recruits}
<DetailItem
label="Recruits"
sublabel="Character recruited by this weapon"
value={formatRecruitsDisplay(weapon.recruits)}
/>
<a href="/database/characters/{weapon.recruits.granblueId}" class="recruits-link">
<img
src={getCharacterImage(weapon.recruits.granblueId, 'square', '01')}
alt={weapon.recruits.name?.en || 'Recruited character'}
class="recruits-image"
/>
<span class="recruits-name">{weapon.recruits.name?.en}</span>
</a>
{:else}
<span class="empty-value"></span>
{/if}
{/if}
</DetailsContainer>
{/if}
</DetailItem>
{/if}
</DetailsContainer>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
@use '$src/themes/layout' as layout;
.recruits-link {
display: flex;
align-items: center;
gap: spacing.$unit;
text-decoration: none;
color: colors.$grey-30;
&:hover .recruits-image {
transform: scale(1.05);
}
&:hover .recruits-name {
color: colors.$blue;
}
}
.recruits-image {
width: 32px;
height: 32px;
border-radius: layout.$item-corner-small;
transition: transform 0.2s ease;
}
.recruits-name {
font-size: typography.$font-regular;
transition: color 0.2s ease;
}
</style>

View file

@ -7,7 +7,6 @@
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
import CopyableText from '$lib/components/ui/CopyableText.svelte'
import { getRarityLabel, getRarityOptions } from '$lib/utils/rarity'
import { getCharacterImage } from '$lib/utils/images'
interface Props {
weapon: any
@ -35,6 +34,20 @@
<DetailsContainer title="Metadata">
{#if editMode}
<DetailItem
label="Name (EN)"
bind:value={editData.name}
editable={true}
type="text"
placeholder="English name"
/>
<DetailItem
label="Name (JP)"
bind:value={editData.nameJp}
editable={true}
type="text"
placeholder="日本語名"
/>
<SuggestionDetailItem
label="Rarity"
bind:value={editData.rarity}
@ -53,6 +66,8 @@
type="text"
/>
{:else}
<DetailItem label="Name (EN)" value={weapon.name?.en || '—'} />
<DetailItem label="Name (JP)" value={weapon.name?.ja || '—'} />
<DetailItem label="Rarity" value={getRarityLabel(weapon.rarity)} />
<DetailItem label="Granblue ID">
{#if weapon.granblueId}
@ -61,53 +76,7 @@
{/if}
</DetailItem>
{#if weapon.recruits}
<DetailItem label="Recruits">
<a href="/database/characters/{weapon.recruits.granblueId}" class="recruits-link">
<img
src={getCharacterImage(weapon.recruits.granblueId, 'square', '01')}
alt={weapon.recruits.name.en || 'Recruited character'}
class="recruits-image"
/>
<span class="recruits-name">{weapon.recruits.name.en}</span>
</a>
</DetailItem>
{/if}
{/if}
</DetailsContainer>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
@use '$src/themes/layout' as layout;
.recruits-link {
display: flex;
align-items: center;
gap: spacing.$unit;
text-decoration: none;
color: colors.$grey-30;
&:hover .recruits-image {
transform: scale(1.05);
}
&:hover .recruits-name {
color: colors.$blue;
}
}
.recruits-image {
width: 32px;
height: 32px;
border-radius: layout.$item-corner-small;
transition: transform 0.2s ease;
}
.recruits-name {
font-size: typography.$font-regular;
transition: color 0.2s ease;
}
</style>