diff --git a/backend/src/app.ts b/backend/src/app.ts index 69b629a..fd92b80 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -117,7 +117,7 @@ const pool = databaseUrl password: postgresPassword, }); -const defaultCalibers = ['9mm', '.22 LR', '5.56 NATO', '.308 Win', '12 Gauge', '.45 ACP']; +const defaultCalibers = ['9mm', '.22 LR', '5.56 NATO', '.308 Win', '12 Gauge - Birdshot', '12 Gauge - Buckshot', '12 Gauge - Slug', '12 Gauge Sporting', '.45 ACP']; const firearmCategories = ['Handgun', 'Rifle', 'Shotgun', 'PCC', 'Other']; const sessionHours = 24 * 7; @@ -384,6 +384,21 @@ const ensureSchema = async () => { }; const ensureProfileDefaults = async (profileId: string) => { + await pool.query( + `UPDATE calibers + SET name = '12 Gauge Sporting', + is_default = TRUE + WHERE profile_id = $1 + AND name = '12 Gauge' + AND NOT EXISTS ( + SELECT 1 + FROM calibers existing + WHERE existing.profile_id = $1 + AND existing.name = '12 Gauge Sporting' + )`, + [profileId], + ); + for (const caliber of defaultCalibers) { const caliberResult = await pool.query( `INSERT INTO calibers (profile_id, name, is_default, is_active) diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 02872f1..ad648da 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -117,7 +117,8 @@ const ammoPageSelectionKey = 'arsenal-iq-ammo-page-calibers'; const ammoPageSelectionMigrationKey = 'arsenal-iq-ammo-page-calibers-v2'; const firearmCategories = ['Handgun', 'Rifle', 'Shotgun', 'PCC', 'Other']; const allFirearmCategoriesLabel = 'All categories'; -const defaultCaliberNames = ['9mm', '.22 LR', '5.56 NATO', '.308 Win', '12 Gauge', '.45 ACP']; +const defaultCaliberNames = ['9mm', '.22 LR', '5.56 NATO', '.308 Win', '12 Gauge - Birdshot', '12 Gauge - Buckshot', '12 Gauge - Slug', '12 Gauge Sporting', '.45 ACP']; +const boxTrackedCalibers = ['12 Gauge Sporting']; const currency = new Intl.NumberFormat('en-US', { style: 'currency', @@ -189,6 +190,18 @@ const getCategorySilhouette = (category: string) => { return silhouettes[normalized] ?? silhouettes.other; }; +const isBoxTrackedCaliber = (caliber: string) => boxTrackedCalibers.includes(caliber); + +const getAmmoUnitLabel = (caliber: string, quantity: number) => { + if (isBoxTrackedCaliber(caliber)) { + return quantity === 1 ? 'box' : 'boxes'; + } + + return quantity === 1 ? 'round' : 'rounds'; +}; + +const getAmmoUnitNoun = (caliber: string) => (isBoxTrackedCaliber(caliber) ? 'boxes' : 'rounds'); + const resolveFirearmImageUrl = (imageUrl: string | null | undefined) => { const trimmed = imageUrl?.trim(); @@ -1057,7 +1070,7 @@ export default function Home() { ) : null}
- {activeView === 'ammo' ? 'Total rounds' : 'Total firearm value'} + {activeView === 'ammo' ? 'Total ammo units' : 'Total firearm value'} {activeView === 'ammo' ? data.summary.totalAmmoRounds @@ -1297,14 +1310,14 @@ export default function Home() {
{ammoChartData.length === 0 ? ( -

Add rounds to a caliber to see how your inventory stacks up.

+

Add ammo to a caliber to see how your inventory stacks up.

) : (
{ammoChartData.map((inventory) => (
{inventory.caliber} - {inventory.roundsOnHand.toLocaleString()} rounds + {inventory.roundsOnHand.toLocaleString()} {getAmmoUnitLabel(inventory.caliber, inventory.roundsOnHand)}