Add used-product flag for store listings.

Merchants can mark items as used on create/edit, see a used badge on cards, and filter the store list for used products.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-19 12:17:20 +03:30
co-authored by Cursor
parent 32f0dbab63
commit 61d145d92e
12 changed files with 110 additions and 2 deletions
@@ -155,6 +155,31 @@
opacity: 0.55;
}
.usedCheckLabel {
display: flex;
align-items: center;
gap: 10px;
margin: 4px 0 12px;
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
cursor: pointer;
user-select: none;
}
.usedCheck {
width: 16px;
height: 16px;
accent-color: var(--primary);
cursor: pointer;
flex-shrink: 0;
}
.usedCheck:disabled {
cursor: not-allowed;
opacity: 0.55;
}
.compactInput:disabled {
opacity: 0.55;
cursor: not-allowed;
@@ -55,6 +55,7 @@ export function CreateStoreItemsModal({
const [closing, setClosing] = useState(false)
const [products, setProducts] = useState<{ id: string; title: string; nameFa: string }[]>([])
const [productId, setProductId] = useState('')
const [isUsed, setIsUsed] = useState(false)
const [variations, setVariations] = useState<ProductVariationSelection[]>([])
const [rows, setRows] = useState<StoreItemDraftRow[]>([])
const [isLoadingProducts, setIsLoadingProducts] = useState(false)
@@ -118,6 +119,7 @@ export function CreateStoreItemsModal({
setMounted(false)
setClosing(false)
setProductId('')
setIsUsed(false)
setVariations([])
setRows([])
setError('')
@@ -281,6 +283,7 @@ export function CreateStoreItemsModal({
try {
await batchCreateStoreItems({
productId,
isUsed,
items: buildPayload(),
})
onCreated?.()
@@ -421,6 +424,17 @@ export function CreateStoreItemsModal({
/>
</div>
<label className={rowStyles.usedCheckLabel}>
<input
type="checkbox"
className={rowStyles.usedCheck}
checked={isUsed}
onChange={(e) => setIsUsed(e.target.checked)}
disabled={busy}
/>
<span>{t('storeItems.create.isUsed')}</span>
</label>
{productId && isLoadingVariations && (
<p className={modalStyles.emptyText}>{t('storeItems.create.loadingVariations')}</p>
)}
@@ -72,6 +72,7 @@ export function EditStoreItemsModal({
const [variations, setVariations] = useState<ProductVariationSelection[]>([])
const [rows, setRows] = useState<StoreItemDraftRow[]>([])
const [removedIds, setRemovedIds] = useState<string[]>([])
const [isUsed, setIsUsed] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const [isSubmitting, setIsSubmitting] = useState(false)
const [error, setError] = useState('')
@@ -123,6 +124,7 @@ export function EditStoreItemsModal({
setClosing(false)
setRemovedIds([])
setRows(itemsToRows(items))
setIsUsed(items.some((item) => item.isUsed))
setError('')
const controller = new AbortController()
void loadVariations(productId, controller.signal)
@@ -223,6 +225,7 @@ export function EditStoreItemsModal({
try {
const data = await syncProductStoreItems({
productId,
isUsed,
items: rows.map((row) => ({
id: row.storeItemId,
selections: formVariations.flatMap((variation) => {
@@ -294,6 +297,17 @@ export function EditStoreItemsModal({
<p className={modalStyles.emptyText}>{t('storeItems.create.loadingVariations')}</p>
) : (
<>
<label className={rowStyles.usedCheckLabel}>
<input
type="checkbox"
className={rowStyles.usedCheck}
checked={isUsed}
onChange={(e) => setIsUsed(e.target.checked)}
disabled={isSubmitting}
/>
<span>{t('storeItems.create.isUsed')}</span>
</label>
<div
className={rowStyles.itemRowHeader}
style={{ gridTemplateColumns: gridTemplate }}
@@ -64,7 +64,8 @@
}
.festivalBadge,
.stockBadge {
.stockBadge,
.usedBadge {
position: absolute;
top: 8px;
padding: 4px 10px;
@@ -86,6 +87,14 @@
color: #7c3aed;
}
.usedBadge {
inset-inline-start: 8px;
top: auto;
bottom: 8px;
color: #b45309;
background: rgba(255, 251, 235, 0.95);
}
.stockBadge {
inset-inline-end: 8px;
color: var(--primary);
@@ -115,6 +115,9 @@ export function StoreItemCard({
{listing.showFestival && (
<span className={styles.festivalBadge}>{t('storeItems.card.festival')}</span>
)}
{listing.isUsed && (
<span className={styles.usedBadge}>{t('storeItems.card.used')}</span>
)}
{listing.productTotalStock > 0 && (
<span className={styles.stockBadge}>
{t('storeItems.card.inStock', { count: listing.productTotalStock })}