add weapons/series view toggle
This commit is contained in:
parent
422b5762d3
commit
93dc96c1b0
1 changed files with 232 additions and 10 deletions
|
|
@ -3,7 +3,12 @@
|
|||
<script lang="ts">
|
||||
import PageMeta from '$lib/components/PageMeta.svelte'
|
||||
import * as m from '$lib/paraglide/messages'
|
||||
import { goto } from '$app/navigation'
|
||||
import { createQuery } from '@tanstack/svelte-query'
|
||||
import { entityQueries } from '$lib/api/queries/entity.queries'
|
||||
import DatabaseGridWithProvider from '$lib/components/database/DatabaseGridWithProvider.svelte'
|
||||
import SegmentedControl from '$lib/components/ui/segmented-control/SegmentedControl.svelte'
|
||||
import Segment from '$lib/components/ui/segmented-control/Segment.svelte'
|
||||
import type { IColumn } from 'wx-svelte-grid'
|
||||
import WeaponImageCell from '$lib/components/database/cells/WeaponImageCell.svelte'
|
||||
import ElementCell from '$lib/components/database/cells/ElementCell.svelte'
|
||||
|
|
@ -12,6 +17,23 @@
|
|||
import LastUpdatedCell from '$lib/components/database/cells/LastUpdatedCell.svelte'
|
||||
import { getRarityLabel } from '$lib/utils/rarity'
|
||||
|
||||
// View mode state
|
||||
let viewMode = $state<'weapons' | 'series'>('weapons')
|
||||
|
||||
// Query for weapon series
|
||||
const weaponSeriesQuery = createQuery(() => entityQueries.weaponSeriesList())
|
||||
|
||||
// Sorted series data
|
||||
const sortedSeries = $derived.by(() => {
|
||||
if (!weaponSeriesQuery.data) return []
|
||||
return [...weaponSeriesQuery.data].sort((a, b) => a.order - b.order)
|
||||
})
|
||||
|
||||
// Navigate to series detail
|
||||
function handleSeriesClick(seriesId: string) {
|
||||
goto(`/database/series/${seriesId}`)
|
||||
}
|
||||
|
||||
// Column configuration for weapons
|
||||
const columns: IColumn[] = [
|
||||
{
|
||||
|
|
@ -73,11 +95,81 @@
|
|||
<PageMeta title={m.page_title_db_weapons()} description={m.page_desc_home()} />
|
||||
|
||||
<div class="database-page">
|
||||
<DatabaseGridWithProvider resource="weapons" {columns} pageSize={20} />
|
||||
{#if viewMode === 'weapons'}
|
||||
<DatabaseGridWithProvider resource="weapons" {columns} pageSize={20}>
|
||||
{#snippet leftActions()}
|
||||
<SegmentedControl bind:value={viewMode} size="xsmall" variant="background">
|
||||
<Segment value="weapons">Weapons</Segment>
|
||||
<Segment value="series">Series</Segment>
|
||||
</SegmentedControl>
|
||||
{/snippet}
|
||||
</DatabaseGridWithProvider>
|
||||
{:else}
|
||||
<div class="grid-container">
|
||||
<div class="controls">
|
||||
<SegmentedControl bind:value={viewMode} size="xsmall" variant="background">
|
||||
<Segment value="weapons">Weapons</Segment>
|
||||
<Segment value="series">Series</Segment>
|
||||
</SegmentedControl>
|
||||
</div>
|
||||
|
||||
{#if weaponSeriesQuery.isPending}
|
||||
<div class="loading-state">Loading series...</div>
|
||||
{:else if weaponSeriesQuery.error}
|
||||
<div class="error-state">Failed to load series</div>
|
||||
{:else if sortedSeries.length > 0}
|
||||
<div class="grid-wrapper">
|
||||
<table class="series-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-order">Order</th>
|
||||
<th class="col-name">Name</th>
|
||||
<th class="col-slug">Slug</th>
|
||||
<th class="col-flags">Flags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each sortedSeries as series (series.id)}
|
||||
<tr onclick={() => handleSeriesClick(series.id)} class="clickable">
|
||||
<td class="col-order">{series.order}</td>
|
||||
<td class="col-name">
|
||||
<span class="series-name">{series.name.en}</span>
|
||||
</td>
|
||||
<td class="col-slug"><code>{series.slug}</code></td>
|
||||
<td class="col-flags">
|
||||
<div class="flags">
|
||||
{#if series.extra}<span class="flag extra">Extra</span>{/if}
|
||||
{#if series.elementChangeable}<span class="flag element">Element</span>{/if}
|
||||
{#if series.hasWeaponKeys}<span class="flag keys">Keys</span>{/if}
|
||||
{#if series.hasAwakening}<span class="flag awaken">Awaken</span>{/if}
|
||||
{#if series.hasAxSkills}<span class="flag ax">AX</span>{/if}
|
||||
{#if !series.extra && !series.elementChangeable && !series.hasWeaponKeys && !series.hasAwakening && !series.hasAxSkills}
|
||||
<span class="no-flags">-</span>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="grid-footer">
|
||||
<div class="pagination-info">
|
||||
{sortedSeries.length} series
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="empty-state">No weapon series found</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/colors' as colors;
|
||||
@use '$src/themes/effects' as effects;
|
||||
@use '$src/themes/layout' as layout;
|
||||
@use '$src/themes/spacing' as spacing;
|
||||
@use '$src/themes/typography' as typography;
|
||||
|
||||
|
|
@ -85,18 +177,148 @@
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: spacing.$unit-2x;
|
||||
.grid-container {
|
||||
background: var(--card-bg);
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.18);
|
||||
border-radius: layout.$page-corner;
|
||||
box-shadow: effects.$page-elevation;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: typography.$font-xxlarge;
|
||||
font-weight: typography.$bold;
|
||||
margin-bottom: spacing.$unit-half;
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: spacing.$unit;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
gap: spacing.$unit;
|
||||
}
|
||||
|
||||
.loading-state,
|
||||
.error-state,
|
||||
.empty-state {
|
||||
padding: spacing.$unit-4x;
|
||||
text-align: center;
|
||||
color: colors.$grey-50;
|
||||
}
|
||||
|
||||
.error-state {
|
||||
color: var(--text-error, #ef4444);
|
||||
}
|
||||
|
||||
.grid-wrapper {
|
||||
overflow-x: auto;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.series-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: spacing.$unit spacing.$unit-2x;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: typography.$font-regular;
|
||||
color: colors.$grey-50;
|
||||
th {
|
||||
background: #f8f9fa;
|
||||
font-weight: typography.$bold;
|
||||
color: #495057;
|
||||
font-size: typography.$font-small;
|
||||
}
|
||||
|
||||
tr.clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
}
|
||||
|
||||
.col-order {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.col-name {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.col-slug {
|
||||
min-width: 150px;
|
||||
|
||||
code {
|
||||
font-size: typography.$font-small;
|
||||
background: #f0f0f0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.col-flags {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.series-name {
|
||||
font-weight: typography.$bold;
|
||||
}
|
||||
|
||||
.no-flags {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.flags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: spacing.$unit-half;
|
||||
}
|
||||
|
||||
.flag {
|
||||
display: inline-block;
|
||||
font-size: typography.$font-tiny;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-weight: typography.$medium;
|
||||
|
||||
&.extra {
|
||||
background: #f3e8ff;
|
||||
color: #6b21a8;
|
||||
}
|
||||
|
||||
&.element {
|
||||
background: linear-gradient(to right, #fecaca, #fef08a, #bbf7d0, #bfdbfe, #e9d5ff, #fbcfe8);
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
&.keys {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
&.awaken {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
&.ax {
|
||||
background: #ffe4e6;
|
||||
color: #9f1239;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: spacing.$unit;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
background: #f8f9fa;
|
||||
|
||||
.pagination-info {
|
||||
font-size: typography.$font-small;
|
||||
color: #6c757d;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in a new issue