2026-04-02 19:24:51 -04:00
import type { APIRoute } from 'astro' ;
import { db } from '../../db/index' ;
2026-04-05 16:09:52 -04:00
import { inventory , skus , cards } from '../../db/schema' ;
2026-04-02 19:24:51 -04:00
import { client } from '../../db/typesense' ;
2026-04-05 16:09:52 -04:00
import { eq , and } from 'drizzle-orm' ;
2026-04-02 19:24:51 -04:00
2026-04-05 16:09:52 -04:00
const GainLoss = ( purchasePrice : any , marketPrice : any ) = > {
2026-04-02 19:24:51 -04:00
if ( ! purchasePrice || ! marketPrice ) return '<div class="fs-5 fw-semibold">N/A</div>' ;
const pp = Number ( purchasePrice ) ;
const mp = Number ( marketPrice ) ;
if ( pp === mp ) return '<div class="fs-5 fw-semibold text-warning">-</div>' ;
2026-04-05 16:09:52 -04:00
if ( pp > mp ) return ` <div class="fs-5 fw-semibold text-critical">- $ ${ ( pp - mp ) . toFixed ( 2 ) } </div> ` ;
return ` <div class="fs-6 fw-semibold text-success">+ $ ${ ( mp - pp ) . toFixed ( 2 ) } </div> ` ;
2026-04-02 19:24:51 -04:00
}
2026-04-05 16:09:52 -04:00
const getInventory = async ( userId : string , cardId : number ) = > {
const inventories = await db
. select ( {
inventoryId : inventory.inventoryId ,
cardId : inventory.cardId ,
condition : inventory.condition ,
quantity : inventory.quantity ,
purchasePrice : inventory.purchasePrice ,
note : inventory.note ,
marketPrice : skus.marketPrice ,
createdAt : inventory.createdAt ,
} )
. from ( inventory )
. leftJoin (
cards ,
eq ( inventory . cardId , cards . cardId )
)
. leftJoin (
skus ,
and (
eq ( cards . productId , skus . productId ) ,
eq ( inventory . condition , skus . condition )
)
)
. where ( and (
eq ( inventory . userId , userId ) ,
eq ( inventory . cardId , cardId )
) ) ;
2026-04-02 19:24:51 -04:00
const invHtml = inventories . map ( inv = > {
2026-04-05 16:09:52 -04:00
const marketPrice = inv . marketPrice ? Number ( inv . marketPrice ) . toFixed ( 2 ) : null ;
const marketPriceDisplay = marketPrice ? ` $ ${ marketPrice } ` : '—' ;
const purchasePriceDisplay = inv . purchasePrice ? ` $ ${ Number ( inv . purchasePrice ) . toFixed ( 2 ) } ` : '—' ;
2026-04-02 19:24:51 -04:00
return `
2026-04-05 16:09:52 -04:00
< article class = "alert alert-dark rounded-4 inventory-entry-card"
2026-04-03 22:50:54 -04:00
data - inventory - id = "${inv.inventoryId}"
data - card - id = "${inv.cardId}"
data - purchase - price = "${inv.purchasePrice}"
data - note = "${(inv.note || '').replace(/" / g , '"' ) } " >
2026-04-05 16:09:52 -04:00
< div class = "d-flex flex-column" >
2026-04-02 19:24:51 -04:00
<!-- Top row -->
2026-04-05 16:09:52 -04:00
< div class = "d-flex justify-content-between gap-3" >
2026-04-02 19:24:51 -04:00
< div class = "min-w-0 flex-grow-1" >
2026-04-05 16:09:52 -04:00
< div class = "fw-semibold fs-6 text-body mb-1" > $ { inv . condition } < / div >
2026-04-02 19:24:51 -04:00
< / div >
2026-04-05 16:09:52 -04:00
< div class = "fs-7 text-secondary" > Added : $ { inv . createdAt ? new Date ( inv . createdAt ) . toLocaleDateString ( ) : '—' } < / div >
2026-04-02 19:24:51 -04:00
< / div >
<!-- Middle row -->
< div class = "row g-2" >
< div class = "col-4" >
< div class = "small text-secondary" > Purchase price < / div >
2026-04-05 16:09:52 -04:00
< div class = "fs-6 fw-semibold" > $ { purchasePriceDisplay } < / div >
2026-04-02 19:24:51 -04:00
< / div >
< div class = "col-4" >
< div class = "small text-secondary" > Market price < / div >
2026-04-05 16:09:52 -04:00
< div class = "fs-6 text-success" > $ { marketPriceDisplay } < / div >
2026-04-02 19:24:51 -04:00
< / div >
< div class = "col-4" >
< div class = "small text-secondary" > Gain / loss < / div >
2026-04-05 16:09:52 -04:00
$ { GainLoss ( inv . purchasePrice , marketPrice ) }
2026-04-02 19:24:51 -04:00
< / div >
< / div >
<!-- Bottom row -->
2026-04-05 16:09:52 -04:00
< div class = "d-flex justify-content-between align-items-center gap-3 flex-wrap mt-2" >
2026-04-02 19:24:51 -04:00
< div class = "d-flex align-items-center gap-2" >
< span class = "small text-secondary" > Qty < / span >
< div class = "btn-group" role = "group" aria-label = "Quantity controls" >
2026-04-03 22:50:54 -04:00
< button type = "button" class = "btn btn-outline-secondary btn-sm" data-inv-action = "decrement" > − < / button >
< button type = "button" class = "btn btn-outline-secondary btn-sm" tabindex = "-1" data - inv - qty > $ { inv . quantity } < / button >
< button type = "button" class = "btn btn-outline-secondary btn-sm" data-inv-action = "increment" > + < / button >
2026-04-02 19:24:51 -04:00
< / div >
< / div >
< div class = "d-flex align-items-center gap-2 flex-wrap" >
2026-04-03 22:50:54 -04:00
< button type = "button" class = "btn btn-sm btn-outline-secondary" data-inv-action = "update" > Edit < / button >
2026-04-05 16:09:52 -04:00
< button type = "button" class = "btn btn-sm btn-outline-danger" data-inv-action = "remove" onclick = "if(!confirm('Are you sure you want to remove this card from your inventory?')) event.stopImmediatePropagation();" > Remove < / button >
2026-04-02 19:24:51 -04:00
< / div >
< / div >
< / div >
< / article > ` ;
} ) ;
return new Response (
invHtml . join ( '' ) ,
{
status : 200 ,
headers : { 'Content-Type' : 'text/html' } ,
}
) ;
}
2026-04-05 16:09:52 -04:00
const addToInventory = async ( userId : string , cardId : number , condition : string , purchasePrice : number , quantity : number , note : string , catalogName : string ) = > {
2026-04-02 19:24:51 -04:00
const inv = await db . insert ( inventory ) . values ( {
userId : userId ,
cardId : cardId ,
catalogName : catalogName ,
condition : condition ,
purchasePrice : purchasePrice ,
quantity : quantity ,
note : note ,
} ) . returning ( ) ;
await client . collections ( 'inventories' ) . documents ( ) . import ( inv . map ( i = > ( {
id : i.inventoryId ,
userId : i.userId ,
catalogName : i.catalogName ,
card_id : i.cardId.toString ( ) ,
} ) ) ) ;
}
2026-04-05 16:09:52 -04:00
const removeFromInventory = async ( inventoryId : string ) = > {
await db . delete ( inventory ) . where ( eq ( inventory . inventoryId , inventoryId ) ) ;
2026-04-02 19:24:51 -04:00
await client . collections ( 'inventories' ) . documents ( inventoryId ) . delete ( ) ;
}
2026-04-05 16:09:52 -04:00
const updateInventory = async ( inventoryId : string , quantity : number , purchasePrice : number , note : string ) = > {
2026-04-02 19:24:51 -04:00
await db . update ( inventory ) . set ( {
quantity : quantity ,
purchasePrice : purchasePrice ,
note : note ,
2026-04-05 16:09:52 -04:00
} ) . where ( eq ( inventory . inventoryId , inventoryId ) ) ;
2026-04-02 19:24:51 -04:00
}
export const POST : APIRoute = async ( { request , locals } ) = > {
const formData = await request . formData ( ) ;
const action = formData . get ( 'action' ) ;
const cardId = Number ( formData . get ( 'cardId' ) ) || 0 ;
const { userId } = locals . auth ( ) ;
switch ( action ) {
case 'add' :
const condition = formData . get ( 'condition' ) ? . toString ( ) || 'Unknown' ;
const purchasePrice = Number ( formData . get ( 'purchasePrice' ) ) || 0 ;
const quantity = Number ( formData . get ( 'quantity' ) ) || 1 ;
const note = formData . get ( 'note' ) ? . toString ( ) || '' ;
const catalogName = formData . get ( 'catalogName' ) ? . toString ( ) || 'Default' ;
await addToInventory ( userId ! , cardId , condition , purchasePrice , quantity , note , catalogName ) ;
break ;
case 'remove' :
const inventoryId = formData . get ( 'inventoryId' ) ? . toString ( ) || '' ;
await removeFromInventory ( inventoryId ) ;
break ;
case 'update' :
const invId = formData . get ( 'inventoryId' ) ? . toString ( ) || '' ;
const qty = Number ( formData . get ( 'quantity' ) ) || 1 ;
const price = Number ( formData . get ( 'purchasePrice' ) ) || 0 ;
const invNote = formData . get ( 'note' ) ? . toString ( ) || '' ;
await updateInventory ( invId , qty , price , invNote ) ;
break ;
default :
2026-04-03 22:10:41 -04:00
return getInventory ( userId ! , cardId ) ;
2026-04-02 19:24:51 -04:00
}
return getInventory ( userId ! , cardId ) ;
} ;