add orphaned item indicators
shows warning badge + dimmed styling when collection items get deleted party banner warns if any items are orphaned
This commit is contained in:
parent
d0592935be
commit
daa104cd99
5 changed files with 148 additions and 2 deletions
|
|
@ -316,6 +316,14 @@
|
|||
// Check if syncing is in progress
|
||||
const isSyncingAll = $derived(syncAllItems.isPending)
|
||||
|
||||
// Check if any items in the party are orphaned (linked collection item was deleted)
|
||||
const hasOrphanedItems = $derived.by(() => {
|
||||
const hasOrphanedWeapons = (party?.weapons ?? []).some((w) => w?.orphaned)
|
||||
const hasOrphanedCharacters = (party?.characters ?? []).some((c) => c?.orphaned)
|
||||
const hasOrphanedSummons = (party?.summons ?? []).some((s) => s?.orphaned)
|
||||
return hasOrphanedWeapons || hasOrphanedCharacters || hasOrphanedSummons
|
||||
})
|
||||
|
||||
function handleTabChange(tab: GridType) {
|
||||
activeTab = tab // Instant UI update
|
||||
|
||||
|
|
@ -941,6 +949,13 @@
|
|||
{/snippet}
|
||||
</PartyInfoGrid>
|
||||
|
||||
{#if hasOrphanedItems}
|
||||
<div class="orphan-warning" role="alert">
|
||||
<Icon name="alertTriangle" size={16} />
|
||||
<span>Some items in this party are no longer in your collection and may have outdated data.</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<PartySegmentedControl
|
||||
selectedTab={activeTab}
|
||||
onTabChange={handleTabChange}
|
||||
|
|
@ -1149,4 +1164,21 @@
|
|||
background: color.adjust($error, $lightness: -10%);
|
||||
}
|
||||
}
|
||||
|
||||
.orphan-warning {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $unit;
|
||||
padding: $unit $unit-2x;
|
||||
background: rgba(209, 137, 58, 0.15);
|
||||
border: 1px solid rgba(209, 137, 58, 0.4);
|
||||
border-radius: $unit-half;
|
||||
color: #c47a1a;
|
||||
font-size: $font-small;
|
||||
|
||||
:global(svg) {
|
||||
flex-shrink: 0;
|
||||
color: #c47a1a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="unit {elementClass}" class:empty={!item} class:is-active={isActive}>
|
||||
<div class="unit {elementClass}" class:empty={!item} class:is-active={isActive} class:orphaned={item?.orphaned}>
|
||||
{#if item}
|
||||
<UnitMenuContainer showGearButton={true}>
|
||||
{#snippet trigger()}
|
||||
|
|
@ -218,6 +218,11 @@
|
|||
title="Perpetuity Ring"
|
||||
/>
|
||||
{/if}
|
||||
{#if item?.orphaned}
|
||||
<div class="orphaned-badge" title="This item is no longer in your collection">
|
||||
<Icon name="alertTriangle" size={16} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if imageUrl}
|
||||
<img
|
||||
class="image {elementClass}"
|
||||
|
|
@ -617,4 +622,34 @@
|
|||
color: colors.$grey-40;
|
||||
}
|
||||
}
|
||||
|
||||
.orphaned-badge {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #d13a3a;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
z-index: 10;
|
||||
pointer-events: auto;
|
||||
cursor: help;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
// Orphaned state
|
||||
.unit.orphaned {
|
||||
.frame {
|
||||
opacity: 0.7;
|
||||
border: 2px solid #d13a3a;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #d13a3a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@
|
|||
|
||||
</script>
|
||||
|
||||
<div class="unit {elementClass}" class:empty={!item} class:is-active={isActive}>
|
||||
<div class="unit {elementClass}" class:empty={!item} class:is-active={isActive} class:orphaned={item?.orphaned}>
|
||||
{#if item}
|
||||
<UnitMenuContainer showGearButton={true}>
|
||||
{#snippet trigger()}
|
||||
|
|
@ -129,6 +129,11 @@
|
|||
class:is-active={isActive}
|
||||
onclick={() => viewDetails()}
|
||||
>
|
||||
{#if item?.orphaned}
|
||||
<div class="orphaned-badge" title="This item is no longer in your collection">
|
||||
<Icon name="alertTriangle" size={16} />
|
||||
</div>
|
||||
{/if}
|
||||
<img
|
||||
class="image {elementClass}"
|
||||
class:placeholder={!item?.summon?.granblueId}
|
||||
|
|
@ -346,6 +351,36 @@
|
|||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.orphaned-badge {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #d13a3a;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
z-index: 10;
|
||||
pointer-events: auto;
|
||||
cursor: help;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
// Orphaned state
|
||||
.unit.orphaned {
|
||||
.frame {
|
||||
opacity: 0.7;
|
||||
border: 2px solid #d13a3a;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #d13a3a;
|
||||
}
|
||||
}
|
||||
|
||||
// Pulsing focus ring animation
|
||||
@keyframes pulse-focus-ring {
|
||||
0%, 100% {
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@
|
|||
class:empty={!item}
|
||||
class:extra={position >= 9}
|
||||
class:is-active={isActive}
|
||||
class:orphaned={item?.orphaned}
|
||||
>
|
||||
{#if item}
|
||||
<UnitMenuContainer showGearButton={true}>
|
||||
|
|
@ -173,6 +174,11 @@
|
|||
onclick={() => viewDetails()}
|
||||
>
|
||||
<div class="modifiers">
|
||||
{#if item?.orphaned}
|
||||
<div class="orphaned-badge" title="This item is no longer in your collection">
|
||||
<Icon name="alertTriangle" size={16} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if awakeningImage}
|
||||
<img
|
||||
class="awakening"
|
||||
|
|
@ -448,6 +454,24 @@
|
|||
z-index: 3;
|
||||
pointer-events: none;
|
||||
|
||||
.orphaned-badge {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #d13a3a;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
z-index: 10;
|
||||
pointer-events: auto;
|
||||
cursor: help;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.awakening {
|
||||
position: absolute;
|
||||
width: 30%;
|
||||
|
|
@ -467,6 +491,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Orphaned state
|
||||
.unit.orphaned {
|
||||
.frame {
|
||||
opacity: 0.7;
|
||||
border: 2px solid #d13a3a;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #d13a3a;
|
||||
}
|
||||
}
|
||||
|
||||
// Position modifiers for grid weapons
|
||||
.frame.weapon.cell {
|
||||
.awakening {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ export interface GridWeapon {
|
|||
collectionWeaponId?: string
|
||||
/** Whether the grid item is out of sync with its collection source */
|
||||
outOfSync?: boolean
|
||||
/** Whether the linked collection item has been deleted (item is orphaned) */
|
||||
orphaned?: boolean
|
||||
}
|
||||
|
||||
// GridCharacter from GridCharacterBlueprint
|
||||
|
|
@ -60,6 +62,8 @@ export interface GridCharacter {
|
|||
collectionCharacterId?: string
|
||||
/** Whether the grid item is out of sync with its collection source */
|
||||
outOfSync?: boolean
|
||||
/** Whether the linked collection item has been deleted (item is orphaned) */
|
||||
orphaned?: boolean
|
||||
}
|
||||
|
||||
// GridSummon from GridSummonBlueprint
|
||||
|
|
@ -76,6 +80,8 @@ export interface GridSummon {
|
|||
collectionSummonId?: string
|
||||
/** Whether the grid item is out of sync with its collection source */
|
||||
outOfSync?: boolean
|
||||
/** Whether the linked collection item has been deleted (item is orphaned) */
|
||||
orphaned?: boolean
|
||||
}
|
||||
|
||||
// JobSkillList for party job skills
|
||||
|
|
@ -115,6 +121,8 @@ export interface Party {
|
|||
extra?: boolean
|
||||
remix?: boolean
|
||||
editKey?: string
|
||||
/** Whether the party contains any orphaned grid items */
|
||||
hasOrphanedItems?: boolean
|
||||
|
||||
// Relationships
|
||||
weapons: GridWeapon[]
|
||||
|
|
|
|||
Loading…
Reference in a new issue