improving the table layout

This commit is contained in:
2026-03-05 22:56:24 -05:00
parent f910d07b48
commit 010a92999a
8 changed files with 90 additions and 11 deletions

View File

@@ -20,7 +20,8 @@
<FilterBar :filters="filters" :result-count="profitResults.length" @set-name-filter="setNameFilter"
@toggle-tier="toggleTier" @set-selected-item-types="setSelectedItemTypes"
@toggle-enchantment="toggleEnchantment" @reset-enchantments="resetEnchantments"
@toggle-variant="toggleVariant" @reset-variants="resetVariants" />
@toggle-variant="toggleVariant" @reset-variants="resetVariants"
@toggle-station="toggleStation" @reset-stations="resetStations" />
<ProfitTable :results="profitResults" :sort-state="sortState" @sort="handleSort" />
</div>
@@ -73,6 +74,8 @@ const {
resetEnchantments,
toggleVariant,
resetVariants,
toggleStation,
resetStations,
} = useFilters()
// Profit calculation

View File

@@ -131,6 +131,20 @@
</div>
</div>
<!-- Station toggles -->
<div class="flex items-center gap-1">
<span class="text-xs text-gray-400">Station</span>
<div class="flex gap-0.5">
<button class="px-2 py-1 rounded text-xs font-semibold transition-colors" :class="filters.stations === null
? 'bg-amber-600 text-amber-100'
: 'bg-gray-800 text-gray-500 hover:bg-gray-700'" @click="$emit('reset-stations')">All</button>
<button v-for="s in ALL_STATIONS" :key="s"
class="px-2 py-1 rounded text-xs font-semibold transition-colors"
:class="isStationActive(s) ? STATION_ACTIVE_CLASS[s] : 'bg-gray-800 text-gray-600 hover:bg-gray-700'"
@click="$emit('toggle-station', s)">{{ STATION_LABEL[s] }}</button>
</div>
</div>
<!-- Result count -->
<span class="ml-auto text-xs text-gray-500 whitespace-nowrap">{{ resultCount }} results</span>
@@ -142,8 +156,8 @@
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { tierStyle, enchantStyle } from '../../utils/formatting'
import type { FilterState, VariantType } from '../../types/filters'
import { ALL_VARIANTS } from '../../types/filters'
import type { Tier, Enchantment } from '../../types/crafting'
import { ALL_VARIANTS, ALL_STATIONS } from '../../types/filters'
import type { Tier, Enchantment, JournalType } from '../../types/crafting'
import { TIERS, ENCHANTMENTS } from '../../data/constants'
import { ITEM_TREE, ALL_ITEM_NAMES, getLeaves } from '../../data/itemTree'
import type { TreeNode } from '../../data/itemTree'
@@ -161,6 +175,8 @@ const emit = defineEmits<{
'reset-enchantments': []
'toggle-variant': [v: VariantType]
'reset-variants': []
'toggle-station': [s: JournalType]
'reset-stations': []
}>()
// ─── Category tree ────────────────────────────────────────────────────────────
@@ -287,6 +303,23 @@ function isVariantActive(v: VariantType): boolean {
return s === null || s.has(v)
}
// ─── Station helpers ──────────────────────────────────────────────────────────
const STATION_LABEL: Record<JournalType, string> = {
warrior: 'Warrior', hunter: 'Hunter', mage: 'Mage', toolmaker: 'Toolmaker',
}
const STATION_ACTIVE_CLASS: Record<JournalType, string> = {
warrior: 'bg-red-900/60 text-red-300',
hunter: 'bg-green-900/60 text-green-300',
mage: 'bg-blue-900/60 text-blue-300',
toolmaker: 'bg-orange-900/60 text-orange-300',
}
function isStationActive(s: JournalType): boolean {
return props.filters.stations === null || props.filters.stations.has(s)
}
// ─── Dropdown open states ─────────────────────────────────────────────────────

View File

