feat: make Product fully independent from MarketplaceProduct
- Add is_digital and product_type columns to Product model - Remove is_digital/product_type properties that derived from MarketplaceProduct - Update Create form with translation tabs, GTIN type, sale price, VAT rate, image - Update Edit form to allow editing is_digital (remove disabled state) - Add Availability field to Edit form - Fix Detail page for directly created products (no marketplace source) - Update vendor_product_service to handle new fields in create/update - Add VendorProductCreate/Update schema fields for translations and is_digital - Add unit tests for is_digital column and direct product creation - Add integration tests for create/update API with new fields - Create product-architecture.md documenting the independent copy pattern - Add migration y3d4e5f6g7h8 for is_digital and product_type columns 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,17 +21,33 @@
|
||||
|
||||
<!-- Product Details -->
|
||||
<div x-show="!loading && product">
|
||||
<!-- Override Info Banner -->
|
||||
<div class="px-4 py-3 mb-6 bg-purple-50 dark:bg-purple-900/20 rounded-lg shadow-md">
|
||||
<!-- Info Banner - adapts based on whether product has marketplace source -->
|
||||
<div class="px-4 py-3 mb-6 rounded-lg shadow-md"
|
||||
:class="product?.marketplace_product_id ? 'bg-purple-50 dark:bg-purple-900/20' : 'bg-blue-50 dark:bg-blue-900/20'">
|
||||
<div class="flex items-start">
|
||||
<span x-html="$icon('information-circle', 'w-5 h-5 text-purple-500 mr-3 mt-0.5 flex-shrink-0')"></span>
|
||||
<span x-html="$icon('information-circle', product?.marketplace_product_id ? 'w-5 h-5 text-purple-500 mr-3 mt-0.5 flex-shrink-0' : 'w-5 h-5 text-blue-500 mr-3 mt-0.5 flex-shrink-0')"></span>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-purple-700 dark:text-purple-300">Vendor Product Catalog Entry</p>
|
||||
<p class="text-xs text-purple-600 dark:text-purple-400 mt-1">
|
||||
This is a vendor-specific copy of a marketplace product. Fields marked with
|
||||
<span class="inline-flex items-center px-1.5 py-0.5 mx-1 text-xs font-medium text-purple-700 bg-purple-100 dark:bg-purple-800 dark:text-purple-300 rounded">Override</span>
|
||||
have been customized for this vendor.
|
||||
</p>
|
||||
<!-- Marketplace-sourced product -->
|
||||
<template x-if="product?.marketplace_product_id">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-purple-700 dark:text-purple-300">Vendor Product Catalog Entry</p>
|
||||
<p class="text-xs text-purple-600 dark:text-purple-400 mt-1">
|
||||
This is a vendor-specific copy of a marketplace product. Fields marked with
|
||||
<span class="inline-flex items-center px-1.5 py-0.5 mx-1 text-xs font-medium text-purple-700 bg-purple-100 dark:bg-purple-800 dark:text-purple-300 rounded">Override</span>
|
||||
have been customized for this vendor.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<!-- Directly created product -->
|
||||
<template x-if="!product?.marketplace_product_id">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-blue-700 dark:text-blue-300">Directly Created Product</p>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-400 mt-1">
|
||||
This product was created directly for this vendor without a marketplace source.
|
||||
All product information is managed independently.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,18 +58,20 @@
|
||||
Quick Actions
|
||||
</h3>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<!-- View Source Product - only for marketplace-sourced products -->
|
||||
<a
|
||||
x-show="product?.marketplace_product_id"
|
||||
:href="'/admin/marketplace-products/' + product?.marketplace_product_id"
|
||||
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple">
|
||||
<span x-html="$icon('database', 'w-4 h-4 mr-2')"></span>
|
||||
View Source Product
|
||||
</a>
|
||||
<button
|
||||
@click="openEditModal()"
|
||||
<a
|
||||
:href="'/admin/vendor-products/' + productId + '/edit'"
|
||||
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-gray-700 dark:text-gray-300 transition-colors duration-150 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600">
|
||||
<span x-html="$icon('pencil', 'w-4 h-4 mr-2')"></span>
|
||||
Edit Overrides
|
||||
</button>
|
||||
<span x-text="product?.marketplace_product_id ? 'Edit Overrides' : 'Edit Product'"></span>
|
||||
</a>
|
||||
<button
|
||||
@click="toggleActive()"
|
||||
:class="product?.is_active
|
||||
@@ -194,7 +212,7 @@
|
||||
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
|
||||
Product Identifiers
|
||||
</h3>
|
||||
<div class="grid gap-4 md:grid-cols-4">
|
||||
<div class="grid gap-4" :class="product?.marketplace_product_id ? 'md:grid-cols-4' : 'md:grid-cols-3'">
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Product ID</p>
|
||||
<p class="text-sm font-mono text-gray-700 dark:text-gray-300" x-text="product?.id || '-'">-</p>
|
||||
@@ -207,41 +225,62 @@
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Vendor SKU</p>
|
||||
<p class="text-sm font-mono text-gray-700 dark:text-gray-300" x-text="product?.vendor_sku || '-'">-</p>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Source SKU - only for marketplace-sourced products -->
|
||||
<div x-show="product?.marketplace_product_id">
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Source SKU</p>
|
||||
<p class="text-sm font-mono text-gray-700 dark:text-gray-300" x-text="product?.source_sku || '-'">-</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Source Information Card -->
|
||||
<div class="px-4 py-3 mb-6 bg-white rounded-lg shadow-md dark:bg-gray-800">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
|
||||
Source Information
|
||||
<!-- Source Information Card - only for marketplace-sourced products -->
|
||||
<template x-if="product?.marketplace_product_id">
|
||||
<div class="px-4 py-3 mb-6 bg-white rounded-lg shadow-md dark:bg-gray-800">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200">
|
||||
Source Information
|
||||
</h3>
|
||||
<a
|
||||
:href="'/admin/marketplace-products/' + product?.marketplace_product_id"
|
||||
class="flex items-center text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400">
|
||||
<span>View Source</span>
|
||||
<span x-html="$icon('arrow-right', 'w-4 h-4 ml-1')"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Marketplace</p>
|
||||
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="product?.source_marketplace || '-'">-</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Source Vendor</p>
|
||||
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="product?.source_vendor || '-'">-</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Marketplace Product ID</p>
|
||||
<p class="text-sm font-mono text-gray-700 dark:text-gray-300" x-text="product?.marketplace_product_id || '-'">-</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Product Origin Card - for directly created products -->
|
||||
<template x-if="!product?.marketplace_product_id">
|
||||
<div class="px-4 py-3 mb-6 bg-white rounded-lg shadow-md dark:bg-gray-800">
|
||||
<h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
|
||||
Product Origin
|
||||
</h3>
|
||||
<a
|
||||
:href="'/admin/marketplace-products/' + product?.marketplace_product_id"
|
||||
class="flex items-center text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400">
|
||||
<span>View Source</span>
|
||||
<span x-html="$icon('arrow-right', 'w-4 h-4 ml-1')"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Marketplace</p>
|
||||
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="product?.source_marketplace || 'Unknown'">-</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Source Vendor</p>
|
||||
<p class="text-sm text-gray-700 dark:text-gray-300" x-text="product?.source_vendor || 'Unknown'">-</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Marketplace Product ID</p>
|
||||
<p class="text-sm font-mono text-gray-700 dark:text-gray-300" x-text="product?.marketplace_product_id || '-'">-</p>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="flex items-center justify-center w-10 h-10 rounded-full bg-blue-100 dark:bg-blue-900/30">
|
||||
<span x-html="$icon('plus-circle', 'w-5 h-5 text-blue-600 dark:text-blue-400')"></span>
|
||||
</span>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Direct Creation</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">This product was created directly in the vendor's catalog</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Description Card -->
|
||||
<div class="px-4 py-3 mb-6 bg-white rounded-lg shadow-md dark:bg-gray-800" x-show="product?.title || product?.description">
|
||||
|
||||
Reference in New Issue
Block a user