Updated inventory modal styling and added inventory button to card grid
This commit is contained in:
@@ -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">
|
<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">
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
<ul id="catalogList" class="list-group list-group-flush">
|
<ul id="catalogList" class="list-group list-group-flush">
|
||||||
@@ -96,12 +89,10 @@ const catalogs = catalogRows
|
|||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main class="col-12 col-md-10 p-4">
|
<main class="col-12 col-md-10 mt-4">
|
||||||
<div class="d-flex flex-wrap gap-2 align-items-center mb-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 cards</a>
|
||||||
|
|
||||||
<a href="/pokemon" class="btn btn-vendor">+ Add Card</a>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -164,8 +155,8 @@ const catalogs = catalogRows
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer border-secondary">
|
<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-outline-danger" data-bs-dismiss="modal">Cancel</button>
|
||||||
<button type="button" class="btn btn-success" id="csvUploadBtn">Upload & Preview</button>
|
<button type="button" class="btn btn-success" id="csvUploadBtn">Upload</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ const altSearchUrl = (card: any) => {
|
|||||||
|
|
||||||
</datalist>
|
</datalist>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
Type a name or pick an existing catalog.
|
Enter a name or pick an existing catalog.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ export const prerender = false;
|
|||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
|
|
||||||
// auth check for inventory management features
|
// auth check for inventory management features
|
||||||
//const { canAddInventory } = Astro.locals;
|
const { canAddInventory } = Astro.locals;
|
||||||
const canAddInventory = false;
|
// const canAddInventory = false;
|
||||||
|
|
||||||
// all the facet fields we want to use for filtering
|
// all the facet fields we want to use for filtering
|
||||||
const facetFields:any = {
|
const facetFields:any = {
|
||||||
|
|||||||
@@ -48,7 +48,17 @@ const purchaseDate = inv?.createdAt ? new Date(inv.createdAt).toISOString().spli
|
|||||||
const marketPrice = sku?.marketPrice != null ? Number(sku.marketPrice) : null;
|
const marketPrice = sku?.marketPrice != null ? Number(sku.marketPrice) : null;
|
||||||
const condition = sku?.condition || 'Near Mint';
|
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 ──────────────────────────
|
// ── Price history for this SKU's condition only ──────────────────────────
|
||||||
const priceHistoryForChart = (sku?.history ?? [])
|
const priceHistoryForChart = (sku?.history ?? [])
|
||||||
@@ -132,40 +142,105 @@ const altSearchUrl = (card: any) => {
|
|||||||
<!-- Inventory details + edit column -->
|
<!-- Inventory details + edit column -->
|
||||||
<div class="col-sm-12 col-md-7">
|
<div class="col-sm-12 col-md-7">
|
||||||
|
|
||||||
<!-- Static fields from the inventory entry -->
|
<!-- Static fields (left) + editable Note/Catalog (right), side by side -->
|
||||||
<div class="d-flex flex-fill flex-row gap-1 flex-wrap flex-lg-nowrap">
|
<div class="row gx-3 gy-2">
|
||||||
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
|
<!-- Left column: read-only stats -->
|
||||||
<h6 class="mb-auto">Quantity</h6>
|
<div class="col-12 col-lg-6">
|
||||||
<p class="mb-0 mt-1">{quantity}</p>
|
<label class="form-label">Condition</label>
|
||||||
</div>
|
<div class="btn-group condition-input w-100 col-12" role="group" aria-label="Condition">
|
||||||
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
|
<input
|
||||||
<h6 class="mb-auto">Purchase Price</h6>
|
type="radio"
|
||||||
<p class="mb-0 mt-1">{purchasePrice != null ? `$${purchasePrice.toFixed(2)}` : '—'}</p>
|
class="btn-check"
|
||||||
</div>
|
name="condition"
|
||||||
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
|
id="cond-nm"
|
||||||
<h6 class="mb-auto">Condition</h6>
|
value="Near Mint"
|
||||||
<p class="mb-0 mt-1">{condition}</p>
|
autocomplete="off"
|
||||||
</div>
|
checked
|
||||||
<div class="alert alert-dark rounded p-2 flex-fill d-flex flex-column mb-0">
|
/>
|
||||||
<h6 class="mb-auto">Market Price</h6>
|
<label class="btn btn-cond-nm" for="cond-nm">NM</label>
|
||||||
<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>
|
|
||||||
|
|
||||||
<!-- Editable fields: Catalog + Note -->
|
<input
|
||||||
<form data-inventory-edit-form class="mt-3" novalidate>
|
type="radio"
|
||||||
<input type="hidden" name="action" value="update" />
|
class="btn-check"
|
||||||
<input type="hidden" name="inventoryId" value={inv?.inventoryId} />
|
name="condition"
|
||||||
<input type="hidden" name="cardId" value={card?.cardId} />
|
id="cond-lp"
|
||||||
<input type="hidden" name="quantity" value={quantity} />
|
value="Lightly Played"
|
||||||
<input type="hidden" name="purchasePrice" value={purchasePrice ?? 0} />
|
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>
|
<label for="catalogName" class="form-label">Catalog</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -181,27 +256,24 @@ const altSearchUrl = (card: any) => {
|
|||||||
<datalist id="catalogSuggestions">
|
<datalist id="catalogSuggestions">
|
||||||
{catalogs.map(name => <option value={name}></option>)}
|
{catalogs.map(name => <option value={name}></option>)}
|
||||||
</datalist>
|
</datalist>
|
||||||
</div>
|
|
||||||
<div class="col-12 col-lg-7">
|
<label for="note" class="form-label mt-2">Note</label>
|
||||||
<label for="note" class="form-label">Note</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="note"
|
id="note"
|
||||||
name="note"
|
name="note"
|
||||||
rows="2"
|
rows="5"
|
||||||
maxlength="255"
|
maxlength="255"
|
||||||
placeholder="e.g. bought at local shop, gift, graded copy…"
|
placeholder="e.g. bought at local shop, gift, graded copy…"
|
||||||
>{inv?.note ?? ''}</textarea>
|
>{inv?.note ?? ''}</textarea>
|
||||||
</div>
|
|
||||||
<div class="col-12 d-flex justify-content-end gap-2 pt-1">
|
</form>
|
||||||
<button type="button" class="btn btn-outline-danger" data-inventory-delete>Delete</button>
|
|
||||||
<button type="submit" class="btn btn-success">Save</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Market Price History chart for this condition -->
|
<!-- 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="col-12">
|
||||||
<div class="alert alert-dark rounded p-2 mb-0">
|
<div class="alert alert-dark rounded p-2 mb-0">
|
||||||
<h6>Market Price History <span class="small text-secondary">({condition})</span></h6>
|
<h6>Market Price History <span class="small text-secondary">({condition})</span></h6>
|
||||||
@@ -232,10 +304,14 @@ const altSearchUrl = (card: any) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- External links column -->
|
<!-- 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={`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={`${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>
|
<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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -243,4 +319,4 @@ const altSearchUrl = (card: any) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
Reference in New Issue
Block a user