@@ -26,10 +26,17 @@
class="w-12 h-12 -my-2 rounded flex-shrink-0"
/>
<span class="text-sm font-medium text-gray-200">{{ result.recipe.displayName.replace(/^T\d+\.\d+\s/, '') }}</span>
<span class="text-xs text-gray-500">{{ result.recipe.category }}</span>
</div>
</td>
<!-- Station badge -->
<td class="px-4 py-3">
<span class="inline-flex items-center justify-center px-2 h-6 rounded text-xs font-semibold whitespace-nowrap"
:class="STATION[result.recipe.journalType].cls">
{{ STATION[result.recipe.journalType].label }}
</span>
</td>
<!-- Tier badge -->
<td class="px-4 py-3">
<span
@@ -156,7 +163,7 @@
<!-- Expanded detail row -->
<tr v-if="expanded" class="border-b border-gray-700/50 bg-gray-900/60">
<td colspan="13" class="px-6 py-4">
<td colspan="14" class="px-6 py-4">
<div class="flex gap-8">
<!-- Ingredients breakdown -->
@@ -251,7 +258,7 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { ProfitResult, Tier } from '../../types/crafting'
import type { ProfitResult, Tier, JournalType } from '../../types/crafting'
import { formatSilver, formatLastUpdated, tierEnchantStyle, itemImageUrl } from '../../utils/formatting'
import { useAlbionPrices } from '../../composables/useAlbionPrices'
import { useFilters } from '../../composables/useFilters'
@@ -267,6 +274,13 @@ const { getManualEntry, setManualPrice, clearManualPrice } = useAlbionPrices()
const { filters } = useFilters()
const { upsert, remove, getQty, inOrder } = useProductionOrder()
const STATION: Record<JournalType, { label: string; cls: string }> = {
warrior: { label: 'Warrior', cls: 'bg-red-900/30 border border-red-700/40 text-red-400' },
hunter: { label: 'Hunter', cls: 'bg-green-900/30 border border-green-700/40 text-green-400' },
mage: { label: 'Mage', cls: 'bg-blue-900/30 border border-blue-700/40 text-blue-400' },
toolmaker: { label: 'Toolmaker', cls: 'bg-orange-900/30 border border-orange-700/40 text-orange-400' },
}
const VARIANT_INFO: Record<string, { label: string; cls: string }> = {
avalon: { label: 'Avalon', cls: 'bg-violet-600/30 text-violet-300 border border-violet-600/40' },
crystal: { label: 'Crystal', cls: 'bg-cyan-600/30 text-cyan-300 border border-cyan-600/40' },

View File

@@ -2,7 +2,7 @@
<thead class="sticky top-0 z-10">
<!-- Group row -->
<tr class="bg-gray-800 border-b border-gray-700/50">
<th colspan="4" class="bg-gray-800" />
<th colspan="5" class="bg-gray-800" />
<th colspan="4" class="px-4 py-1.5 text-center text-[10px] font-semibold text-gray-500 uppercase tracking-wider border-l border-gray-700/60">
No Focus
</th>
@@ -68,6 +68,7 @@ import type { SortField, SortState } from '../../types/crafting'
const leftCols = [
{ field: 'variantType' as SortField, label: 'Variant', sortable: true },
{ field: 'displayName' as SortField, label: 'Item', sortable: true },
{ field: 'station' as SortField, label: 'Station', sortable: true },
{ field: 'tier' as SortField, label: 'Tier', sortable: true },
]

View File

@@ -39,6 +39,7 @@ export function useCraftingProfit(
if (!f.tiers.has(recipe.tier)) continue
if (f.enchantments !== null && !f.enchantments.has(recipe.enchantment)) continue
if (f.variants !== null && !f.variants.has(variantOf(recipe.outputItemId))) continue
if (f.stations !== null && !f.stations.has(recipe.journalType)) continue
const baseName = recipe.displayName.replace(/^T\d+\.\d /, '')
if (f.selectedItemTypes !== null && !f.selectedItemTypes.has(baseName)) continue
if (nameLower && !recipe.displayName.toLowerCase().includes(nameLower)) continue
@@ -127,6 +128,10 @@ export function useCraftingProfit(
aVal = variantRank(a.recipe.outputItemId)
bVal = variantRank(b.recipe.outputItemId)
break
case 'station':
aVal = a.recipe.journalType
bVal = b.recipe.journalType
break
default:
aVal = a.effectiveMaterialCost
bVal = b.effectiveMaterialCost

View File

@@ -1,7 +1,7 @@
import { ref, watch } from 'vue'
import type { FilterState, VariantType } from '../types/filters'
import { ALL_VARIANTS } from '../types/filters'
import type { Tier, Enchantment } from '../types/crafting'
import { ALL_VARIANTS, ALL_STATIONS } from '../types/filters'
import type { Tier, Enchantment, JournalType } from '../types/crafting'
import type { AlbionCity } from '../types/api'
import { ENCHANTMENTS, TIERS } from '../data/constants'
@@ -14,6 +14,7 @@ interface StoredFilters {
tiers?: Tier[]
enchantments?: Enchantment[] | null
variants?: VariantType[] | null
stations?: JournalType[] | null
selectedItemTypes?: string[] | null
}
@@ -33,6 +34,7 @@ function buildInitialState(): FilterState {
tiers: s.tiers ? new Set(s.tiers) : new Set(TIERS),
enchantments: s.enchantments === undefined ? null : (s.enchantments === null ? null : new Set(s.enchantments)),
variants: s.variants === undefined ? null : (s.variants === null ? null : new Set(s.variants)),
stations: s.stations === undefined ? null : (s.stations === null ? null : new Set(s.stations)),
selectedItemTypes: s.selectedItemTypes === undefined ? null : (s.selectedItemTypes === null ? null : new Set(s.selectedItemTypes)),
nameFilter: '',
}
@@ -49,6 +51,7 @@ watch(filters, () => {
tiers: [...f.tiers],
enchantments: f.enchantments === null ? null : [...f.enchantments],
variants: f.variants === null ? null : [...f.variants],
stations: f.stations === null ? null : [...f.stations],
selectedItemTypes: f.selectedItemTypes === null ? null : [...f.selectedItemTypes],
}
try {
@@ -110,6 +113,21 @@ function resetVariants() {
filters.value.variants = null
}
function toggleStation(station: JournalType) {
const current = filters.value.stations
const next = current === null ? new Set(ALL_STATIONS) : new Set(current)
if (next.has(station)) {
next.delete(station)
} else {
next.add(station)
}
filters.value.stations = next.size === ALL_STATIONS.length ? null : next
}
function resetStations() {
filters.value.stations = null
}
export function useFilters() {
return {
filters,
@@ -121,5 +139,7 @@ export function useFilters() {
resetEnchantments,
toggleVariant,
resetVariants,
toggleStation,
resetStations,
}
}

View File

@@ -7,7 +7,7 @@ export type JournalType = 'warrior' | 'mage' | 'hunter' | 'toolmaker'
export type FameType = 'twoHanded' | 'oneHanded' | 'armorChest' | 'small'
export type SortField = 'materialCost' | 'displayName' | 'tier' | 'variantType'
export type SortField = 'materialCost' | 'displayName' | 'tier' | 'variantType' | 'station'
export type SortDirection = 'asc' | 'desc'

View File

@@ -1,10 +1,12 @@
import type { AlbionCity } from './api'
import type { Tier, Enchantment } from './crafting'
import type { Tier, Enchantment, JournalType } from './crafting'
export type VariantType = 'basic' | 'artifact' | 'avalon' | 'crystal'
export const ALL_VARIANTS: VariantType[] = ['basic', 'artifact', 'avalon', 'crystal']
export const ALL_STATIONS: JournalType[] = ['warrior', 'hunter', 'mage', 'toolmaker']
export interface FilterState {
city: AlbionCity
tiers: Set<Tier>
@@ -12,4 +14,5 @@ export interface FilterState {
nameFilter: string
enchantments: Set<Enchantment> | null
variants: Set<VariantType> | null
stations: Set<JournalType> | null
}