feat: Stock infinity symbol, sale price display, rich text editor, inline create categories/tags
Fixed 4 major UX issues:
1. Stock Column - Show Infinity Symbol
Problem: Stock shows badge even when not managed
Solution:
- Check manage_stock flag
- If true: Show StockBadge with quantity
- If false: Show ∞ (infinity symbol) for unlimited
Result: Clear visual for unlimited stock
2. Type Column & Price Display
Problem: Type column empty, price ignores sale price
Solution:
- Type: Show badge with product.type (simple, variable, etc.)
- Price: Respect sale price hierarchy:
1. price_html (WooCommerce formatted)
2. sale_price (show strikethrough regular + green sale)
3. regular_price (normal display)
4. — (dash for no price)
Result:
- Type visible with badge styling
- Sale prices show with strikethrough
- Clear visual hierarchy
3. Rich Text Editor for Description
Problem: Description shows raw HTML in textarea
Solution:
- Created RichTextEditor component with Tiptap
- Toolbar: Bold, Italic, H2, Lists, Quote, Undo/Redo
- Integrated into GeneralTab
Features:
- WYSIWYG editing
- Keyboard shortcuts
- Clean toolbar UI
- Saves as HTML
Result: Professional rich text editing experience
4. Inline Create Categories & Tags
Problem: Cannot create new categories/tags in product form
Solution:
- Added input + "Add" button above each list
- Press Enter or click Add to create
- Auto-selects newly created item
- Shows loading state
- Toast notifications
Result:
- No need to leave product form
- Seamless workflow
- Better UX
Files Changed:
- index.tsx: Stock ∞, sale price display, type badge
- GeneralTab.tsx: RichTextEditor integration
- OrganizationTab.tsx: Inline create UI
- RichTextEditor.tsx: New reusable component
Note: Variation attribute value issue (screenshot 1) needs API data format investigation
This commit is contained in:
@@ -381,18 +381,31 @@ export default function Products() {
|
||||
</td>
|
||||
<td className="p-3 font-mono text-sm">{product.sku || '—'}</td>
|
||||
<td className="p-3">
|
||||
<StockBadge value={product.stock_status} quantity={product.manage_stock ? product.stock_quantity : undefined} />
|
||||
{product.manage_stock ? (
|
||||
<StockBadge value={product.stock_status} quantity={product.stock_quantity} />
|
||||
) : (
|
||||
<span className="text-sm text-muted-foreground">∞</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="p-3">
|
||||
{product.price_html ? (
|
||||
<span dangerouslySetInnerHTML={{ __html: product.price_html }} />
|
||||
) : product.sale_price ? (
|
||||
<span className="space-x-1">
|
||||
<span className="line-through text-muted-foreground text-sm">{formatMoney(product.regular_price)}</span>
|
||||
<span className="text-emerald-600 font-medium">{formatMoney(product.sale_price)}</span>
|
||||
</span>
|
||||
) : product.regular_price ? (
|
||||
<span>{formatMoney(product.regular_price)}</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="p-3 text-sm capitalize">{product.type || '—'}</td>
|
||||
<td className="p-3 text-sm">
|
||||
<span className="capitalize px-2 py-1 rounded-md bg-muted text-muted-foreground text-xs">
|
||||
{product.type || 'simple'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="p-3 text-right">
|
||||
<Link to={`/products/${product.id}/edit`} className="text-sm text-primary hover:underline">
|
||||
{__('Edit')}
|
||||
|
||||
Reference in New Issue
Block a user