Merge branch 'svelte-main' into feature/max-exorcism-level
This commit is contained in:
commit
9e1ecce936
20 changed files with 166 additions and 1132 deletions
|
|
@ -520,9 +520,9 @@ export interface EntityRawData {
|
|||
}
|
||||
|
||||
/**
|
||||
* Suggestions for character fields parsed from wiki data
|
||||
* Parsed character data from wiki text
|
||||
*/
|
||||
export interface CharacterSuggestions {
|
||||
export interface ParsedCharacterData {
|
||||
nameEn?: string
|
||||
nameJp?: string
|
||||
granblueId?: string
|
||||
|
|
@ -558,9 +558,9 @@ export interface CharacterSuggestions {
|
|||
}
|
||||
|
||||
/**
|
||||
* Suggestions for weapon fields parsed from wiki data
|
||||
* Parsed weapon data from wiki text
|
||||
*/
|
||||
export interface WeaponSuggestions {
|
||||
export interface ParsedWeaponData {
|
||||
nameEn?: string
|
||||
nameJp?: string
|
||||
granblueId?: string
|
||||
|
|
@ -590,9 +590,9 @@ export interface WeaponSuggestions {
|
|||
}
|
||||
|
||||
/**
|
||||
* Suggestions for summon fields parsed from wiki data
|
||||
* Parsed summon data from wiki text
|
||||
*/
|
||||
export interface SummonSuggestions {
|
||||
export interface ParsedSummonData {
|
||||
nameEn?: string
|
||||
nameJp?: string
|
||||
granblueId?: string
|
||||
|
|
@ -629,7 +629,7 @@ export interface BatchPreviewResult<T> {
|
|||
status: 'success' | 'error'
|
||||
granblueId?: string
|
||||
wikiRaw?: string
|
||||
suggestions?: T
|
||||
parsedData?: T
|
||||
imageStatus?: 'pending' | 'exists' | 'error' | 'no_id'
|
||||
error?: string
|
||||
redirectedFrom?: string
|
||||
|
|
@ -1240,7 +1240,7 @@ export class EntityAdapter extends BaseAdapter {
|
|||
// ============================================
|
||||
|
||||
/**
|
||||
* Fetches wiki data and suggestions for multiple character wiki pages
|
||||
* Fetches wiki data and parses it for multiple character wiki pages
|
||||
* Requires editor role (>= 7)
|
||||
* @param wikiPages - Array of wiki page names (max 10)
|
||||
* @param wikiData - Optional pre-fetched wiki text keyed by page name
|
||||
|
|
@ -1248,21 +1248,21 @@ export class EntityAdapter extends BaseAdapter {
|
|||
async batchPreviewCharacters(
|
||||
wikiPages: string[],
|
||||
wikiData?: Record<string, string>
|
||||
): Promise<BatchPreviewResponse<CharacterSuggestions>> {
|
||||
): Promise<BatchPreviewResponse<ParsedCharacterData>> {
|
||||
const body: { wiki_pages: string[]; wiki_data?: Record<string, string> } = {
|
||||
wiki_pages: wikiPages
|
||||
}
|
||||
if (wikiData) {
|
||||
body.wiki_data = wikiData
|
||||
}
|
||||
return this.request<BatchPreviewResponse<CharacterSuggestions>>('/characters/batch_preview', {
|
||||
return this.request<BatchPreviewResponse<ParsedCharacterData>>('/characters/batch_preview', {
|
||||
method: 'POST',
|
||||
body
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches wiki data and suggestions for multiple weapon wiki pages
|
||||
* Fetches wiki data and parses it for multiple weapon wiki pages
|
||||
* Requires editor role (>= 7)
|
||||
* @param wikiPages - Array of wiki page names (max 10)
|
||||
* @param wikiData - Optional pre-fetched wiki text keyed by page name
|
||||
|
|
@ -1270,21 +1270,21 @@ export class EntityAdapter extends BaseAdapter {
|
|||
async batchPreviewWeapons(
|
||||
wikiPages: string[],
|
||||
wikiData?: Record<string, string>
|
||||
): Promise<BatchPreviewResponse<WeaponSuggestions>> {
|
||||
): Promise<BatchPreviewResponse<ParsedWeaponData>> {
|
||||
const body: { wiki_pages: string[]; wiki_data?: Record<string, string> } = {
|
||||
wiki_pages: wikiPages
|
||||
}
|
||||
if (wikiData) {
|
||||
body.wiki_data = wikiData
|
||||
}
|
||||
return this.request<BatchPreviewResponse<WeaponSuggestions>>('/weapons/batch_preview', {
|
||||
return this.request<BatchPreviewResponse<ParsedWeaponData>>('/weapons/batch_preview', {
|
||||
method: 'POST',
|
||||
body
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches wiki data and suggestions for multiple summon wiki pages
|
||||
* Fetches wiki data and parses it for multiple summon wiki pages
|
||||
* Requires editor role (>= 7)
|
||||
* @param wikiPages - Array of wiki page names (max 10)
|
||||
* @param wikiData - Optional pre-fetched wiki text keyed by page name
|
||||
|
|
@ -1292,14 +1292,14 @@ export class EntityAdapter extends BaseAdapter {
|
|||
async batchPreviewSummons(
|
||||
wikiPages: string[],
|
||||
wikiData?: Record<string, string>
|
||||
): Promise<BatchPreviewResponse<SummonSuggestions>> {
|
||||
): Promise<BatchPreviewResponse<ParsedSummonData>> {
|
||||
const body: { wiki_pages: string[]; wiki_data?: Record<string, string> } = {
|
||||
wiki_pages: wikiPages
|
||||
}
|
||||
if (wikiData) {
|
||||
body.wiki_data = wikiData
|
||||
}
|
||||
return this.request<BatchPreviewResponse<SummonSuggestions>>('/summons/batch_preview', {
|
||||
return this.request<BatchPreviewResponse<ParsedSummonData>>('/summons/batch_preview', {
|
||||
method: 'POST',
|
||||
body
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,185 +0,0 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import { Tooltip as TooltipBase } from 'bits-ui'
|
||||
import Icon from '$lib/components/Icon.svelte'
|
||||
|
||||
interface Props {
|
||||
/** The suggested value to display */
|
||||
suggestion: string | number | boolean | null | undefined
|
||||
/** Label for the suggestion (e.g., field name) */
|
||||
label?: string
|
||||
/** Whether the suggestion has been dismissed */
|
||||
dismissed?: boolean
|
||||
/** Callback when user accepts the suggestion */
|
||||
onAccept?: () => void
|
||||
/** Callback when user dismisses the suggestion */
|
||||
onDismiss?: () => void
|
||||
}
|
||||
|
||||
let { suggestion, label, dismissed = false, onAccept, onDismiss }: Props = $props()
|
||||
|
||||
// Format the suggestion for display
|
||||
const displayValue = $derived(() => {
|
||||
if (suggestion === null || suggestion === undefined) return 'None'
|
||||
if (typeof suggestion === 'boolean') return suggestion ? 'Yes' : 'No'
|
||||
return String(suggestion)
|
||||
})
|
||||
|
||||
let isOpen = $state(false)
|
||||
</script>
|
||||
|
||||
{#if suggestion !== undefined && suggestion !== null && !dismissed}
|
||||
<TooltipBase.Root bind:open={isOpen} delayDuration={0}>
|
||||
<TooltipBase.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<button
|
||||
{...props}
|
||||
type="button"
|
||||
class="suggestion-badge"
|
||||
aria-label="Wiki suggestion available"
|
||||
>
|
||||
<Icon name="sparkles" size={14} />
|
||||
</button>
|
||||
{/snippet}
|
||||
</TooltipBase.Trigger>
|
||||
<TooltipBase.Content class="suggestion-tooltip" sideOffset={4}>
|
||||
<div class="suggestion-content">
|
||||
{#if label}
|
||||
<span class="suggestion-label">{label}:</span>
|
||||
{/if}
|
||||
<span class="suggestion-value">{displayValue()}</span>
|
||||
</div>
|
||||
<div class="suggestion-actions">
|
||||
{#if onAccept}
|
||||
<button
|
||||
type="button"
|
||||
class="action accept"
|
||||
onclick={() => {
|
||||
onAccept?.()
|
||||
isOpen = false
|
||||
}}
|
||||
title="Accept suggestion"
|
||||
>
|
||||
<Icon name="check" size={14} />
|
||||
</button>
|
||||
{/if}
|
||||
{#if onDismiss}
|
||||
<button
|
||||
type="button"
|
||||
class="action dismiss"
|
||||
onclick={() => {
|
||||
onDismiss?.()
|
||||
isOpen = false
|
||||
}}
|
||||
title="Dismiss suggestion"
|
||||
>
|
||||
<Icon name="x" size={14} />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</TooltipBase.Content>
|
||||
</TooltipBase.Root>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/layout' as *;
|
||||
@use '$src/themes/typography' as *;
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/colors' as *;
|
||||
|
||||
.suggestion-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: calc($unit * 2.5);
|
||||
height: calc($unit * 2.5);
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $wind-text-20 0%, $water-text-20 100%);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin-left: $unit-half;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 8px rgba($wind-text-20, 0.5);
|
||||
}
|
||||
|
||||
:global(svg) {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.suggestion-tooltip) {
|
||||
background: var(--tooltip-bg, #2a2a2a);
|
||||
color: var(--tooltip-text, white);
|
||||
padding: $unit;
|
||||
border-radius: $item-corner;
|
||||
font-size: $font-small;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.suggestion-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-fourth;
|
||||
}
|
||||
|
||||
.suggestion-label {
|
||||
font-size: calc($font-small * 0.9);
|
||||
color: var(--text-secondary);
|
||||
font-weight: $normal;
|
||||
}
|
||||
|
||||
.suggestion-value {
|
||||
font-weight: $medium;
|
||||
color: white;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.suggestion-actions {
|
||||
display: flex;
|
||||
gap: $unit-half;
|
||||
justify-content: flex-end;
|
||||
padding-top: $unit-half;
|
||||
border-top: 1px solid rgba(white, 0.1);
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: calc($unit * 3.5);
|
||||
height: calc($unit * 3.5);
|
||||
border-radius: $item-corner-small;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
&.accept {
|
||||
background: $wind-text-20;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: $wind-text-10;
|
||||
}
|
||||
}
|
||||
|
||||
&.dismiss {
|
||||
background: $grey-60;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: $grey-50;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte'
|
||||
import Input from './Input.svelte'
|
||||
import Select from './Select.svelte'
|
||||
import Checkbox from './checkbox/Checkbox.svelte'
|
||||
import DatePicker from './DatePicker.svelte'
|
||||
import SuggestionBadge from './SuggestionBadge.svelte'
|
||||
import Icon from '../Icon.svelte'
|
||||
|
||||
interface SelectOption {
|
||||
value: string | number
|
||||
label: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
label,
|
||||
sublabel,
|
||||
value = $bindable(),
|
||||
children,
|
||||
editable = false,
|
||||
type = 'text',
|
||||
options,
|
||||
placeholder,
|
||||
element,
|
||||
onchange,
|
||||
width,
|
||||
linkUrl,
|
||||
hasLinkButton = false,
|
||||
// Suggestion props
|
||||
suggestion,
|
||||
suggestionLabel,
|
||||
dismissedSuggestion = false,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
}: {
|
||||
label: string
|
||||
/** Secondary label displayed below the main label */
|
||||
sublabel?: string
|
||||
value?: string | number | boolean | null | undefined
|
||||
children?: Snippet
|
||||
editable?: boolean
|
||||
type?: 'text' | 'number' | 'select' | 'checkbox' | 'date'
|
||||
options?: SelectOption[]
|
||||
placeholder?: string
|
||||
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
||||
/** Callback for checkbox type when value changes */
|
||||
onchange?: (checked: boolean) => void
|
||||
/** Custom width for the input field (e.g., '320px') */
|
||||
width?: string
|
||||
/** URL to open when link button is clicked */
|
||||
linkUrl?: string | null
|
||||
/** Whether to show the link button (disabled when linkUrl is empty) */
|
||||
hasLinkButton?: boolean
|
||||
// Suggestion props
|
||||
/** The suggested value from wiki */
|
||||
suggestion?: string | number | boolean | null | undefined
|
||||
/** Label for the suggestion tooltip */
|
||||
suggestionLabel?: string
|
||||
/** Whether the suggestion has been dismissed */
|
||||
dismissedSuggestion?: boolean
|
||||
/** Callback when user accepts the suggestion */
|
||||
onAcceptSuggestion?: () => void
|
||||
/** Callback when user dismisses the suggestion */
|
||||
onDismissSuggestion?: () => void
|
||||
} = $props()
|
||||
|
||||
// For checkbox type, derive the checked state from value
|
||||
const checkboxValue = $derived(type === 'checkbox' ? Boolean(value) : false)
|
||||
|
||||
// Show link button when hasLinkButton is true or linkUrl is provided
|
||||
const showLinkButton = $derived(hasLinkButton || !!linkUrl)
|
||||
const linkDisabled = $derived(!linkUrl)
|
||||
|
||||
// Handle checkbox change and call onchange if provided
|
||||
function handleCheckboxChange(checked: boolean) {
|
||||
value = checked as any
|
||||
onchange?.(checked)
|
||||
}
|
||||
|
||||
// Open URL in new tab
|
||||
function openLink() {
|
||||
if (linkUrl) {
|
||||
window.open(linkUrl, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
}
|
||||
|
||||
// Show suggestion badge when:
|
||||
// 1. We have a suggestion
|
||||
// 2. The suggestion hasn't been dismissed
|
||||
// 3. The current value is different from the suggestion (or value is empty)
|
||||
const showSuggestion = $derived(
|
||||
suggestion !== undefined &&
|
||||
suggestion !== null &&
|
||||
!dismissedSuggestion &&
|
||||
(value === undefined || value === null || value === '' || value !== suggestion)
|
||||
)
|
||||
|
||||
// Format suggestion for display in badge
|
||||
const formattedSuggestion = $derived(() => {
|
||||
if (type === 'select' && options && suggestion !== undefined && suggestion !== null) {
|
||||
const opt = options.find((o) => o.value === suggestion)
|
||||
return opt?.label ?? suggestion
|
||||
}
|
||||
return suggestion
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="detail-item" class:editable class:hasChildren={!!children}>
|
||||
<div class="label-container">
|
||||
<div class="label-row">
|
||||
<span class="label">{label}</span>
|
||||
{#if editable && showSuggestion}
|
||||
<SuggestionBadge
|
||||
suggestion={formattedSuggestion()}
|
||||
label={suggestionLabel || label}
|
||||
dismissed={dismissedSuggestion}
|
||||
onAccept={onAcceptSuggestion}
|
||||
onDismiss={onDismissSuggestion}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{#if sublabel}
|
||||
<span class="sublabel">{sublabel}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if editable}
|
||||
<div class="edit-value" style:--custom-width={width}>
|
||||
{#if type === 'select' && options}
|
||||
<Select
|
||||
bind:value={value as string | number | undefined}
|
||||
{options}
|
||||
{placeholder}
|
||||
size="medium"
|
||||
contained
|
||||
/>
|
||||
{:else if type === 'checkbox'}
|
||||
<Checkbox
|
||||
checked={checkboxValue}
|
||||
onCheckedChange={handleCheckboxChange}
|
||||
contained
|
||||
{element}
|
||||
/>
|
||||
{:else if type === 'number'}
|
||||
<Input
|
||||
bind:value
|
||||
type="number"
|
||||
variant="number"
|
||||
contained={true}
|
||||
{placeholder}
|
||||
alignRight={true}
|
||||
/>
|
||||
{:else if type === 'date'}
|
||||
<DatePicker bind:value={value as string | null} contained={true} {placeholder} />
|
||||
{:else}
|
||||
<Input bind:value type="text" contained={true} {placeholder} alignRight={false} />
|
||||
{#if showLinkButton}
|
||||
<button
|
||||
type="button"
|
||||
class="link-button"
|
||||
onclick={openLink}
|
||||
disabled={linkDisabled}
|
||||
title={linkDisabled ? 'No link available' : 'Open link'}
|
||||
>
|
||||
<Icon name="link" size={16} />
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{:else if children}
|
||||
<div class="value" class:edit-value={editable}>
|
||||
{@render children()}
|
||||
</div>
|
||||
{:else}
|
||||
<span class="value">{value || '—'}</span>
|
||||
{/if}
|
||||
</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;
|
||||
@use '$src/themes/effects' as effects;
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: spacing.$unit 0;
|
||||
border-radius: layout.$item-corner;
|
||||
font-size: typography.$font-regular;
|
||||
min-height: calc(spacing.$unit * 5);
|
||||
|
||||
&:not(.editable) {
|
||||
padding: spacing.$unit;
|
||||
margin: 0 calc(spacing.$unit * -1);
|
||||
}
|
||||
|
||||
&:hover:not(.editable):not(.hasChildren) {
|
||||
background: colors.$grey-90;
|
||||
}
|
||||
|
||||
&.editable:focus-within,
|
||||
&.hasChildren:focus-within {
|
||||
background: var(--input-bg-hover);
|
||||
}
|
||||
|
||||
&.editable,
|
||||
&.hasChildren {
|
||||
background: var(--input-bg);
|
||||
}
|
||||
|
||||
.label-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
margin-right: spacing.$unit-2x;
|
||||
gap: spacing.$unit-fourth;
|
||||
}
|
||||
|
||||
.label-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: typography.$medium;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.sublabel {
|
||||
font-size: typography.$font-small;
|
||||
color: var(--text-tertiary);
|
||||
font-weight: typography.$normal;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: colors.$grey-30;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.edit-value {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-grow: 0;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: spacing.$unit-half;
|
||||
|
||||
:global(.input),
|
||||
:global(.select) {
|
||||
width: var(--custom-width, 240px);
|
||||
}
|
||||
|
||||
:global(.input.number) {
|
||||
width: var(--custom-width, 120px);
|
||||
}
|
||||
|
||||
.link-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: layout.$item-corner;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
@include effects.smooth-transition(effects.$duration-quick, background-color, color, opacity);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: colors.$grey-90;
|
||||
color: colors.$grey-30;
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
background: colors.$grey-80;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { CharacterSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import CopyableText from '$lib/components/ui/CopyableText.svelte'
|
||||
import Select from '$lib/components/ui/Select.svelte'
|
||||
import MultiSelect from '$lib/components/ui/MultiSelect.svelte'
|
||||
|
|
@ -16,21 +14,12 @@
|
|||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: CharacterSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
character,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
const rarityOptions = getRarityOptions()
|
||||
|
|
@ -100,16 +89,12 @@
|
|||
onAcceptSuggestion={() => onAcceptSuggestion?.('nameJp', suggestions?.nameJp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('nameJp')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Rarity"
|
||||
bind:value={editData.rarity}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={rarityOptions}
|
||||
suggestion={suggestions?.rarity}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('rarity')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('rarity', suggestions?.rarity)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('rarity')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
label="Granblue ID"
|
||||
|
|
|
|||
|
|
@ -1,30 +1,19 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { CharacterSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
|
||||
interface Props {
|
||||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: CharacterSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
character,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
const flb = $derived(editMode ? Boolean(editData.flb) : Boolean(character?.uncap?.flb))
|
||||
|
|
@ -33,39 +22,27 @@
|
|||
|
||||
<DetailsContainer title="HP Stats">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Base HP"
|
||||
bind:value={editData.minHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.minHp}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('minHp')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('minHp', suggestions?.minHp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('minHp')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max HP"
|
||||
bind:value={editData.maxHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxHp}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxHp')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxHp', suggestions?.maxHp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxHp')}
|
||||
/>
|
||||
{#if flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max HP (FLB)"
|
||||
bind:value={editData.maxHpFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxHpFlb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxHpFlb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxHpFlb', suggestions?.maxHpFlb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxHpFlb')}
|
||||
/>
|
||||
{/if}
|
||||
{#if ulb}
|
||||
|
|
@ -85,39 +62,27 @@
|
|||
|
||||
<DetailsContainer title="Attack Stats">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Base Attack"
|
||||
bind:value={editData.minAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.minAtk}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('minAtk')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('minAtk', suggestions?.minAtk)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('minAtk')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max Attack"
|
||||
bind:value={editData.maxAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxAtk}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxAtk')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxAtk', suggestions?.maxAtk)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxAtk')}
|
||||
/>
|
||||
{#if flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max Attack (FLB)"
|
||||
bind:value={editData.maxAtkFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxAtkFlb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxAtkFlb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxAtkFlb', suggestions?.maxAtkFlb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxAtkFlb')}
|
||||
/>
|
||||
{/if}
|
||||
{#if ulb}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { CharacterSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import ElementLabel from '$lib/components/labels/ElementLabel.svelte'
|
||||
import ProficiencyLabel from '$lib/components/labels/ProficiencyLabel.svelte'
|
||||
import { getElementOptions } from '$lib/utils/element'
|
||||
|
|
@ -16,21 +14,12 @@
|
|||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: CharacterSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
character,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
const elementOptions = getElementOptions()
|
||||
|
|
@ -41,71 +30,47 @@
|
|||
|
||||
<DetailsContainer title="Details">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Element"
|
||||
bind:value={editData.element}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={elementOptions}
|
||||
suggestion={suggestions?.element}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('element')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('element', suggestions?.element)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('element')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Race 1"
|
||||
bind:value={editData.race1}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={raceOptions}
|
||||
suggestion={suggestions?.race1}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('race1')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('race1', suggestions?.race1)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('race1')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Race 2"
|
||||
bind:value={editData.race2}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={raceOptions}
|
||||
suggestion={suggestions?.race2}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('race2')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('race2', suggestions?.race2)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('race2')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Gender"
|
||||
bind:value={editData.gender}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={genderOptions}
|
||||
suggestion={suggestions?.gender}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('gender')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('gender', suggestions?.gender)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('gender')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Proficiency 1"
|
||||
bind:value={editData.proficiency1}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={proficiencyOptions}
|
||||
suggestion={suggestions?.proficiency1}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('proficiency1')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('proficiency1', suggestions?.proficiency1)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('proficiency1')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Proficiency 2"
|
||||
bind:value={editData.proficiency2}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={proficiencyOptions}
|
||||
suggestion={suggestions?.proficiency2}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('proficiency2')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('proficiency2', suggestions?.proficiency2)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('proficiency2')}
|
||||
/>
|
||||
{:else}
|
||||
<DetailItem label="Element">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { CharacterSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
|
||||
import { getCharacterMaxUncapLevel } from '$lib/utils/uncap'
|
||||
import { getElementLabel } from '$lib/utils/element'
|
||||
|
|
@ -15,11 +13,6 @@
|
|||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: CharacterSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
// Callback when editData is modified (for triggering reactivity in parent)
|
||||
onDataChange?: () => void
|
||||
}
|
||||
|
|
@ -28,10 +21,6 @@
|
|||
character,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion,
|
||||
onDataChange
|
||||
}: Props = $props()
|
||||
|
||||
|
|
@ -115,29 +104,21 @@
|
|||
<DetailItem label="Transcendence" value={transcendence ? 'Yes' : 'No'} />
|
||||
<DetailItem label="Special" value={special ? 'Yes' : 'No'} />
|
||||
{:else}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="FLB"
|
||||
bind:value={editData.flb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
onchange={handleFlbChange}
|
||||
suggestion={suggestions?.flb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('flb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('flb', suggestions?.flb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('flb')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="ULB"
|
||||
bind:value={editData.ulb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
onchange={handleUlbChange}
|
||||
suggestion={suggestions?.ulb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('ulb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('ulb', suggestions?.ulb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('ulb')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Transcendence"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { SummonSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import MultiSelect from '$lib/components/ui/MultiSelect.svelte'
|
||||
|
|
@ -11,21 +10,12 @@
|
|||
summon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: SummonSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
summon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
// Promotion options for multiselect
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { SummonSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import CopyableText from '$lib/components/ui/CopyableText.svelte'
|
||||
import { getRarityLabel, getRarityOptions } from '$lib/utils/rarity'
|
||||
|
||||
|
|
@ -12,21 +10,12 @@
|
|||
summon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: SummonSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
summon,
|
||||
editMode = false,
|
||||
editData = $bindable<any>(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable<any>()
|
||||
}: Props = $props()
|
||||
|
||||
const rarityOptions = getRarityOptions()
|
||||
|
|
@ -56,16 +45,12 @@
|
|||
onAcceptSuggestion={() => onAcceptSuggestion?.('nameJp', suggestions?.nameJp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('nameJp')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Rarity"
|
||||
bind:value={editData.rarity}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={rarityOptions}
|
||||
suggestion={suggestions?.rarity}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('rarity')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('rarity', suggestions?.rarity)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('rarity')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Granblue ID"
|
||||
|
|
|
|||
|
|
@ -1,30 +1,19 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { SummonSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
|
||||
interface Props {
|
||||
summon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: SummonSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
summon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
const flb = $derived(editMode ? Boolean(editData.flb) : Boolean(summon?.uncap?.flb))
|
||||
|
|
@ -52,39 +41,27 @@
|
|||
|
||||
<DetailsContainer title="HP Stats">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Base HP"
|
||||
bind:value={editData.minHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.minHp}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('minHp')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('minHp', suggestions?.minHp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('minHp')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max HP"
|
||||
bind:value={editData.maxHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxHp}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxHp')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxHp', suggestions?.maxHp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxHp')}
|
||||
/>
|
||||
{#if flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max HP (FLB)"
|
||||
bind:value={editData.maxHpFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxHpFlb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxHpFlb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxHpFlb', suggestions?.maxHpFlb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxHpFlb')}
|
||||
/>
|
||||
{/if}
|
||||
{#if ulb}
|
||||
|
|
@ -122,39 +99,27 @@
|
|||
|
||||
<DetailsContainer title="Attack Stats">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Base Attack"
|
||||
bind:value={editData.minAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.minAtk}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('minAtk')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('minAtk', suggestions?.minAtk)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('minAtk')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max Attack"
|
||||
bind:value={editData.maxAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxAtk}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxAtk')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxAtk', suggestions?.maxAtk)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxAtk')}
|
||||
/>
|
||||
{#if flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max Attack (FLB)"
|
||||
bind:value={editData.maxAtkFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxAtkFlb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxAtkFlb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxAtkFlb', suggestions?.maxAtkFlb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxAtkFlb')}
|
||||
/>
|
||||
{/if}
|
||||
{#if ulb}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { createQuery } from '@tanstack/svelte-query'
|
||||
import type { SummonSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import { entityQueries } from '$lib/api/queries/entity.queries'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import ElementLabel from '$lib/components/labels/ElementLabel.svelte'
|
||||
import { getElementLabel, getElementOptions } from '$lib/utils/element'
|
||||
import type { SummonSeriesRef } from '$lib/types/api/summonSeries'
|
||||
|
|
@ -17,21 +15,12 @@
|
|||
summon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: SummonSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
summon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
// Fetch summon series list from API
|
||||
|
|
@ -67,16 +56,12 @@
|
|||
|
||||
<DetailsContainer title="Details">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Element"
|
||||
bind:value={editData.element}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={elementOptions}
|
||||
suggestion={suggestions?.element}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('element')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('element', suggestions?.element)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('element')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Series"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { SummonSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
|
||||
import { getSummonMaxUncapLevel } from '$lib/utils/uncap'
|
||||
import { getElementLabel } from '$lib/utils/element'
|
||||
|
|
@ -15,11 +13,6 @@
|
|||
summon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: SummonSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
// Callback when editData is modified (for triggering reactivity in parent)
|
||||
onDataChange?: () => void
|
||||
}
|
||||
|
|
@ -28,10 +21,6 @@
|
|||
summon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion,
|
||||
onDataChange
|
||||
}: Props = $props()
|
||||
|
||||
|
|
@ -101,29 +90,21 @@
|
|||
{/if}
|
||||
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="FLB"
|
||||
bind:value={editData.flb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
onchange={handleFlbChange}
|
||||
suggestion={suggestions?.flb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('flb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('flb', suggestions?.flb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('flb')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="ULB"
|
||||
bind:value={editData.ulb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
onchange={handleUlbChange}
|
||||
suggestion={suggestions?.ulb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('ulb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('ulb', suggestions?.ulb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('ulb')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Transcendence"
|
||||
|
|
@ -133,16 +114,12 @@
|
|||
element={elementName}
|
||||
onchange={handleTranscendenceChange}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Subaura"
|
||||
bind:value={editData.subaura}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
suggestion={suggestions?.subaura}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('subaura')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('subaura', suggestions?.subaura)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('subaura')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Limit"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { WeaponSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import MultiSelect from '$lib/components/ui/MultiSelect.svelte'
|
||||
|
|
@ -13,21 +12,12 @@
|
|||
weapon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: WeaponSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
weapon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
// Promotion options for multiselect
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { WeaponSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import CopyableText from '$lib/components/ui/CopyableText.svelte'
|
||||
import { getRarityLabel, getRarityOptions } from '$lib/utils/rarity'
|
||||
|
||||
|
|
@ -12,21 +10,12 @@
|
|||
weapon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: WeaponSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
weapon,
|
||||
editMode = false,
|
||||
editData = $bindable<any>(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable<any>()
|
||||
}: Props = $props()
|
||||
|
||||
const rarityOptions = getRarityOptions()
|
||||
|
|
@ -56,16 +45,12 @@
|
|||
onAcceptSuggestion={() => onAcceptSuggestion?.('nameJp', suggestions?.nameJp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('nameJp')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Rarity"
|
||||
bind:value={editData.rarity}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={rarityOptions}
|
||||
suggestion={suggestions?.rarity}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('rarity')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('rarity', suggestions?.rarity)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('rarity')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Granblue ID"
|
||||
|
|
|
|||
|
|
@ -1,30 +1,19 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { WeaponSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
|
||||
interface Props {
|
||||
weapon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: WeaponSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
weapon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
const flb = $derived(editMode ? Boolean(editData.flb) : Boolean(weapon?.uncap?.flb))
|
||||
|
|
@ -56,39 +45,27 @@
|
|||
|
||||
<DetailsContainer title="HP Stats">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Base HP"
|
||||
bind:value={editData.minHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.minHp}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('minHp')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('minHp', suggestions?.minHp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('minHp')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max HP"
|
||||
bind:value={editData.maxHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxHp}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxHp')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxHp', suggestions?.maxHp)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxHp')}
|
||||
/>
|
||||
{#if flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max HP (FLB)"
|
||||
bind:value={editData.maxHpFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxHpFlb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxHpFlb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxHpFlb', suggestions?.maxHpFlb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxHpFlb')}
|
||||
/>
|
||||
{/if}
|
||||
{#if ulb}
|
||||
|
|
@ -114,39 +91,27 @@
|
|||
|
||||
<DetailsContainer title="Attack Stats">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Base Attack"
|
||||
bind:value={editData.minAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.minAtk}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('minAtk')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('minAtk', suggestions?.minAtk)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('minAtk')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max Attack"
|
||||
bind:value={editData.maxAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxAtk}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxAtk')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxAtk', suggestions?.maxAtk)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxAtk')}
|
||||
/>
|
||||
{#if flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Max Attack (FLB)"
|
||||
bind:value={editData.maxAtkFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
suggestion={suggestions?.maxAtkFlb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('maxAtkFlb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('maxAtkFlb', suggestions?.maxAtkFlb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('maxAtkFlb')}
|
||||
/>
|
||||
{/if}
|
||||
{#if ulb}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { createQuery } from '@tanstack/svelte-query'
|
||||
import type { WeaponSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import { entityQueries } from '$lib/api/queries/entity.queries'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import ElementLabel from '$lib/components/labels/ElementLabel.svelte'
|
||||
import ProficiencyLabel from '$lib/components/labels/ProficiencyLabel.svelte'
|
||||
import { getElementLabel, getElementOptions } from '$lib/utils/element'
|
||||
|
|
@ -20,21 +18,12 @@
|
|||
weapon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: WeaponSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
weapon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion
|
||||
editData = $bindable()
|
||||
}: Props = $props()
|
||||
|
||||
// Fetch weapon series list from API
|
||||
|
|
@ -72,27 +61,19 @@
|
|||
|
||||
<DetailsContainer title="Details">
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Element"
|
||||
bind:value={editData.element}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={elementOptions}
|
||||
suggestion={suggestions?.element}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('element')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('element', suggestions?.element)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('element')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Proficiency"
|
||||
bind:value={editData.proficiency}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={proficiencyOptions}
|
||||
suggestion={suggestions?.proficiency}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('proficiency')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('proficiency', suggestions?.proficiency)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('proficiency')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Series"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<svelte:options runes={true} />
|
||||
|
||||
<script lang="ts">
|
||||
import type { WeaponSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
|
||||
import { getElementLabel } from '$lib/utils/element'
|
||||
|
||||
|
|
@ -14,11 +12,6 @@
|
|||
weapon: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
// Suggestion support for batch import
|
||||
suggestions?: WeaponSuggestions
|
||||
dismissedSuggestions?: Set<string>
|
||||
onAcceptSuggestion?: (field: string, value: any) => void
|
||||
onDismissSuggestion?: (field: string) => void
|
||||
// Callback when editData is modified (for triggering reactivity in parent)
|
||||
onDataChange?: () => void
|
||||
}
|
||||
|
|
@ -27,10 +20,6 @@
|
|||
weapon,
|
||||
editMode = false,
|
||||
editData = $bindable(),
|
||||
suggestions,
|
||||
dismissedSuggestions,
|
||||
onAcceptSuggestion,
|
||||
onDismissSuggestion,
|
||||
onDataChange
|
||||
}: Props = $props()
|
||||
|
||||
|
|
@ -116,29 +105,21 @@
|
|||
{/if}
|
||||
|
||||
{#if editMode}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="FLB"
|
||||
bind:value={editData.flb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
onchange={handleFlbChange}
|
||||
suggestion={suggestions?.flb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('flb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('flb', suggestions?.flb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('flb')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="ULB"
|
||||
bind:value={editData.ulb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
onchange={handleUlbChange}
|
||||
suggestion={suggestions?.ulb}
|
||||
dismissedSuggestion={dismissedSuggestions?.has('ulb')}
|
||||
onAcceptSuggestion={() => onAcceptSuggestion?.('ulb', suggestions?.ulb)}
|
||||
onDismissSuggestion={() => onDismissSuggestion?.('ulb')}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Transcendence"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import PageMeta from '$lib/components/PageMeta.svelte'
|
||||
import * as m from '$lib/paraglide/messages'
|
||||
import { goto } from '$app/navigation'
|
||||
import { entityAdapter, type CharacterSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import { entityAdapter, type ParsedCharacterData } from '$lib/api/adapters/entity.adapter'
|
||||
import { fetchWikiPages, buildWikiDataMap } from '$lib/api/wiki'
|
||||
import { getGameCdnCharacterImage, getPlaceholderImage } from '$lib/utils/images'
|
||||
import {
|
||||
|
|
@ -24,7 +24,6 @@
|
|||
import type { EntityTab } from '$lib/features/database/import/TabbedEntitySelector.svelte'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import SidebarHeader from '$lib/components/ui/SidebarHeader.svelte'
|
||||
import Button from '$lib/components/ui/Button.svelte'
|
||||
import Input from '$lib/components/ui/Input.svelte'
|
||||
|
|
@ -38,7 +37,7 @@
|
|||
wikiPage: string
|
||||
status: 'loading' | 'success' | 'error'
|
||||
granblueId?: string
|
||||
suggestions?: CharacterSuggestions
|
||||
parsedData?: ParsedCharacterData
|
||||
wikiRaw?: string
|
||||
error?: string
|
||||
}
|
||||
|
|
@ -56,7 +55,6 @@
|
|||
|
||||
// Form data per entity (keyed by wikiPage) - using Record for proper reactivity
|
||||
let formDataByPage = $state<Record<string, any>>({})
|
||||
let dismissedByPage = $state<Record<string, Set<string>>>({})
|
||||
let savedEntities = $state<Set<string>>(new Set())
|
||||
|
||||
// Store wiki raw data per entity for sending with create request
|
||||
|
|
@ -118,44 +116,44 @@
|
|||
)
|
||||
|
||||
// Initialize empty form data for an entity
|
||||
function createEmptyFormData(wikiPage: string, suggestions?: CharacterSuggestions) {
|
||||
function createEmptyFormData(wikiPage: string, parsedData?: ParsedCharacterData) {
|
||||
return {
|
||||
name: suggestions?.nameEn ?? '',
|
||||
nameJp: suggestions?.nameJp ?? '',
|
||||
granblueId: suggestions?.granblueId ?? '',
|
||||
characterId: suggestions?.characterId?.join(', ') ?? '',
|
||||
rarity: suggestions?.rarity ?? 3,
|
||||
element: suggestions?.element ?? 0,
|
||||
race1: suggestions?.race1 ?? null,
|
||||
race2: suggestions?.race2 ?? null,
|
||||
gender: suggestions?.gender ?? 0,
|
||||
proficiency1: suggestions?.proficiency1 ?? 0,
|
||||
proficiency2: suggestions?.proficiency2 ?? 0,
|
||||
season: suggestions?.season ?? (null as number | null),
|
||||
series: suggestions?.series ?? ([] as number[]),
|
||||
minHp: suggestions?.minHp ?? 0,
|
||||
maxHp: suggestions?.maxHp ?? 0,
|
||||
maxHpFlb: suggestions?.maxHpFlb ?? 0,
|
||||
name: parsedData?.nameEn ?? '',
|
||||
nameJp: parsedData?.nameJp ?? '',
|
||||
granblueId: parsedData?.granblueId ?? '',
|
||||
characterId: parsedData?.characterId?.join(', ') ?? '',
|
||||
rarity: parsedData?.rarity ?? 3,
|
||||
element: parsedData?.element ?? 0,
|
||||
race1: parsedData?.race1 ?? null,
|
||||
race2: parsedData?.race2 ?? null,
|
||||
gender: parsedData?.gender ?? 0,
|
||||
proficiency1: parsedData?.proficiency1 ?? 0,
|
||||
proficiency2: parsedData?.proficiency2 ?? 0,
|
||||
season: parsedData?.season ?? (null as number | null),
|
||||
series: parsedData?.series ?? ([] as number[]),
|
||||
minHp: parsedData?.minHp ?? 0,
|
||||
maxHp: parsedData?.maxHp ?? 0,
|
||||
maxHpFlb: parsedData?.maxHpFlb ?? 0,
|
||||
maxHpUlb: 0,
|
||||
minAtk: suggestions?.minAtk ?? 0,
|
||||
maxAtk: suggestions?.maxAtk ?? 0,
|
||||
maxAtkFlb: suggestions?.maxAtkFlb ?? 0,
|
||||
minAtk: parsedData?.minAtk ?? 0,
|
||||
maxAtk: parsedData?.maxAtk ?? 0,
|
||||
maxAtkFlb: parsedData?.maxAtkFlb ?? 0,
|
||||
maxAtkUlb: 0,
|
||||
baseDa: 0,
|
||||
baseTa: 0,
|
||||
ougiRatio: 0,
|
||||
ougiRatioFlb: 0,
|
||||
flb: suggestions?.flb ?? false,
|
||||
ulb: suggestions?.ulb ?? false,
|
||||
flb: parsedData?.flb ?? false,
|
||||
ulb: parsedData?.ulb ?? false,
|
||||
transcendence: false,
|
||||
special: false,
|
||||
releaseDate: suggestions?.releaseDate ?? '',
|
||||
flbDate: suggestions?.flbDate ?? '',
|
||||
ulbDate: suggestions?.ulbDate ?? '',
|
||||
releaseDate: parsedData?.releaseDate ?? '',
|
||||
flbDate: parsedData?.flbDate ?? '',
|
||||
ulbDate: parsedData?.ulbDate ?? '',
|
||||
wikiEn: wikiPage ? wikiPage.replace(/ /g, '_') : '',
|
||||
wikiJa: '',
|
||||
gamewith: suggestions?.gamewith ?? '',
|
||||
kamigame: suggestions?.kamigame ?? '',
|
||||
gamewith: parsedData?.gamewith ?? '',
|
||||
kamigame: parsedData?.kamigame ?? '',
|
||||
nicknamesEn: [] as string[],
|
||||
nicknamesJp: [] as string[]
|
||||
}
|
||||
|
|
@ -220,22 +218,20 @@
|
|||
wikiPage: result.wikiPage,
|
||||
status: result.status,
|
||||
granblueId: result.granblueId,
|
||||
suggestions: result.suggestions,
|
||||
parsedData: result.parsedData,
|
||||
wikiRaw: result.wikiRaw,
|
||||
error: result.error
|
||||
})
|
||||
|
||||
// Create form data for successful results
|
||||
if (result.status === 'success') {
|
||||
formDataByPage[result.wikiPage] = createEmptyFormData(result.wikiPage, result.suggestions)
|
||||
dismissedByPage[result.wikiPage] = new Set<string>()
|
||||
formDataByPage[result.wikiPage] = createEmptyFormData(result.wikiPage, result.parsedData)
|
||||
}
|
||||
})
|
||||
entities = updatedEntities
|
||||
|
||||
// Trigger reactivity by reassigning
|
||||
formDataByPage = { ...formDataByPage }
|
||||
dismissedByPage = { ...dismissedByPage }
|
||||
} catch (error) {
|
||||
console.error('Batch preview error:', error)
|
||||
fetchError = 'Failed to fetch wiki data. Please try again.'
|
||||
|
|
@ -249,24 +245,6 @@
|
|||
selectedWikiPage = wikiPage
|
||||
}
|
||||
|
||||
// Accept a suggestion
|
||||
function handleAcceptSuggestion(field: string, value: any) {
|
||||
if (!selectedWikiPage || !formDataByPage[selectedWikiPage]) return
|
||||
|
||||
formDataByPage[selectedWikiPage][field] = value
|
||||
formDataByPage = { ...formDataByPage }
|
||||
}
|
||||
|
||||
// Dismiss a suggestion
|
||||
function handleDismissSuggestion(field: string) {
|
||||
if (!selectedWikiPage) return
|
||||
|
||||
const dismissed = dismissedByPage[selectedWikiPage] ?? new Set<string>()
|
||||
dismissed.add(field)
|
||||
dismissedByPage[selectedWikiPage] = dismissed
|
||||
dismissedByPage = { ...dismissedByPage }
|
||||
}
|
||||
|
||||
// Save current entity
|
||||
async function saveCurrentEntity() {
|
||||
if (!selectedWikiPage) return
|
||||
|
|
@ -466,47 +444,29 @@
|
|||
<p>Loading wiki data...</p>
|
||||
</div>
|
||||
{:else if selectedWikiPage && formDataByPage[selectedWikiPage]}
|
||||
{@const suggestions = selectedEntity.suggestions}
|
||||
{@const dismissed = dismissedByPage[selectedWikiPage] ?? new Set<string>()}
|
||||
<section class="details">
|
||||
<CharacterMetadataSection
|
||||
character={emptyCharacter}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<CharacterUncapSection
|
||||
character={emptyCharacter}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<CharacterTaxonomySection
|
||||
character={emptyCharacter}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<CharacterStatsSection
|
||||
character={emptyCharacter}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<DetailsContainer title="Nicknames">
|
||||
|
|
@ -519,42 +479,29 @@
|
|||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="Dates">
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Release Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].releaseDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.releaseDate}
|
||||
dismissedSuggestion={dismissed.has('releaseDate')}
|
||||
onAcceptSuggestion={() =>
|
||||
handleAcceptSuggestion('releaseDate', suggestions?.releaseDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('releaseDate')}
|
||||
/>
|
||||
{#if formDataByPage[selectedWikiPage].flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="FLB Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].flbDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.flbDate}
|
||||
dismissedSuggestion={dismissed.has('flbDate')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('flbDate', suggestions?.flbDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('flbDate')}
|
||||
/>
|
||||
{/if}
|
||||
{#if formDataByPage[selectedWikiPage].ulb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="ULB Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].ulbDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.ulbDate}
|
||||
dismissedSuggestion={dismissed.has('ulbDate')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('ulbDate', suggestions?.ulbDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('ulbDate')}
|
||||
/>
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
|
@ -580,7 +527,7 @@
|
|||
hasLinkButton={true}
|
||||
linkUrl={buildWikiJaUrl(formDataByPage[selectedWikiPage].wikiJa, 'character')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Gamewith"
|
||||
bind:value={formDataByPage[selectedWikiPage].gamewith}
|
||||
editable={true}
|
||||
|
|
@ -589,12 +536,8 @@
|
|||
width="480px"
|
||||
hasLinkButton={true}
|
||||
linkUrl={buildGamewithUrl(formDataByPage[selectedWikiPage].gamewith)}
|
||||
suggestion={suggestions?.gamewith}
|
||||
dismissedSuggestion={dismissed.has('gamewith')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('gamewith', suggestions?.gamewith)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('gamewith')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Kamigame"
|
||||
bind:value={formDataByPage[selectedWikiPage].kamigame}
|
||||
editable={true}
|
||||
|
|
@ -603,10 +546,6 @@
|
|||
width="480px"
|
||||
hasLinkButton={true}
|
||||
linkUrl={buildKamigameUrl(formDataByPage[selectedWikiPage].kamigame, 'character')}
|
||||
suggestion={suggestions?.kamigame}
|
||||
dismissedSuggestion={dismissed.has('kamigame')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('kamigame', suggestions?.kamigame)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('kamigame')}
|
||||
/>
|
||||
</DetailsContainer>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import PageMeta from '$lib/components/PageMeta.svelte'
|
||||
import * as m from '$lib/paraglide/messages'
|
||||
import { goto } from '$app/navigation'
|
||||
import { entityAdapter, type SummonSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import { entityAdapter, type ParsedSummonData } from '$lib/api/adapters/entity.adapter'
|
||||
import { fetchWikiPages, buildWikiDataMap } from '$lib/api/wiki'
|
||||
import { getGameCdnSummonImage, getPlaceholderImage } from '$lib/utils/images'
|
||||
import {
|
||||
|
|
@ -24,7 +24,6 @@
|
|||
import type { EntityTab } from '$lib/features/database/import/TabbedEntitySelector.svelte'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import SidebarHeader from '$lib/components/ui/SidebarHeader.svelte'
|
||||
import Button from '$lib/components/ui/Button.svelte'
|
||||
import Input from '$lib/components/ui/Input.svelte'
|
||||
|
|
@ -38,7 +37,7 @@
|
|||
wikiPage: string
|
||||
status: 'loading' | 'success' | 'error'
|
||||
granblueId?: string
|
||||
suggestions?: SummonSuggestions
|
||||
parsedData?: ParsedSummonData
|
||||
error?: string
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +54,6 @@
|
|||
|
||||
// Form data per entity (keyed by wikiPage) - using Record for proper reactivity
|
||||
let formDataByPage = $state<Record<string, any>>({})
|
||||
let dismissedByPage = $state<Record<string, Set<string>>>({})
|
||||
let savedEntities = $state<Set<string>>(new Set())
|
||||
|
||||
// Store wiki raw data per entity for sending with create request
|
||||
|
|
@ -117,40 +115,40 @@
|
|||
)
|
||||
|
||||
// Initialize empty form data for an entity
|
||||
function createEmptyFormData(wikiPage: string, suggestions?: SummonSuggestions) {
|
||||
function createEmptyFormData(wikiPage: string, parsedData?: ParsedSummonData) {
|
||||
return {
|
||||
name: suggestions?.nameEn ?? '',
|
||||
nameJp: suggestions?.nameJp ?? '',
|
||||
granblue_id: suggestions?.granblueId ?? '',
|
||||
summonId: suggestions?.summonId ?? '',
|
||||
rarity: suggestions?.rarity ?? 3,
|
||||
element: suggestions?.element ?? 0,
|
||||
name: parsedData?.nameEn ?? '',
|
||||
nameJp: parsedData?.nameJp ?? '',
|
||||
granblue_id: parsedData?.granblueId ?? '',
|
||||
summonId: parsedData?.summonId ?? '',
|
||||
rarity: parsedData?.rarity ?? 3,
|
||||
element: parsedData?.element ?? 0,
|
||||
series: '',
|
||||
promotions: [] as number[],
|
||||
minHp: suggestions?.minHp ?? 0,
|
||||
maxHp: suggestions?.maxHp ?? 0,
|
||||
maxHpFlb: suggestions?.maxHpFlb ?? 0,
|
||||
maxHpUlb: suggestions?.maxHpUlb ?? 0,
|
||||
minHp: parsedData?.minHp ?? 0,
|
||||
maxHp: parsedData?.maxHp ?? 0,
|
||||
maxHpFlb: parsedData?.maxHpFlb ?? 0,
|
||||
maxHpUlb: parsedData?.maxHpUlb ?? 0,
|
||||
maxHpTranscendence: 0,
|
||||
minAtk: suggestions?.minAtk ?? 0,
|
||||
maxAtk: suggestions?.maxAtk ?? 0,
|
||||
maxAtkFlb: suggestions?.maxAtkFlb ?? 0,
|
||||
maxAtkUlb: suggestions?.maxAtkUlb ?? 0,
|
||||
minAtk: parsedData?.minAtk ?? 0,
|
||||
maxAtk: parsedData?.maxAtk ?? 0,
|
||||
maxAtkFlb: parsedData?.maxAtkFlb ?? 0,
|
||||
maxAtkUlb: parsedData?.maxAtkUlb ?? 0,
|
||||
maxAtkTranscendence: 0,
|
||||
maxLevel: suggestions?.maxLevel ?? 100,
|
||||
flb: suggestions?.flb ?? false,
|
||||
ulb: suggestions?.ulb ?? false,
|
||||
transcendence: suggestions?.transcendence ?? false,
|
||||
subaura: suggestions?.subaura ?? false,
|
||||
maxLevel: parsedData?.maxLevel ?? 100,
|
||||
flb: parsedData?.flb ?? false,
|
||||
ulb: parsedData?.ulb ?? false,
|
||||
transcendence: parsedData?.transcendence ?? false,
|
||||
subaura: parsedData?.subaura ?? false,
|
||||
limit: false,
|
||||
releaseDate: suggestions?.releaseDate ?? '',
|
||||
flbDate: suggestions?.flbDate ?? '',
|
||||
ulbDate: suggestions?.ulbDate ?? '',
|
||||
releaseDate: parsedData?.releaseDate ?? '',
|
||||
flbDate: parsedData?.flbDate ?? '',
|
||||
ulbDate: parsedData?.ulbDate ?? '',
|
||||
transcendenceDate: '',
|
||||
wikiEn: wikiPage ? wikiPage.replace(/ /g, '_') : '',
|
||||
wikiJa: '',
|
||||
gamewith: suggestions?.gamewith ?? '',
|
||||
kamigame: suggestions?.kamigame ?? '',
|
||||
gamewith: parsedData?.gamewith ?? '',
|
||||
kamigame: parsedData?.kamigame ?? '',
|
||||
nicknamesEn: [] as string[],
|
||||
nicknamesJp: [] as string[]
|
||||
}
|
||||
|
|
@ -215,21 +213,19 @@
|
|||
wikiPage: result.wikiPage,
|
||||
status: result.status,
|
||||
granblueId: result.granblueId,
|
||||
suggestions: result.suggestions,
|
||||
parsedData: result.parsedData,
|
||||
error: result.error
|
||||
})
|
||||
|
||||
// Create form data for successful results
|
||||
if (result.status === 'success') {
|
||||
formDataByPage[result.wikiPage] = createEmptyFormData(result.wikiPage, result.suggestions)
|
||||
dismissedByPage[result.wikiPage] = new Set<string>()
|
||||
formDataByPage[result.wikiPage] = createEmptyFormData(result.wikiPage, result.parsedData)
|
||||
}
|
||||
})
|
||||
entities = updatedEntities
|
||||
|
||||
// Trigger reactivity by reassigning
|
||||
formDataByPage = { ...formDataByPage }
|
||||
dismissedByPage = { ...dismissedByPage }
|
||||
} catch (error) {
|
||||
console.error('Batch preview error:', error)
|
||||
fetchError = 'Failed to fetch wiki data. Please try again.'
|
||||
|
|
@ -243,24 +239,6 @@
|
|||
selectedWikiPage = wikiPage
|
||||
}
|
||||
|
||||
// Accept a suggestion
|
||||
function handleAcceptSuggestion(field: string, value: any) {
|
||||
if (!selectedWikiPage || !formDataByPage[selectedWikiPage]) return
|
||||
|
||||
formDataByPage[selectedWikiPage][field] = value
|
||||
formDataByPage = { ...formDataByPage }
|
||||
}
|
||||
|
||||
// Dismiss a suggestion
|
||||
function handleDismissSuggestion(field: string) {
|
||||
if (!selectedWikiPage) return
|
||||
|
||||
const dismissed = dismissedByPage[selectedWikiPage] ?? new Set<string>()
|
||||
dismissed.add(field)
|
||||
dismissedByPage[selectedWikiPage] = dismissed
|
||||
dismissedByPage = { ...dismissedByPage }
|
||||
}
|
||||
|
||||
// Save current entity
|
||||
async function saveCurrentEntity() {
|
||||
if (!selectedWikiPage) return
|
||||
|
|
@ -445,47 +423,29 @@
|
|||
<p>Loading wiki data...</p>
|
||||
</div>
|
||||
{:else if selectedWikiPage && formDataByPage[selectedWikiPage]}
|
||||
{@const suggestions = selectedEntity.suggestions}
|
||||
{@const dismissed = dismissedByPage[selectedWikiPage] ?? new Set<string>()}
|
||||
<section class="details">
|
||||
<SummonMetadataSection
|
||||
summon={emptySummon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<SummonUncapSection
|
||||
summon={emptySummon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<SummonTaxonomySection
|
||||
summon={emptySummon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<SummonStatsSection
|
||||
summon={emptySummon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<DetailsContainer title="Nicknames">
|
||||
|
|
@ -498,42 +458,29 @@
|
|||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="Dates">
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Release Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].releaseDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.releaseDate}
|
||||
dismissedSuggestion={dismissed.has('releaseDate')}
|
||||
onAcceptSuggestion={() =>
|
||||
handleAcceptSuggestion('releaseDate', suggestions?.releaseDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('releaseDate')}
|
||||
/>
|
||||
{#if formDataByPage[selectedWikiPage].flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="FLB Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].flbDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.flbDate}
|
||||
dismissedSuggestion={dismissed.has('flbDate')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('flbDate', suggestions?.flbDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('flbDate')}
|
||||
/>
|
||||
{/if}
|
||||
{#if formDataByPage[selectedWikiPage].ulb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="ULB Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].ulbDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.ulbDate}
|
||||
dismissedSuggestion={dismissed.has('ulbDate')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('ulbDate', suggestions?.ulbDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('ulbDate')}
|
||||
/>
|
||||
{/if}
|
||||
{#if formDataByPage[selectedWikiPage].transcendence}
|
||||
|
|
@ -568,7 +515,7 @@
|
|||
hasLinkButton={true}
|
||||
linkUrl={buildWikiJaUrl(formDataByPage[selectedWikiPage].wikiJa, 'summon')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Gamewith"
|
||||
bind:value={formDataByPage[selectedWikiPage].gamewith}
|
||||
editable={true}
|
||||
|
|
@ -577,12 +524,8 @@
|
|||
width="480px"
|
||||
hasLinkButton={true}
|
||||
linkUrl={buildGamewithUrl(formDataByPage[selectedWikiPage].gamewith)}
|
||||
suggestion={suggestions?.gamewith}
|
||||
dismissedSuggestion={dismissed.has('gamewith')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('gamewith', suggestions?.gamewith)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('gamewith')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Kamigame"
|
||||
bind:value={formDataByPage[selectedWikiPage].kamigame}
|
||||
editable={true}
|
||||
|
|
@ -591,10 +534,6 @@
|
|||
width="480px"
|
||||
hasLinkButton={true}
|
||||
linkUrl={buildKamigameUrl(formDataByPage[selectedWikiPage].kamigame, 'summon')}
|
||||
suggestion={suggestions?.kamigame}
|
||||
dismissedSuggestion={dismissed.has('kamigame')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('kamigame', suggestions?.kamigame)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('kamigame')}
|
||||
/>
|
||||
</DetailsContainer>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import PageMeta from '$lib/components/PageMeta.svelte'
|
||||
import * as m from '$lib/paraglide/messages'
|
||||
import { goto } from '$app/navigation'
|
||||
import { entityAdapter, type WeaponSuggestions } from '$lib/api/adapters/entity.adapter'
|
||||
import { entityAdapter, type ParsedWeaponData } from '$lib/api/adapters/entity.adapter'
|
||||
import { fetchWikiPages, buildWikiDataMap } from '$lib/api/wiki'
|
||||
import { getGameCdnWeaponImage, getPlaceholderImage } from '$lib/utils/images'
|
||||
import {
|
||||
|
|
@ -26,7 +26,6 @@
|
|||
import type { EntityTab } from '$lib/features/database/import/TabbedEntitySelector.svelte'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte'
|
||||
import SidebarHeader from '$lib/components/ui/SidebarHeader.svelte'
|
||||
import Button from '$lib/components/ui/Button.svelte'
|
||||
import Input from '$lib/components/ui/Input.svelte'
|
||||
|
|
@ -40,7 +39,7 @@
|
|||
wikiPage: string
|
||||
status: 'loading' | 'success' | 'error'
|
||||
granblueId?: string
|
||||
suggestions?: WeaponSuggestions
|
||||
parsedData?: ParsedWeaponData
|
||||
error?: string
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +56,6 @@
|
|||
|
||||
// Form data per entity (keyed by wikiPage) - using Record for proper reactivity
|
||||
let formDataByPage = $state<Record<string, any>>({})
|
||||
let dismissedByPage = $state<Record<string, Set<string>>>({})
|
||||
let savedEntities = $state<Set<string>>(new Set())
|
||||
|
||||
// Store wiki raw data per entity for sending with create request
|
||||
|
|
@ -117,26 +115,26 @@
|
|||
)
|
||||
|
||||
// Initialize empty form data for an entity
|
||||
function createEmptyFormData(wikiPage: string, suggestions?: WeaponSuggestions) {
|
||||
function createEmptyFormData(wikiPage: string, parsedData?: ParsedWeaponData) {
|
||||
return {
|
||||
name: suggestions?.nameEn ?? '',
|
||||
nameJp: suggestions?.nameJp ?? '',
|
||||
granblue_id: suggestions?.granblueId ?? '',
|
||||
rarity: suggestions?.rarity ?? 3,
|
||||
element: suggestions?.element ?? 0,
|
||||
proficiency: suggestions?.proficiency ?? 0,
|
||||
series: suggestions?.series ?? '',
|
||||
name: parsedData?.nameEn ?? '',
|
||||
nameJp: parsedData?.nameJp ?? '',
|
||||
granblue_id: parsedData?.granblueId ?? '',
|
||||
rarity: parsedData?.rarity ?? 3,
|
||||
element: parsedData?.element ?? 0,
|
||||
proficiency: parsedData?.proficiency ?? 0,
|
||||
series: parsedData?.series ?? '',
|
||||
newSeries: 0,
|
||||
promotions: [] as number[],
|
||||
minHp: suggestions?.minHp ?? 0,
|
||||
maxHp: suggestions?.maxHp ?? 0,
|
||||
maxHpFlb: suggestions?.maxHpFlb ?? 0,
|
||||
maxHpUlb: suggestions?.maxHpUlb ?? 0,
|
||||
minAtk: suggestions?.minAtk ?? 0,
|
||||
maxAtk: suggestions?.maxAtk ?? 0,
|
||||
maxAtkFlb: suggestions?.maxAtkFlb ?? 0,
|
||||
maxAtkUlb: suggestions?.maxAtkUlb ?? 0,
|
||||
maxLevel: suggestions?.maxLevel ?? 100,
|
||||
minHp: parsedData?.minHp ?? 0,
|
||||
maxHp: parsedData?.maxHp ?? 0,
|
||||
maxHpFlb: parsedData?.maxHpFlb ?? 0,
|
||||
maxHpUlb: parsedData?.maxHpUlb ?? 0,
|
||||
minAtk: parsedData?.minAtk ?? 0,
|
||||
maxAtk: parsedData?.maxAtk ?? 0,
|
||||
maxAtkFlb: parsedData?.maxAtkFlb ?? 0,
|
||||
maxAtkUlb: parsedData?.maxAtkUlb ?? 0,
|
||||
maxLevel: parsedData?.maxLevel ?? 100,
|
||||
maxSkillLevel: 10,
|
||||
maxAwakeningLevel: 0,
|
||||
maxExorcismLevel: null as number | null,
|
||||
|
|
@ -147,17 +145,17 @@
|
|||
extra: false,
|
||||
limit: false,
|
||||
ax: false,
|
||||
releaseDate: suggestions?.releaseDate ?? '',
|
||||
flbDate: suggestions?.flbDate ?? '',
|
||||
ulbDate: suggestions?.ulbDate ?? '',
|
||||
releaseDate: parsedData?.releaseDate ?? '',
|
||||
flbDate: parsedData?.flbDate ?? '',
|
||||
ulbDate: parsedData?.ulbDate ?? '',
|
||||
transcendenceDate: '',
|
||||
wikiEn: wikiPage ? wikiPage.replace(/ /g, '_') : '',
|
||||
wikiJa: '',
|
||||
gamewith: suggestions?.gamewith ?? '',
|
||||
kamigame: suggestions?.kamigame ?? '',
|
||||
gamewith: parsedData?.gamewith ?? '',
|
||||
kamigame: parsedData?.kamigame ?? '',
|
||||
nicknamesEn: [] as string[],
|
||||
nicknamesJp: [] as string[],
|
||||
recruits: suggestions?.recruits ?? null,
|
||||
recruits: parsedData?.recruits ?? null,
|
||||
// Forge chain
|
||||
forgedFrom: null as string | null,
|
||||
forgeOrder: null as number | null
|
||||
|
|
@ -223,21 +221,19 @@
|
|||
wikiPage: result.wikiPage,
|
||||
status: result.status,
|
||||
granblueId: result.granblueId,
|
||||
suggestions: result.suggestions,
|
||||
parsedData: result.parsedData,
|
||||
error: result.error
|
||||
})
|
||||
|
||||
// Create form data for successful results
|
||||
if (result.status === 'success') {
|
||||
formDataByPage[result.wikiPage] = createEmptyFormData(result.wikiPage, result.suggestions)
|
||||
dismissedByPage[result.wikiPage] = new Set<string>()
|
||||
formDataByPage[result.wikiPage] = createEmptyFormData(result.wikiPage, result.parsedData)
|
||||
}
|
||||
})
|
||||
entities = updatedEntities
|
||||
|
||||
// Trigger reactivity by reassigning
|
||||
formDataByPage = { ...formDataByPage }
|
||||
dismissedByPage = { ...dismissedByPage }
|
||||
} catch (error) {
|
||||
console.error('Batch preview error:', error)
|
||||
fetchError = 'Failed to fetch wiki data. Please try again.'
|
||||
|
|
@ -251,24 +247,6 @@
|
|||
selectedWikiPage = wikiPage
|
||||
}
|
||||
|
||||
// Accept a suggestion
|
||||
function handleAcceptSuggestion(field: string, value: any) {
|
||||
if (!selectedWikiPage || !formDataByPage[selectedWikiPage]) return
|
||||
|
||||
formDataByPage[selectedWikiPage][field] = value
|
||||
formDataByPage = { ...formDataByPage }
|
||||
}
|
||||
|
||||
// Dismiss a suggestion
|
||||
function handleDismissSuggestion(field: string) {
|
||||
if (!selectedWikiPage) return
|
||||
|
||||
const dismissed = dismissedByPage[selectedWikiPage] ?? new Set<string>()
|
||||
dismissed.add(field)
|
||||
dismissedByPage[selectedWikiPage] = dismissed
|
||||
dismissedByPage = { ...dismissedByPage }
|
||||
}
|
||||
|
||||
// Save current entity
|
||||
async function saveCurrentEntity() {
|
||||
if (!selectedWikiPage) return
|
||||
|
|
@ -461,57 +439,35 @@
|
|||
<p>Loading wiki data...</p>
|
||||
</div>
|
||||
{:else if selectedWikiPage && formDataByPage[selectedWikiPage]}
|
||||
{@const suggestions = selectedEntity.suggestions}
|
||||
{@const dismissed = dismissedByPage[selectedWikiPage] ?? new Set<string>()}
|
||||
<section class="details">
|
||||
<WeaponMetadataSection
|
||||
weapon={emptyWeapon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<WeaponUncapSection
|
||||
weapon={emptyWeapon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<WeaponTaxonomySection
|
||||
weapon={emptyWeapon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<WeaponStatsSection
|
||||
weapon={emptyWeapon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<WeaponGachaSection
|
||||
weapon={emptyWeapon}
|
||||
editMode={true}
|
||||
bind:editData={formDataByPage[selectedWikiPage]}
|
||||
{suggestions}
|
||||
dismissedSuggestions={dismissed}
|
||||
onAcceptSuggestion={handleAcceptSuggestion}
|
||||
onDismissSuggestion={handleDismissSuggestion}
|
||||
/>
|
||||
|
||||
<WeaponForgeSection
|
||||
|
|
@ -530,42 +486,29 @@
|
|||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="Dates">
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Release Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].releaseDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.releaseDate}
|
||||
dismissedSuggestion={dismissed.has('releaseDate')}
|
||||
onAcceptSuggestion={() =>
|
||||
handleAcceptSuggestion('releaseDate', suggestions?.releaseDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('releaseDate')}
|
||||
/>
|
||||
{#if formDataByPage[selectedWikiPage].flb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="FLB Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].flbDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.flbDate}
|
||||
dismissedSuggestion={dismissed.has('flbDate')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('flbDate', suggestions?.flbDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('flbDate')}
|
||||
/>
|
||||
{/if}
|
||||
{#if formDataByPage[selectedWikiPage].ulb}
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="ULB Date"
|
||||
bind:value={formDataByPage[selectedWikiPage].ulbDate}
|
||||
editable={true}
|
||||
type="text"
|
||||
placeholder="YYYY-MM-DD"
|
||||
suggestion={suggestions?.ulbDate}
|
||||
dismissedSuggestion={dismissed.has('ulbDate')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('ulbDate', suggestions?.ulbDate)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('ulbDate')}
|
||||
/>
|
||||
{/if}
|
||||
{#if formDataByPage[selectedWikiPage].transcendence}
|
||||
|
|
@ -600,7 +543,7 @@
|
|||
hasLinkButton={true}
|
||||
linkUrl={buildWikiJaUrl(formDataByPage[selectedWikiPage].wikiJa, 'weapon')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Gamewith"
|
||||
bind:value={formDataByPage[selectedWikiPage].gamewith}
|
||||
editable={true}
|
||||
|
|
@ -609,12 +552,8 @@
|
|||
width="480px"
|
||||
hasLinkButton={true}
|
||||
linkUrl={buildGamewithUrl(formDataByPage[selectedWikiPage].gamewith)}
|
||||
suggestion={suggestions?.gamewith}
|
||||
dismissedSuggestion={dismissed.has('gamewith')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('gamewith', suggestions?.gamewith)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('gamewith')}
|
||||
/>
|
||||
<SuggestionDetailItem
|
||||
<DetailItem
|
||||
label="Kamigame"
|
||||
bind:value={formDataByPage[selectedWikiPage].kamigame}
|
||||
editable={true}
|
||||
|
|
@ -623,10 +562,6 @@
|
|||
width="480px"
|
||||
hasLinkButton={true}
|
||||
linkUrl={buildKamigameUrl(formDataByPage[selectedWikiPage].kamigame, 'weapon', formDataByPage[selectedWikiPage].rarity)}
|
||||
suggestion={suggestions?.kamigame}
|
||||
dismissedSuggestion={dismissed.has('kamigame')}
|
||||
onAcceptSuggestion={() => handleAcceptSuggestion('kamigame', suggestions?.kamigame)}
|
||||
onDismissSuggestion={() => handleDismissSuggestion('kamigame')}
|
||||
/>
|
||||
</DetailsContainer>
|
||||
</section>
|
||||
|
|
|
|||
Loading…
Reference in a new issue