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:
co-authored by
Cursor
parent
32f0dbab63
commit
61d145d92e
@@ -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 })}
|
||||
|
||||
Reference in New Issue
Block a user