add new weapon series form page
This commit is contained in:
parent
04ce22fbdb
commit
136ad9d489
1 changed files with 198 additions and 0 deletions
198
src/routes/(app)/database/series/weapons/new/+page.svelte
Normal file
198
src/routes/(app)/database/series/weapons/new/+page.svelte
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation'
|
||||||
|
import { useQueryClient } from '@tanstack/svelte-query'
|
||||||
|
import { entityAdapter } from '$lib/api/adapters/entity.adapter'
|
||||||
|
import PageMeta from '$lib/components/PageMeta.svelte'
|
||||||
|
import * as m from '$lib/paraglide/messages'
|
||||||
|
import DatabasePageHeader from '$lib/components/database/DatabasePageHeader.svelte'
|
||||||
|
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||||
|
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||||
|
import Button from '$lib/components/ui/Button.svelte'
|
||||||
|
import { getAugmentTypeOptions } from '$lib/utils/augmentType'
|
||||||
|
import type { AugmentType } from '$lib/types/api/weaponStatModifier'
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
// Save state
|
||||||
|
let isSaving = $state(false)
|
||||||
|
let saveError = $state<string | null>(null)
|
||||||
|
|
||||||
|
// Form fields
|
||||||
|
let formData = $state({
|
||||||
|
nameEn: '',
|
||||||
|
nameJa: '',
|
||||||
|
slug: '',
|
||||||
|
order: 0,
|
||||||
|
extra: false,
|
||||||
|
elementChangeable: false,
|
||||||
|
hasWeaponKeys: false,
|
||||||
|
hasAwakening: false,
|
||||||
|
augmentType: 'no_augment' as AugmentType
|
||||||
|
})
|
||||||
|
|
||||||
|
// Augment type options for dropdown
|
||||||
|
const augmentTypeOptions = getAugmentTypeOptions().map((opt) => ({
|
||||||
|
value: opt.value,
|
||||||
|
label: opt.label
|
||||||
|
}))
|
||||||
|
|
||||||
|
async function createSeries() {
|
||||||
|
if (!formData.nameEn || !formData.slug) {
|
||||||
|
saveError = 'Name (EN) and Slug are required.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isSaving = true
|
||||||
|
saveError = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = {
|
||||||
|
name_en: formData.nameEn,
|
||||||
|
name_jp: formData.nameJa,
|
||||||
|
slug: formData.slug,
|
||||||
|
order: formData.order,
|
||||||
|
extra: formData.extra,
|
||||||
|
element_changeable: formData.elementChangeable,
|
||||||
|
has_weapon_keys: formData.hasWeaponKeys,
|
||||||
|
has_awakening: formData.hasAwakening,
|
||||||
|
augment_type: formData.augmentType
|
||||||
|
}
|
||||||
|
|
||||||
|
await entityAdapter.createWeaponSeries(payload)
|
||||||
|
|
||||||
|
// Invalidate cache
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: ['weaponSeries'],
|
||||||
|
refetchType: 'all'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Navigate to the new series detail page
|
||||||
|
goto(`/database/series/weapons/${formData.slug}`)
|
||||||
|
} catch (error) {
|
||||||
|
saveError = 'Failed to create weapon series. Please try again.'
|
||||||
|
console.error('Create error:', error)
|
||||||
|
} finally {
|
||||||
|
isSaving = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<PageMeta title="New Weapon Series" description={m.page_desc_home()} />
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
<DatabasePageHeader title="New Weapon Series" backHref="/database/weapons?view=series">
|
||||||
|
{#snippet rightAction()}
|
||||||
|
<Button variant="ghost" size="small" onclick={createSeries} disabled={isSaving}>
|
||||||
|
{isSaving ? 'Creating...' : 'Create'}
|
||||||
|
</Button>
|
||||||
|
{/snippet}
|
||||||
|
</DatabasePageHeader>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
{#if saveError}
|
||||||
|
<div class="error-banner">{saveError}</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<DetailsContainer title="Basic Info">
|
||||||
|
<DetailItem
|
||||||
|
label="Name (EN)"
|
||||||
|
bind:value={formData.nameEn}
|
||||||
|
editable={true}
|
||||||
|
type="text"
|
||||||
|
placeholder="English name"
|
||||||
|
width="320px"
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Name (JA)"
|
||||||
|
bind:value={formData.nameJa}
|
||||||
|
editable={true}
|
||||||
|
type="text"
|
||||||
|
placeholder="Japanese name"
|
||||||
|
width="320px"
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Slug"
|
||||||
|
bind:value={formData.slug}
|
||||||
|
editable={true}
|
||||||
|
type="text"
|
||||||
|
placeholder="url-friendly-slug"
|
||||||
|
width="240px"
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Order"
|
||||||
|
bind:value={formData.order}
|
||||||
|
editable={true}
|
||||||
|
type="number"
|
||||||
|
placeholder="0"
|
||||||
|
/>
|
||||||
|
</DetailsContainer>
|
||||||
|
|
||||||
|
<DetailsContainer title="Flags">
|
||||||
|
<DetailItem
|
||||||
|
label="Extra Grid"
|
||||||
|
sublabel="Weapon can be placed in Extra grid slots"
|
||||||
|
bind:value={formData.extra}
|
||||||
|
editable={true}
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Element Changeable"
|
||||||
|
sublabel="Weapon element can be changed by player"
|
||||||
|
bind:value={formData.elementChangeable}
|
||||||
|
editable={true}
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Has Weapon Keys"
|
||||||
|
sublabel="Weapon supports Pendulum/Teluma keys"
|
||||||
|
bind:value={formData.hasWeaponKeys}
|
||||||
|
editable={true}
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Has Awakening"
|
||||||
|
sublabel="Weapon can be awakened"
|
||||||
|
bind:value={formData.hasAwakening}
|
||||||
|
editable={true}
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Augment Type"
|
||||||
|
sublabel="Type of stat augments this series supports"
|
||||||
|
bind:value={formData.augmentType}
|
||||||
|
editable={true}
|
||||||
|
type="select"
|
||||||
|
options={augmentTypeOptions}
|
||||||
|
/>
|
||||||
|
</DetailsContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use '$src/themes/colors' as colors;
|
||||||
|
@use '$src/themes/layout' as layout;
|
||||||
|
@use '$src/themes/spacing' as spacing;
|
||||||
|
@use '$src/themes/typography' as typography;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background: white;
|
||||||
|
border-radius: layout.$page-corner;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-banner {
|
||||||
|
background: #fef2f2;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
color: #dc2626;
|
||||||
|
padding: spacing.$unit-2x;
|
||||||
|
margin: spacing.$unit-2x;
|
||||||
|
border-radius: layout.$item-corner;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in a new issue