diff --git a/src/components/AddItemModal.vue b/src/components/AddItemModal.vue
index ba3ab07..6d3253b 100644
--- a/src/components/AddItemModal.vue
+++ b/src/components/AddItemModal.vue
@@ -32,6 +32,22 @@
+
+
@@ -200,6 +216,14 @@ const types = [
{ value: 'sealed', label: 'Sealed Product', icon: '📦' },
]
+const conditions = [
+ { value: 'NM', label: 'NM' },
+ { value: 'LP', label: 'LP' },
+ { value: 'MP', label: 'MP' },
+ { value: 'HP', label: 'HP' },
+ { value: 'DMG', label: 'DMG' },
+]
+
const gradesByCompany = {
PSA: ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1'],
BGS: ['10', '9.5', '9', '8.5', '8', '7.5', '7', '6', '5', '4', '3', '2', '1'],
@@ -259,6 +283,7 @@ const form = ref({
priceVariant: '',
gradingCompany: 'PSA',
grade: '10',
+ condition: 'NM',
purchasePrice: null,
currentValue: null,
quantity: 1,
@@ -314,6 +339,7 @@ function submit() {
subtypes: props.card.subtypes,
},
priceVariant: form.value.priceVariant,
+ condition: form.value.condition,
currentMarketPrice: currentPrice.value,
}
} else if (itemType.value === 'graded') {
@@ -390,6 +416,30 @@ watch(() => props.card, () => {
.type-tab:hover { background: var(--bg-hover); color: var(--text-primary); }
.type-tab.active { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); }
+.condition-chips {
+ display: flex;
+ gap: 8px;
+ margin-top: 4px;
+}
+.condition-chip {
+ flex: 1;
+ padding: 6px 4px;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ background: var(--bg-card);
+ color: var(--text-secondary);
+ font-size: 11px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.15s;
+}
+.condition-chip:hover { border-color: var(--text-muted); }
+.condition-chip.active {
+ background: var(--accent);
+ color: white;
+ border-color: var(--accent);
+}
+
.card-preview {
display: flex;
align-items: center;
diff --git a/src/stores/portfolio.js b/src/stores/portfolio.js
index f556724..b606853 100644
--- a/src/stores/portfolio.js
+++ b/src/stores/portfolio.js
@@ -6,6 +6,7 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { loadState, saveState, isStale as _isStale, hasNeverPriced as _hasNeverPriced } from '../db'
+import { getAdjustedPrice } from '../utils/conditionMultipliers'
// Legacy localStorage keys (for migration)
const LEGACY_PORTFOLIOS_KEY = 'rarebox_portfolios'
@@ -152,9 +153,14 @@ export const usePortfolioStore = defineStore('portfolio', () => {
return portfolios.value.reduce((total, p) => {
return total + p.items.reduce((sum, item) => {
const qty = item.quantity || 1
- const value = item.type === 'card'
+ const basePrice = item.type === 'card'
? (item.currentMarketPrice || item.purchasePrice || 0)
: (item.currentValue || item.purchasePrice || 0)
+
+ const value = item.type === 'card'
+ ? getAdjustedPrice(basePrice, item.condition, !!item.grading)
+ : basePrice
+
return sum + value * qty
}, 0)
}, 0)
@@ -273,16 +279,25 @@ export const usePortfolioStore = defineStore('portfolio', () => {
const totalCost = items.reduce((s, i) => s + (i.purchasePrice || 0) * (i.quantity || 1), 0)
const totalValue = items.reduce((s, i) => {
const qty = i.quantity || 1
- const val = i.type === 'card'
+ const basePrice = i.type === 'card'
? (i.currentMarketPrice || i.purchasePrice || 0)
: (i.currentValue || i.purchasePrice || 0)
+
+ const val = i.type === 'card'
+ ? getAdjustedPrice(basePrice, i.condition, !!i.grading)
+ : basePrice
+
return s + val * qty
}, 0)
const gain = totalValue - totalCost
const gainPct = totalCost > 0 ? (gain / totalCost) * 100 : 0
const topGainer = items.reduce((best, item) => {
const cost = (item.purchasePrice || 0)
- const val = item.type === 'card' ? (item.currentMarketPrice || cost) : (item.currentValue || cost)
+ const basePrice = item.type === 'card' ? (item.currentMarketPrice || cost) : (item.currentValue || cost)
+ const val = item.type === 'card'
+ ? getAdjustedPrice(basePrice, item.condition, !!item.grading)
+ : basePrice
+
const g = cost > 0 ? (val - cost) / cost * 100 : 0
return g > (best?.gain || -Infinity) ? { item, gain: g } : best
}, null)
@@ -301,9 +316,14 @@ export const usePortfolioStore = defineStore('portfolio', () => {
const values = {}
for (const item of portfolio.items) {
- const price = item.type === 'card'
+ const basePrice = item.type === 'card'
? (item.currentMarketPrice || item.purchasePrice || 0)
: (item.currentValue || item.purchasePrice || 0)
+
+ const price = item.type === 'card'
+ ? getAdjustedPrice(basePrice, item.condition, !!item.grading)
+ : basePrice
+
if (price > 0) values[item.id] = price
}
diff --git a/src/utils/conditionMultipliers.js b/src/utils/conditionMultipliers.js
new file mode 100644
index 0000000..3065710
--- /dev/null
+++ b/src/utils/conditionMultipliers.js
@@ -0,0 +1,27 @@
+/**
+ * Condition multipliers for raw cards.
+ * Based on industry standards (e.g., Card Ladder).
+ * NM (Near Mint) is the baseline (1.0).
+ */
+export const CONDITION_MULTIPLIERS = {
+ 'NM': 1.0,
+ 'LP': 0.8,
+ 'MP': 0.5,
+ 'HP': 0.3,
+ 'DMG': 0.15
+};
+
+/**
+ * Calculates the adjusted price based on condition.
+ * Only applies to raw cards. Graded items are untouched.
+ *
+ * @param {number} price - The raw market price (assumed NM)
+ * @param {string} condition - The condition code (NM, LP, etc.)
+ * @param {boolean} isGraded - Whether the item is a graded slab
+ * @returns {number} The condition-adjusted price
+ */
+export function getAdjustedPrice(price, condition = 'NM', isGraded = false) {
+ if (isGraded) return price;
+ const multiplier = CONDITION_MULTIPLIERS[condition] || 1.0;
+ return price * multiplier;
+}