Updated inventory modal styling and added inventory button to card grid

This commit is contained in:
zach
2026-07-16 08:53:33 -04:00
parent b91b64f32c
commit f0ee662450
4 changed files with 130 additions and 63 deletions

View File

@@ -47,13 +47,6 @@ const catalogs = catalogRows
<aside class="col-12 col-md-2 border-end border-secondary bg-dark p-3 d-flex flex-column gap-3">
<div class="d-flex justify-content-between align-items-center">
<h6 class="mb-0 text-uppercase text-secondary fw-bold ls-wide" style="letter-spacing:.08em">Catalogs</h6>
<button
class="btn btn-purple-secondary fs-7"
title="New catalog"
type="button"
data-bs-toggle="modal"
data-bs-target="#newCatalogModal"
>+ New</button>
</div>
<ul id="catalogList" class="list-group list-group-flush">
@@ -96,12 +89,10 @@ const catalogs = catalogRows
</div>
</aside>
<main class="col-12 col-md-10 p-4">
<div class="d-flex flex-wrap gap-2 align-items-center mb-4">
<main class="col-12 col-md-10 mt-4">
<div class="d-flex flex-wrap gap-2 mb-4">
<div class="vr opacity-25 mx-1"></div>
<a href="/pokemon" class="btn btn-vendor">+ Add Card</a>
<a href="/pokemon" class="btn btn-vendor">Add cards</a>
<button
type="button"
@@ -164,8 +155,8 @@ const catalogs = catalogRows
</div>
</div>
<div class="modal-footer border-secondary">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success" id="csvUploadBtn">Upload &amp; Preview</button>
<button type="button" class="btn btn-outline-danger" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success" id="csvUploadBtn">Upload</button>
</div>
</div>
</div>

View File

