add variant fileter

This commit is contained in:
2026-03-05 00:00:38 -05:00
parent f44f1acb92
commit 6ec5b95982
8 changed files with 186 additions and 17 deletions

View File

@@ -85,6 +85,8 @@
<tr class="border-b border-gray-700 bg-gray-700/30">
<th class="text-left px-4 py-2 text-gray-400 font-semibold uppercase tracking-wider">Material</th>
<th class="text-right px-4 py-2 text-gray-400 font-semibold uppercase tracking-wider">Qty</th>
<th class="text-right px-4 py-2 text-gray-400 font-semibold uppercase tracking-wider">Unit Price</th>
<th class="text-right px-4 py-2 text-gray-400 font-semibold uppercase tracking-wider">Total</th>
</tr>
</thead>
<tbody>
@@ -100,6 +102,36 @@
</div>
</td>
<td class="px-4 py-1.5 text-right font-mono text-gray-200 font-medium">{{ mat.qty.toLocaleString() }}</td>
<td class="px-4 py-1.5 text-right font-mono">
<div v-if="editingItemId === mat.itemId" class="flex items-center justify-end gap-1">
<input
v-focus
type="number"
min="1"
:placeholder="mat.unitPrice > 0 ? 'empty to clear' : 'silver'"
class="w-28 bg-gray-900 border border-amber-500 rounded px-1.5 py-0.5 text-right text-xs font-mono text-gray-200 focus:outline-none placeholder-gray-600"
v-model="inputValue"
@keydown.enter="saveEdit(mat.itemId)"
@keydown.escape="cancelEdit"
/>
<button class="text-amber-500 hover:text-amber-300 text-xs leading-none px-0.5" @click="saveEdit(mat.itemId)"></button>
<button class="text-gray-500 hover:text-gray-300 text-xs leading-none px-0.5" @click="cancelEdit"></button>
</div>
<button
v-else
class="group flex items-center gap-0.5 ml-auto"
:class="priceButtonClass(mat.itemId, mat.unitPrice)"
:title="priceTitle(mat.itemId, mat.unitPrice)"
@click="startEdit(mat.itemId, mat.unitPrice)"
>
{{ mat.unitPrice > 0 ? formatSilver(mat.unitPrice) : '—' }}
<span class="opacity-0 group-hover:opacity-40 text-[10px] ml-0.5"></span>
</button>
</td>
<td class="px-4 py-1.5 text-right font-mono text-gray-200"
:title="mat.totalCost > 0 ? mat.totalCost.toLocaleString() : undefined">
{{ mat.totalCost > 0 ? formatSilver(mat.totalCost) : '—' }}
</td>
</tr>
</tbody>
</table>
@@ -169,17 +201,62 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { useProductionOrder } from '../composables/useProductionOrder'
import { useAlbionPrices } from '../composables/useAlbionPrices'
import { useFilters } from '../composables/useFilters'
import { formatSilver, formatItemId, tierStyle, itemImageUrl } from '../utils/formatting'
import { formatSilver, formatItemId, formatLastUpdated, tierStyle, itemImageUrl } from '../utils/formatting'
import type { Tier, JournalType } from '../types/crafting'
const vFocus = { mounted: (el: HTMLElement) => el.focus() }
const { orderItems, upsert, remove, clear } = useProductionOrder()
const { getPrice } = useAlbionPrices()
const { getPrice, isManualPrice, getManualEntry, setManualPrice, clearManualPrice } = useAlbionPrices()
const { filters } = useFilters()
// ─── Inline price editing ─────────────────────────────────────────────────────
const editingItemId = ref<string | null>(null)
const inputValue = ref('')
function startEdit(itemId: string, current: number) {
editingItemId.value = itemId
inputValue.value = current > 0 ? String(current) : ''
}
function saveEdit(itemId: string) {
const v = Math.round(Number(inputValue.value))
if (!inputValue.value) {
clearManualPrice(itemId, filters.value.city)
} else if (v > 0) {
setManualPrice(itemId, filters.value.city, v)
}
editingItemId.value = null
inputValue.value = ''
}
function cancelEdit() {
editingItemId.value = null
inputValue.value = ''
}
function priceButtonClass(itemId: string, currentPrice: number): string {
if (isManualPrice(itemId, filters.value.city)) return 'text-amber-400 hover:text-amber-200'
if (currentPrice === 0) return 'text-gray-500 hover:text-amber-400'
return 'text-gray-300 hover:text-gray-100'
}
function priceTitle(itemId: string, currentPrice: number): string {
const exact = currentPrice > 0 ? currentPrice.toLocaleString() : null
const entry = getManualEntry(itemId, filters.value.city)
if (entry && isManualPrice(itemId, filters.value.city)) {
return exact
? `${exact} — set ${formatLastUpdated(new Date(entry.editedAt))} — click to edit`
: `Set ${formatLastUpdated(new Date(entry.editedAt))} — click to edit`
}
return exact ? `${exact} — click to set price` : 'Click to set price'
}
// Laborer journal fills ≈ 13 crafts per book (ratio is constant across tiers)
const CRAFTS_PER_JOURNAL = 13