show dates on entity detail pages
This commit is contained in:
parent
6f1f0c60a2
commit
1b8232ed5a
3 changed files with 270 additions and 27 deletions
|
|
@ -17,10 +17,10 @@
|
|||
import CharacterUncapSection from '$lib/features/database/characters/sections/CharacterUncapSection.svelte'
|
||||
import CharacterTaxonomySection from '$lib/features/database/characters/sections/CharacterTaxonomySection.svelte'
|
||||
import CharacterStatsSection from '$lib/features/database/characters/sections/CharacterStatsSection.svelte'
|
||||
import CharacterImagesSection from '$lib/features/database/characters/sections/CharacterImagesSection.svelte'
|
||||
import EntityImagesTab from '$lib/features/database/detail/tabs/EntityImagesTab.svelte'
|
||||
import EntityRawDataTab from '$lib/features/database/detail/tabs/EntityRawDataTab.svelte'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import { getCharacterImage } from '$lib/utils/images'
|
||||
|
||||
// Types
|
||||
|
|
@ -81,6 +81,9 @@
|
|||
return getCharacterImage(character?.granblueId, 'grid', '01')
|
||||
}
|
||||
|
||||
// Available image sizes for characters
|
||||
const characterSizes = ['detail', 'grid', 'main', 'square']
|
||||
|
||||
// Generate image items for character (variants and poses based on uncap level)
|
||||
const characterImages = $derived.by((): ImageItem[] => {
|
||||
if (!character?.granblueId) return []
|
||||
|
|
@ -88,8 +91,7 @@
|
|||
const variants = ['detail', 'grid', 'main', 'square'] as const
|
||||
const images: ImageItem[] = []
|
||||
|
||||
// Determine available poses based on uncap level
|
||||
// _01 = Base, _02 = MLB (3*), _03 = FLB (5*), _04 = Transcendence
|
||||
// Only include poses that are available - _01 = Base, _02 = MLB (3*), _03 = FLB (5*), _04 = Transcendence
|
||||
const poses: { id: string; label: string }[] = [
|
||||
{ id: '01', label: 'Base' },
|
||||
{ id: '02', label: 'MLB' }
|
||||
|
|
@ -103,19 +105,51 @@
|
|||
poses.push({ id: '04', label: 'Transcendence' })
|
||||
}
|
||||
|
||||
for (const variant of variants) {
|
||||
for (const pose of poses) {
|
||||
for (const pose of poses) {
|
||||
for (const variant of variants) {
|
||||
images.push({
|
||||
url: getCharacterImage(character.granblueId, variant, pose.id),
|
||||
label: `${variant} (${pose.label})`,
|
||||
variant,
|
||||
pose: pose.id
|
||||
pose: pose.id,
|
||||
poseLabel: pose.label
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return images
|
||||
})
|
||||
|
||||
// Image download handlers
|
||||
async function handleDownloadImage(size: string, transformation: string | undefined, force: boolean) {
|
||||
if (!character?.id) return
|
||||
await entityAdapter.downloadCharacterImage(character.id, size, transformation, force)
|
||||
}
|
||||
|
||||
async function handleDownloadAllPose(pose: string, force: boolean) {
|
||||
if (!character?.id) return
|
||||
// Download all sizes for this pose
|
||||
for (const size of characterSizes) {
|
||||
await entityAdapter.downloadCharacterImage(character.id, size, pose, force)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDownloadAllImages(force: boolean) {
|
||||
if (!character?.id) return
|
||||
await entityAdapter.downloadCharacterImages(character.id, { force })
|
||||
}
|
||||
|
||||
async function handleDownloadSize(size: string) {
|
||||
if (!character?.id) return
|
||||
// Download this size for all available poses
|
||||
const poses = ['01', '02']
|
||||
if (character.uncap?.flb) poses.push('03')
|
||||
if (character.uncap?.transcendence) poses.push('04')
|
||||
|
||||
for (const pose of poses) {
|
||||
await entityAdapter.downloadCharacterImage(character.id, size, pose, false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
|
|
@ -128,6 +162,9 @@
|
|||
editUrl={canEdit ? editUrl : undefined}
|
||||
{currentTab}
|
||||
onTabChange={handleTabChange}
|
||||
onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
|
||||
onDownloadSize={canEdit ? handleDownloadSize : undefined}
|
||||
availableSizes={characterSizes}
|
||||
>
|
||||
{#if currentTab === 'info'}
|
||||
<section class="details">
|
||||
|
|
@ -136,12 +173,18 @@
|
|||
<CharacterTaxonomySection {character} />
|
||||
<CharacterStatsSection {character} />
|
||||
|
||||
{#if character?.id && character?.granblueId}
|
||||
<CharacterImagesSection
|
||||
characterId={character.id}
|
||||
granblueId={character.granblueId}
|
||||
{canEdit}
|
||||
/>
|
||||
{#if character.releaseDate || character.flbDate || character.ulbDate}
|
||||
<DetailsContainer title="Dates">
|
||||
{#if character.releaseDate}
|
||||
<DetailItem label="Release Date" value={character.releaseDate} />
|
||||
{/if}
|
||||
{#if character.flbDate}
|
||||
<DetailItem label="FLB Date" value={character.flbDate} />
|
||||
{/if}
|
||||
{#if character.ulbDate}
|
||||
<DetailItem label="ULB Date" value={character.ulbDate} />
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
{/if}
|
||||
|
||||
{#if relatedQuery.data?.length}
|
||||
|
|
@ -162,13 +205,26 @@
|
|||
{/if}
|
||||
</section>
|
||||
{:else if currentTab === 'images'}
|
||||
<EntityImagesTab images={characterImages} />
|
||||
<EntityImagesTab
|
||||
images={characterImages}
|
||||
{canEdit}
|
||||
onDownloadImage={canEdit ? handleDownloadImage : undefined}
|
||||
onDownloadAllPose={canEdit ? handleDownloadAllPose : undefined}
|
||||
/>
|
||||
{:else if currentTab === 'raw'}
|
||||
<EntityRawDataTab
|
||||
wikiRaw={rawDataQuery.data?.wikiRaw}
|
||||
gameRawEn={rawDataQuery.data?.gameRawEn}
|
||||
gameRawJp={rawDataQuery.data?.gameRawJp}
|
||||
isLoading={rawDataQuery.isLoading}
|
||||
{canEdit}
|
||||
onFetchWiki={canEdit && character?.id
|
||||
? async () => {
|
||||
const result = await entityAdapter.fetchCharacterWiki(character.id)
|
||||
rawDataQuery.refetch()
|
||||
return result
|
||||
}
|
||||
: undefined}
|
||||
/>
|
||||
{/if}
|
||||
</DetailScaffold>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
import SummonStatsSection from '$lib/features/database/summons/sections/SummonStatsSection.svelte'
|
||||
import EntityImagesTab from '$lib/features/database/detail/tabs/EntityImagesTab.svelte'
|
||||
import EntityRawDataTab from '$lib/features/database/detail/tabs/EntityRawDataTab.svelte'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import { getSummonImage } from '$lib/utils/images'
|
||||
|
||||
// Types
|
||||
|
|
@ -69,23 +71,85 @@
|
|||
return getSummonImage(summon?.granblueId, 'grid')
|
||||
}
|
||||
|
||||
// Available image sizes for summons
|
||||
const summonSizes = ['detail', 'grid', 'main', 'square', 'wide']
|
||||
|
||||
// Generate image items for summon (detail, grid, main, square, wide variants)
|
||||
// Summons have transformations: Base (no suffix), ULB (_02), Transcendence Stage 1 (_03), Transcendence Stage 5 (_04)
|
||||
const summonImages = $derived.by((): ImageItem[] => {
|
||||
if (!summon?.granblueId) return []
|
||||
|
||||
const variants = ['detail', 'grid', 'main', 'square', 'wide'] as const
|
||||
const images: ImageItem[] = []
|
||||
|
||||
for (const variant of variants) {
|
||||
images.push({
|
||||
url: getSummonImage(summon.granblueId, variant),
|
||||
label: variant,
|
||||
variant
|
||||
})
|
||||
// Only include transformations that are available
|
||||
const transformations: { id: string; label: string; suffix?: string }[] = [
|
||||
{ id: '01', label: 'Base', suffix: undefined }
|
||||
]
|
||||
|
||||
if (summon.uncap?.ulb) {
|
||||
transformations.push({ id: '02', label: 'ULB', suffix: '02' })
|
||||
}
|
||||
|
||||
if (summon.uncap?.transcendence) {
|
||||
transformations.push(
|
||||
{ id: '03', label: 'Transcendence (1)', suffix: '03' },
|
||||
{ id: '04', label: 'Transcendence (5)', suffix: '04' }
|
||||
)
|
||||
}
|
||||
|
||||
for (const transformation of transformations) {
|
||||
for (const variant of variants) {
|
||||
images.push({
|
||||
url: getSummonImage(summon.granblueId, variant, transformation.suffix),
|
||||
label: `${variant} (${transformation.label})`,
|
||||
variant,
|
||||
pose: transformation.id,
|
||||
poseLabel: transformation.label
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return images
|
||||
})
|
||||
|
||||
// Image download handlers
|
||||
async function handleDownloadImage(size: string, transformation: string | undefined, force: boolean) {
|
||||
if (!summon?.id) return
|
||||
// For summons, '01' means base (no transformation suffix)
|
||||
const trans = transformation === '01' ? undefined : transformation
|
||||
await entityAdapter.downloadSummonImage(summon.id, size, trans, force)
|
||||
}
|
||||
|
||||
async function handleDownloadAllPose(pose: string, force: boolean) {
|
||||
if (!summon?.id) return
|
||||
const trans = pose === '01' ? undefined : pose
|
||||
// Download all sizes for this pose
|
||||
for (const size of summonSizes) {
|
||||
await entityAdapter.downloadSummonImage(summon.id, size, trans, force)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDownloadAllImages(force: boolean) {
|
||||
if (!summon?.id) return
|
||||
await entityAdapter.downloadSummonImages(summon.id, { force })
|
||||
}
|
||||
|
||||
async function handleDownloadSize(size: string) {
|
||||
if (!summon?.id) return
|
||||
// Download this size for all available transformations
|
||||
const transformations: (string | undefined)[] = [undefined]
|
||||
if (summon.uncap?.ulb) {
|
||||
transformations.push('02')
|
||||
}
|
||||
if (summon.uncap?.transcendence) {
|
||||
transformations.push('03', '04')
|
||||
}
|
||||
|
||||
for (const trans of transformations) {
|
||||
await entityAdapter.downloadSummonImage(summon.id, size, trans, false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
|
|
@ -98,6 +162,9 @@
|
|||
editUrl={canEdit ? editUrl : undefined}
|
||||
{currentTab}
|
||||
onTabChange={handleTabChange}
|
||||
onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
|
||||
onDownloadSize={canEdit ? handleDownloadSize : undefined}
|
||||
availableSizes={summonSizes}
|
||||
>
|
||||
{#if currentTab === 'info'}
|
||||
<section class="details">
|
||||
|
|
@ -147,15 +214,45 @@
|
|||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if summon.releaseDate || summon.flbDate || summon.ulbDate || summon.transcendenceDate}
|
||||
<DetailsContainer title="Dates">
|
||||
{#if summon.releaseDate}
|
||||
<DetailItem label="Release Date" value={summon.releaseDate} />
|
||||
{/if}
|
||||
{#if summon.flbDate}
|
||||
<DetailItem label="FLB Date" value={summon.flbDate} />
|
||||
{/if}
|
||||
{#if summon.ulbDate}
|
||||
<DetailItem label="ULB Date" value={summon.ulbDate} />
|
||||
{/if}
|
||||
{#if summon.transcendenceDate}
|
||||
<DetailItem label="Transcendence Date" value={summon.transcendenceDate} />
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
{/if}
|
||||
</section>
|
||||
{:else if currentTab === 'images'}
|
||||
<EntityImagesTab images={summonImages} />
|
||||
<EntityImagesTab
|
||||
images={summonImages}
|
||||
{canEdit}
|
||||
onDownloadImage={canEdit ? handleDownloadImage : undefined}
|
||||
onDownloadAllPose={canEdit ? handleDownloadAllPose : undefined}
|
||||
/>
|
||||
{:else if currentTab === 'raw'}
|
||||
<EntityRawDataTab
|
||||
wikiRaw={rawDataQuery.data?.wikiRaw}
|
||||
gameRawEn={rawDataQuery.data?.gameRawEn}
|
||||
gameRawJp={rawDataQuery.data?.gameRawJp}
|
||||
isLoading={rawDataQuery.isLoading}
|
||||
{canEdit}
|
||||
onFetchWiki={canEdit && summon?.id
|
||||
? async () => {
|
||||
const result = await entityAdapter.fetchSummonWiki(summon.id)
|
||||
rawDataQuery.refetch()
|
||||
return result
|
||||
}
|
||||
: undefined}
|
||||
/>
|
||||
{/if}
|
||||
</DetailScaffold>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
import WeaponStatsSection from '$lib/features/database/weapons/sections/WeaponStatsSection.svelte'
|
||||
import EntityImagesTab from '$lib/features/database/detail/tabs/EntityImagesTab.svelte'
|
||||
import EntityRawDataTab from '$lib/features/database/detail/tabs/EntityRawDataTab.svelte'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import { getWeaponGridImage, getWeaponImage as getWeaponImageUrl } from '$lib/utils/images'
|
||||
|
||||
// Types
|
||||
|
|
@ -69,23 +71,78 @@
|
|||
return getWeaponGridImage(weapon?.granblueId, weapon?.element, weapon?.instanceElement)
|
||||
}
|
||||
|
||||
// Available image sizes for weapons
|
||||
const weaponSizes = ['base', 'grid', 'main', 'square']
|
||||
|
||||
// Generate image items for weapon (base, grid, main, square variants)
|
||||
// Weapons have transformations: Base (no suffix), Transcendence Stage 1 (_02), Transcendence Stage 5 (_03)
|
||||
const weaponImages = $derived.by((): ImageItem[] => {
|
||||
if (!weapon?.granblueId) return []
|
||||
|
||||
const variants = ['base', 'grid', 'main', 'square'] as const
|
||||
const images: ImageItem[] = []
|
||||
|
||||
for (const variant of variants) {
|
||||
images.push({
|
||||
url: getWeaponImageUrl(weapon.granblueId, variant),
|
||||
label: variant,
|
||||
variant
|
||||
})
|
||||
// Only include transformations that are available
|
||||
const transformations: { id: string; label: string; suffix?: string }[] = [
|
||||
{ id: '01', label: 'Base', suffix: undefined }
|
||||
]
|
||||
|
||||
if (weapon.uncap?.transcendence) {
|
||||
transformations.push(
|
||||
{ id: '02', label: 'Transcendence (1)', suffix: '02' },
|
||||
{ id: '03', label: 'Transcendence (5)', suffix: '03' }
|
||||
)
|
||||
}
|
||||
|
||||
for (const transformation of transformations) {
|
||||
for (const variant of variants) {
|
||||
images.push({
|
||||
url: getWeaponImageUrl(weapon.granblueId, variant, undefined, transformation.suffix),
|
||||
label: `${variant} (${transformation.label})`,
|
||||
variant,
|
||||
pose: transformation.id,
|
||||
poseLabel: transformation.label
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return images
|
||||
})
|
||||
|
||||
// Image download handlers
|
||||
async function handleDownloadImage(size: string, transformation: string | undefined, force: boolean) {
|
||||
if (!weapon?.id) return
|
||||
// For weapons, '01' means base (no transformation suffix)
|
||||
const trans = transformation === '01' ? undefined : transformation
|
||||
await entityAdapter.downloadWeaponImage(weapon.id, size, trans, force)
|
||||
}
|
||||
|
||||
async function handleDownloadAllPose(pose: string, force: boolean) {
|
||||
if (!weapon?.id) return
|
||||
const trans = pose === '01' ? undefined : pose
|
||||
// Download all sizes for this pose
|
||||
for (const size of weaponSizes) {
|
||||
await entityAdapter.downloadWeaponImage(weapon.id, size, trans, force)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDownloadAllImages(force: boolean) {
|
||||
if (!weapon?.id) return
|
||||
await entityAdapter.downloadWeaponImages(weapon.id, { force })
|
||||
}
|
||||
|
||||
async function handleDownloadSize(size: string) {
|
||||
if (!weapon?.id) return
|
||||
// Download this size for all available transformations
|
||||
const transformations: (string | undefined)[] = [undefined]
|
||||
if (weapon.uncap?.transcendence) {
|
||||
transformations.push('02', '03')
|
||||
}
|
||||
|
||||
for (const trans of transformations) {
|
||||
await entityAdapter.downloadWeaponImage(weapon.id, size, trans, false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="page">
|
||||
|
|
@ -98,6 +155,9 @@
|
|||
editUrl={canEdit ? editUrl : undefined}
|
||||
{currentTab}
|
||||
onTabChange={handleTabChange}
|
||||
onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
|
||||
onDownloadSize={canEdit ? handleDownloadSize : undefined}
|
||||
availableSizes={weaponSizes}
|
||||
>
|
||||
{#if currentTab === 'info'}
|
||||
<section class="details">
|
||||
|
|
@ -123,15 +183,45 @@
|
|||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if weapon.releaseDate || weapon.flbDate || weapon.ulbDate || weapon.transcendenceDate}
|
||||
<DetailsContainer title="Dates">
|
||||
{#if weapon.releaseDate}
|
||||
<DetailItem label="Release Date" value={weapon.releaseDate} />
|
||||
{/if}
|
||||
{#if weapon.flbDate}
|
||||
<DetailItem label="FLB Date" value={weapon.flbDate} />
|
||||
{/if}
|
||||
{#if weapon.ulbDate}
|
||||
<DetailItem label="ULB Date" value={weapon.ulbDate} />
|
||||
{/if}
|
||||
{#if weapon.transcendenceDate}
|
||||
<DetailItem label="Transcendence Date" value={weapon.transcendenceDate} />
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
{/if}
|
||||
</section>
|
||||
{:else if currentTab === 'images'}
|
||||
<EntityImagesTab images={weaponImages} />
|
||||
<EntityImagesTab
|
||||
images={weaponImages}
|
||||
{canEdit}
|
||||
onDownloadImage={canEdit ? handleDownloadImage : undefined}
|
||||
onDownloadAllPose={canEdit ? handleDownloadAllPose : undefined}
|
||||
/>
|
||||
{:else if currentTab === 'raw'}
|
||||
<EntityRawDataTab
|
||||
wikiRaw={rawDataQuery.data?.wikiRaw}
|
||||
gameRawEn={rawDataQuery.data?.gameRawEn}
|
||||
gameRawJp={rawDataQuery.data?.gameRawJp}
|
||||
isLoading={rawDataQuery.isLoading}
|
||||
{canEdit}
|
||||
onFetchWiki={canEdit && weapon?.id
|
||||
? async () => {
|
||||
const result = await entityAdapter.fetchWeaponWiki(weapon.id)
|
||||
rawDataQuery.refetch()
|
||||
return result
|
||||
}
|
||||
: undefined}
|
||||
/>
|
||||
{/if}
|
||||
</DetailScaffold>
|
||||
|
|
|
|||
Loading…
Reference in a new issue