@@ -473,7 +473,7 @@ const altSearchUrl = (card: any) => {
</datalist>
<div class="form-text">
Type a name or pick an existing catalog.
Enter a name or pick an existing catalog.
</div>
</div>
<div class="col-12">

View File

@@ -7,8 +7,8 @@ export const prerender = false;
import * as util from 'util';
// auth check for inventory management features
//const { canAddInventory } = Astro.locals;
const canAddInventory = false;
const { canAddInventory } = Astro.locals;
// const canAddInventory = false;
// all the facet fields we want to use for filtering
const facetFields:any = {

View File

@@ -48,7 +48,17 @@ const purchaseDate = inv?.createdAt ? new Date(inv.createdAt).toISOString().spli
const marketPrice = sku?.marketPrice != null ? Number(sku.marketPrice) : null;
const condition = sku?.condition || 'Near Mint';
const gain = (purchasePrice != null && marketPrice != null) ? marketPrice - purchasePrice : null;
const gain =
purchasePrice != null && marketPrice != null
? marketPrice - purchasePrice
: null;
const gainPercent =
purchasePrice != null &&
marketPrice != null &&
purchasePrice > 0
? ((marketPrice - purchasePrice) / purchasePrice) * 100
: null;
// ── Price history for this SKU's condition only ──────────────────────────
const priceHistoryForChart = (sku?.history ?? [])
@@ -132,40 +142,105 @@ const altSearchUrl = (card: any) => {
<!-- Inventory details + edit column -->
<div class="col-sm-12 col-md-7">
<!-- Static fields from the inventory entry -->
<div class="d-flex flex-fill flex-row gap-1 flex-wrap flex-lg-nowrap">
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
<h6 class="mb-auto">Quantity</h6>
<p class="mb-0 mt-1">{quantity}</p>
</div>
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
<h6 class="mb-auto">Purchase Price</h6>
<p class="mb-0 mt-1">{purchasePrice != null ? `$${purchasePrice.toFixed(2)}` : '—'}</p>
</div>
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
<h6 class="mb-auto">Condition</h6>
<p class="mb-0 mt-1">{condition}</p>
</div>
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
<h6 class="mb-auto">Market Price</h6>
<p class="mb-0 mt-1">{marketPrice != null ? `$${marketPrice.toFixed(2)}` : '—'}</p>
</div>
<div class={`alert rounded p-2 flex-fill d-flex flex-column mb-0 ${gain == null ? 'alert-dark' : gain >= 0 ? 'alert-success' : 'alert-danger'}`}>
<h6 class="mb-auto">Gain / Loss</h6>
<p class="mb-0 mt-1">{gain == null ? '—' : `${gain >= 0 ? '+' : ''}$${Math.abs(gain).toFixed(2)}`}</p>
</div>
</div>
<!-- Static fields (left) + editable Note/Catalog (right), side by side -->
<div class="row gx-3 gy-2">
<!-- Left column: read-only stats -->
<div class="col-12 col-lg-6">
<label class="form-label">Condition</label>
<div class="btn-group condition-input w-100 col-12" role="group" aria-label="Condition">
<input
type="radio"
class="btn-check"
name="condition"
id="cond-nm"
value="Near Mint"
autocomplete="off"
checked
/>
<label class="btn btn-cond-nm" for="cond-nm">NM</label>
<!-- Editable fields: Catalog + Note -->
<form data-inventory-edit-form class="mt-3" novalidate>
<input type="hidden" name="action" value="update" />
<input type="hidden" name="inventoryId" value={inv?.inventoryId} />
<input type="hidden" name="cardId" value={card?.cardId} />
<input type="hidden" name="quantity" value={quantity} />
<input type="hidden" name="purchasePrice" value={purchasePrice ?? 0} />
<input
type="radio"
class="btn-check"
name="condition"
id="cond-lp"
value="Lightly Played"
autocomplete="off"
disabled
/>
<label class="btn btn-cond-lp" for="cond-lp">LP</label>
<input
type="radio"
class="btn-check"
name="condition"
id="cond-mp"
value="Moderately Played"
autocomplete="off"
disabled
/>
<label class="btn btn-cond-mp" for="cond-mp">MP</label>
<input
type="radio"
class="btn-check"
name="condition"
id="cond-hp"
value="Heavily Played"
autocomplete="off"
disabled
/>
<label class="btn btn-cond-hp" for="cond-hp">HP</label>
<input
type="radio"
class="btn-check"
name="condition"
id="cond-dmg"
value="Damaged"
autocomplete="off"
disabled
/>
<label class="btn btn-cond-dmg" for="cond-dmg">DMG</label>
</div>
<p class="mb-1 my-2">Purchased: <span class="text-secondary">{inv.createdAt ? new Date(inv.createdAt).toLocaleDateString() : '—'}</span></p>
<div class="d-flex flex-wrap justify-content-center flex-row gap-2">
<div class="alert alert-dark d-flex flex-column flex-fill rounded p-2 mb-2">
<p class="mb-auto">Purchase Price</p>
<h6 class="mb-0 mt-1">{purchasePrice != null ? `$${purchasePrice.toFixed(2)}` : '—'}</h6>
</div>
<div class="alert alert-dark d-flex flex-column flex-fill rounded p-2 mb-2">
<p class="mb-auto">Market Price</p>
<h6 class="mb-0 mt-1">{marketPrice != null ? `$${marketPrice.toFixed(2)}` : '—'}</h6>
</div>
</div>
<div class="d-flex flex-wrap justify-content-between flex-row gap-2">
<div class={`alert rounded p-2 flex-fill ${gain == null ? 'alert-dark' : gain >= 0 ? 'alert-success' : 'alert-danger'}`}>
<p class="mb-auto">Gain / Loss ($)</p>
<h6 class="mb-0 mt-1">{gain == null ? '—' : `${gain >= 0 ? '+' : ''}$${Math.abs(gain).toFixed(2)}`}</h6>
</div>
<div class={`alert rounded p-2 flex-fill ${gainPercent == null ? 'alert-dark' : gainPercent >= 0 ? 'alert-success' : 'alert-danger'}`}>
<p class="mb-auto">Gain / Loss (%)</p>
<h6 class="mb-0 mt-1">
{
gainPercent == null
? '—'
: `${gainPercent >= 0 ? '+' : ''}${Math.abs(gainPercent).toFixed(2)}%`
}
</h6>
</div>
</div>
</div>
<!-- Right column: editable Note + Catalog -->
<div class="col-12 col-lg-6">
<form data-inventory-edit-form novalidate>
<input type="hidden" name="action" value="update" />
<input type="hidden" name="inventoryId" value={inv?.inventoryId} />
<input type="hidden" name="cardId" value={card?.cardId} />
<input type="hidden" name="quantity" value={quantity} />
<input type="hidden" name="purchasePrice" value={purchasePrice ?? 0} />
<div class="row gx-3 gy-2">
<div class="col-12 col-lg-5">
<label for="catalogName" class="form-label">Catalog</label>
<input
type="text"
@@ -181,27 +256,24 @@ const altSearchUrl = (card: any) => {
<datalist id="catalogSuggestions">
{catalogs.map(name => <option value={name}></option>)}
</datalist>
</div>
<div class="col-12 col-lg-7">
<label for="note" class="form-label">Note</label>
<label for="note" class="form-label mt-2">Note</label>
<textarea
class="form-control"
id="note"
name="note"
rows="2"
rows="5"
maxlength="255"
placeholder="e.g. bought at local shop, gift, graded copy…"
>{inv?.note ?? ''}</textarea>
</div>
<div class="col-12 d-flex justify-content-end gap-2 pt-1">
<button type="button" class="btn btn-outline-danger" data-inventory-delete>Delete</button>
<button type="submit" class="btn btn-success">Save</button>
</div>
</form>
</div>
</form>
</div>
<!-- Market Price History chart for this condition -->
<div class="d-block d-lg-flex gap-1 mt-3 price-chart-container">
<div class="d-block d-lg-flex gap-1 mt-2 price-chart-container">
<div class="col-12">
<div class="alert alert-dark rounded p-2 mb-0">
<h6>Market Price History <span class="small text-secondary">({condition})</span></h6>
@@ -232,10 +304,14 @@ const altSearchUrl = (card: any) => {
</div>
<!-- External links column -->
<div class="col-sm-12 col-md-2 mt-0 mt-md-5 d-flex flex-row flex-md-column">
<div class="col-sm-12 col-md-2 mt-0 mt-md-4 d-flex flex-row flex-md-column">
<a class="btn btn-dark mb-2 w-100 p-2" href={`https://www.tcgplayer.com/product/${card?.productId}`} target="_blank" onclick="dataLayer.push({'event': 'tcgplayerClick', 'tcgplayerUrl': this.getAttribute('href')});"><img src="/vendors/tcgplayer.webp"> <span class="d-none d-lg-inline">TCGPlayer</span></a>
<a class="btn btn-dark mb-2 w-100 p-2" href={`${ebaySearchUrl(card)}`} target="_blank" onclick="dataLayer.push({'event': 'ebayClick', 'ebayUrl': this.getAttribute('href')});"><span set:html={ebay} /></a>
<a class="btn btn-dark mb-2 w-100 p-2" href={`${altSearchUrl(card)}`} target="_blank" onclick="dataLayer.push({'event': 'altClick', 'altUrl': this.getAttribute('href')});"><svg width="48" height="20.16" viewBox="0 0 48 20" fill="none"><path d="M14.2761 19.9996H18.5308L11.6934 0.0712891H7.76953L14.2761 19.9996Z" fill="#ffffff"></path><path d="M6.17778 19.9986H6.14536L3.19643 11.2305L0 19.9988L6.17768 19.9989L6.17778 19.9986Z" fill="#ffffff"></path><path d="M24.7842 0H20.6759V19.9661H34.3427V16.5426H24.7842V0Z" fill="#ffffff"></path><path d="M41.6644 3.42355H47.4981V0H31.5033V3.42355H37.5561V19.9661H41.6644V3.42355Z" fill="#ffffff"></path></svg></a>
<div class="d-flex gap-2 mt-auto align-items-end justify-content-evenly">
<button type="button" class="btn btn-outline-danger flex-fill" data-inventory-delete>Delete</button>
<button type="submit" class="btn btn-success flex-fill flex-grow-1">Save</button>
</div>
</div>
</div>
@@ -243,4 +319,4 @@ const altSearchUrl = (card: any) => {
</div>
</div>
</Fragment>
)}
)}