fix(admin): Clean up store detail and intelligence pages
- Remove Update dropdown from DispensaryDetail page - Remove Crawl Now button from StoreDetailPage - Change "Last Crawl" to "Last Updated" on both detail pages - Tone down emerald colors on StoreDetailPage (use gray borders/tabs) - Simplify THC/CBD/Stock badges to plain text - Remove duplicate state dropdown from IntelligenceStores filters - Make store rows clickable in IntelligenceStores 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -204,47 +204,6 @@ export function DispensaryDetail() {
|
|||||||
Back to Dispensaries
|
Back to Dispensaries
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Update Dropdown */}
|
|
||||||
<div className="relative">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowUpdateDropdown(!showUpdateDropdown)}
|
|
||||||
disabled={isUpdating}
|
|
||||||
className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed"
|
|
||||||
>
|
|
||||||
<RefreshCw className={`w-4 h-4 ${isUpdating ? 'animate-spin' : ''}`} />
|
|
||||||
{isUpdating ? 'Updating...' : 'Update'}
|
|
||||||
{!isUpdating && <ChevronDown className="w-4 h-4" />}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showUpdateDropdown && !isUpdating && (
|
|
||||||
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-10">
|
|
||||||
<button
|
|
||||||
onClick={() => handleUpdate('products')}
|
|
||||||
className="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-t-lg"
|
|
||||||
>
|
|
||||||
Products
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleUpdate('brands')}
|
|
||||||
className="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
|
||||||
>
|
|
||||||
Brands
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleUpdate('specials')}
|
|
||||||
className="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
|
||||||
>
|
|
||||||
Specials
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleUpdate('all')}
|
|
||||||
className="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-b-lg border-t border-gray-200"
|
|
||||||
>
|
|
||||||
All
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dispensary Header */}
|
{/* Dispensary Header */}
|
||||||
@@ -266,7 +225,7 @@ export function DispensaryDetail() {
|
|||||||
<div className="flex items-center gap-2 text-sm text-gray-600 bg-gray-50 px-4 py-2 rounded-lg">
|
<div className="flex items-center gap-2 text-sm text-gray-600 bg-gray-50 px-4 py-2 rounded-lg">
|
||||||
<Calendar className="w-4 h-4" />
|
<Calendar className="w-4 h-4" />
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium">Last Crawl Date:</span>
|
<span className="font-medium">Last Updated:</span>
|
||||||
<span className="ml-2">
|
<span className="ml-2">
|
||||||
{dispensary.last_menu_scrape
|
{dispensary.last_menu_scrape
|
||||||
? new Date(dispensary.last_menu_scrape).toLocaleDateString('en-US', {
|
? new Date(dispensary.last_menu_scrape).toLocaleDateString('en-US', {
|
||||||
|
|||||||
@@ -140,14 +140,14 @@ export function IntelligenceBrands() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/admin/intelligence/stores')}
|
onClick={() => navigate('/admin/intelligence/stores')}
|
||||||
className="btn btn-sm btn-outline gap-1"
|
className="btn btn-sm gap-1 bg-white border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
<MapPin className="w-4 h-4" />
|
<MapPin className="w-4 h-4" />
|
||||||
<span>Stores</span>
|
<span>Stores</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/admin/intelligence/pricing')}
|
onClick={() => navigate('/admin/intelligence/pricing')}
|
||||||
className="btn btn-sm btn-outline gap-1"
|
className="btn btn-sm gap-1 bg-white border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
<DollarSign className="w-4 h-4" />
|
<DollarSign className="w-4 h-4" />
|
||||||
<span>Pricing</span>
|
<span>Pricing</span>
|
||||||
|
|||||||
@@ -121,14 +121,14 @@ export function IntelligencePricing() {
|
|||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/admin/intelligence/brands')}
|
onClick={() => navigate('/admin/intelligence/brands')}
|
||||||
className="btn btn-sm btn-outline gap-1"
|
className="btn btn-sm gap-1 bg-white border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
<Building2 className="w-4 h-4" />
|
<Building2 className="w-4 h-4" />
|
||||||
<span>Brands</span>
|
<span>Brands</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/admin/intelligence/stores')}
|
onClick={() => navigate('/admin/intelligence/stores')}
|
||||||
className="btn btn-sm btn-outline gap-1"
|
className="btn btn-sm gap-1 bg-white border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
<MapPin className="w-4 h-4" />
|
<MapPin className="w-4 h-4" />
|
||||||
<span>Stores</span>
|
<span>Stores</span>
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export function IntelligenceStores() {
|
|||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/admin/intelligence/brands')}
|
onClick={() => navigate('/admin/intelligence/brands')}
|
||||||
className="btn btn-sm btn-outline gap-1"
|
className="btn btn-sm gap-1 bg-white border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
<Building2 className="w-4 h-4" />
|
<Building2 className="w-4 h-4" />
|
||||||
<span>Brands</span>
|
<span>Brands</span>
|
||||||
@@ -159,7 +159,7 @@ export function IntelligenceStores() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/admin/intelligence/pricing')}
|
onClick={() => navigate('/admin/intelligence/pricing')}
|
||||||
className="btn btn-sm btn-outline gap-1"
|
className="btn btn-sm gap-1 bg-white border-gray-300 text-gray-700 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
<DollarSign className="w-4 h-4" />
|
<DollarSign className="w-4 h-4" />
|
||||||
<span>Pricing</span>
|
<span>Pricing</span>
|
||||||
@@ -220,26 +220,6 @@ export function IntelligenceStores() {
|
|||||||
className="input input-bordered input-sm w-full pl-10"
|
className="input input-bordered input-sm w-full pl-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="dropdown">
|
|
||||||
<button tabIndex={0} className="btn btn-sm btn-outline gap-2">
|
|
||||||
{stateLabel}
|
|
||||||
<ChevronDown className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
<ul tabIndex={0} className="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-40 max-h-60 overflow-y-auto">
|
|
||||||
<li>
|
|
||||||
<a onClick={() => setSelectedState(null)} className={isAllStates ? 'active' : ''}>
|
|
||||||
All States
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{localStates.map(state => (
|
|
||||||
<li key={state}>
|
|
||||||
<a onClick={() => setSelectedState(state)} className={selectedState === state ? 'active' : ''}>
|
|
||||||
{state}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm text-gray-500">
|
<span className="text-sm text-gray-500">
|
||||||
Showing {filteredStores.length} of {stores.length} stores
|
Showing {filteredStores.length} of {stores.length} stores
|
||||||
</span>
|
</span>
|
||||||
@@ -273,7 +253,7 @@ export function IntelligenceStores() {
|
|||||||
<tr
|
<tr
|
||||||
key={store.id}
|
key={store.id}
|
||||||
className="hover:bg-gray-50 cursor-pointer"
|
className="hover:bg-gray-50 cursor-pointer"
|
||||||
onClick={() => navigate(`/admin/orchestrator/stores?storeId=${store.id}`)}
|
onClick={() => navigate(`/stores/list/${store.id}`)}
|
||||||
>
|
>
|
||||||
<td>
|
<td>
|
||||||
<span className="font-medium">{store.name}</span>
|
<span className="font-medium">{store.name}</span>
|
||||||
|
|||||||
@@ -153,29 +153,6 @@ export function StoreDetailPage() {
|
|||||||
Back to Stores
|
Back to Stores
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Update Button */}
|
|
||||||
<div className="relative">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowUpdateDropdown(!showUpdateDropdown)}
|
|
||||||
disabled={isUpdating}
|
|
||||||
className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed"
|
|
||||||
>
|
|
||||||
<RefreshCw className={`w-4 h-4 ${isUpdating ? 'animate-spin' : ''}`} />
|
|
||||||
{isUpdating ? 'Crawling...' : 'Crawl Now'}
|
|
||||||
{!isUpdating && <ChevronDown className="w-4 h-4" />}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showUpdateDropdown && !isUpdating && (
|
|
||||||
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-10">
|
|
||||||
<button
|
|
||||||
onClick={handleCrawl}
|
|
||||||
className="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-lg"
|
|
||||||
>
|
|
||||||
Start Full Crawl
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Store Header */}
|
{/* Store Header */}
|
||||||
@@ -200,7 +177,7 @@ export function StoreDetailPage() {
|
|||||||
<div className="flex items-center gap-2 text-sm text-gray-600 bg-gray-50 px-4 py-2 rounded-lg">
|
<div className="flex items-center gap-2 text-sm text-gray-600 bg-gray-50 px-4 py-2 rounded-lg">
|
||||||
<Clock className="w-4 h-4" />
|
<Clock className="w-4 h-4" />
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium">Last Crawl:</span>
|
<span className="font-medium">Last Updated:</span>
|
||||||
<span className="ml-2">
|
<span className="ml-2">
|
||||||
{lastCrawl?.completed_at
|
{lastCrawl?.completed_at
|
||||||
? new Date(lastCrawl.completed_at).toLocaleDateString('en-US', {
|
? new Date(lastCrawl.completed_at).toLocaleDateString('en-US', {
|
||||||
@@ -212,15 +189,6 @@ export function StoreDetailPage() {
|
|||||||
})
|
})
|
||||||
: 'Never'}
|
: 'Never'}
|
||||||
</span>
|
</span>
|
||||||
{lastCrawl?.status && (
|
|
||||||
<span className={`ml-2 px-2 py-0.5 rounded text-xs ${
|
|
||||||
lastCrawl.status === 'completed' ? 'bg-green-100 text-green-800' :
|
|
||||||
lastCrawl.status === 'failed' ? 'bg-red-100 text-red-800' :
|
|
||||||
'bg-yellow-100 text-yellow-800'
|
|
||||||
}`}>
|
|
||||||
{lastCrawl.status}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -282,8 +250,8 @@ export function StoreDetailPage() {
|
|||||||
setStockFilter('in_stock');
|
setStockFilter('in_stock');
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
}}
|
}}
|
||||||
className={`bg-white rounded-lg border p-4 hover:border-blue-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
className={`bg-white rounded-lg border p-4 hover:border-gray-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
||||||
stockFilter === 'in_stock' ? 'border-blue-500' : 'border-gray-200'
|
stockFilter === 'in_stock' ? 'border-gray-400' : 'border-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -303,8 +271,8 @@ export function StoreDetailPage() {
|
|||||||
setStockFilter('out_of_stock');
|
setStockFilter('out_of_stock');
|
||||||
setSearchQuery('');
|
setSearchQuery('');
|
||||||
}}
|
}}
|
||||||
className={`bg-white rounded-lg border p-4 hover:border-blue-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
className={`bg-white rounded-lg border p-4 hover:border-gray-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
||||||
stockFilter === 'out_of_stock' ? 'border-blue-500' : 'border-gray-200'
|
stockFilter === 'out_of_stock' ? 'border-gray-400' : 'border-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -320,8 +288,8 @@ export function StoreDetailPage() {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('brands')}
|
onClick={() => setActiveTab('brands')}
|
||||||
className={`bg-white rounded-lg border p-4 hover:border-blue-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
className={`bg-white rounded-lg border p-4 hover:border-gray-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
||||||
activeTab === 'brands' ? 'border-blue-500' : 'border-gray-200'
|
activeTab === 'brands' ? 'border-gray-400' : 'border-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -337,8 +305,8 @@ export function StoreDetailPage() {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('categories')}
|
onClick={() => setActiveTab('categories')}
|
||||||
className={`bg-white rounded-lg border p-4 hover:border-blue-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
className={`bg-white rounded-lg border p-4 hover:border-gray-300 hover:shadow-md transition-all cursor-pointer text-left ${
|
||||||
activeTab === 'categories' ? 'border-blue-500' : 'border-gray-200'
|
activeTab === 'categories' ? 'border-gray-400' : 'border-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -364,7 +332,7 @@ export function StoreDetailPage() {
|
|||||||
}}
|
}}
|
||||||
className={`py-4 px-2 text-sm font-medium border-b-2 ${
|
className={`py-4 px-2 text-sm font-medium border-b-2 ${
|
||||||
activeTab === 'products'
|
activeTab === 'products'
|
||||||
? 'border-blue-600 text-blue-600'
|
? 'border-gray-800 text-gray-900'
|
||||||
: 'border-transparent text-gray-600 hover:text-gray-900'
|
: 'border-transparent text-gray-600 hover:text-gray-900'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -374,7 +342,7 @@ export function StoreDetailPage() {
|
|||||||
onClick={() => setActiveTab('brands')}
|
onClick={() => setActiveTab('brands')}
|
||||||
className={`py-4 px-2 text-sm font-medium border-b-2 ${
|
className={`py-4 px-2 text-sm font-medium border-b-2 ${
|
||||||
activeTab === 'brands'
|
activeTab === 'brands'
|
||||||
? 'border-blue-600 text-blue-600'
|
? 'border-gray-800 text-gray-900'
|
||||||
: 'border-transparent text-gray-600 hover:text-gray-900'
|
: 'border-transparent text-gray-600 hover:text-gray-900'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -384,7 +352,7 @@ export function StoreDetailPage() {
|
|||||||
onClick={() => setActiveTab('categories')}
|
onClick={() => setActiveTab('categories')}
|
||||||
className={`py-4 px-2 text-sm font-medium border-b-2 ${
|
className={`py-4 px-2 text-sm font-medium border-b-2 ${
|
||||||
activeTab === 'categories'
|
activeTab === 'categories'
|
||||||
? 'border-blue-600 text-blue-600'
|
? 'border-gray-800 text-gray-900'
|
||||||
: 'border-transparent text-gray-600 hover:text-gray-900'
|
: 'border-transparent text-gray-600 hover:text-gray-900'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -433,7 +401,7 @@ export function StoreDetailPage() {
|
|||||||
|
|
||||||
{productsLoading ? (
|
{productsLoading ? (
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
<div className="inline-block animate-spin rounded-full h-6 w-6 border-4 border-blue-500 border-t-transparent"></div>
|
<div className="inline-block animate-spin rounded-full h-6 w-6 border-4 border-gray-400 border-t-transparent"></div>
|
||||||
<p className="mt-2 text-sm text-gray-600">Loading products...</p>
|
<p className="mt-2 text-sm text-gray-600">Loading products...</p>
|
||||||
</div>
|
</div>
|
||||||
) : products.length === 0 ? (
|
) : products.length === 0 ? (
|
||||||
@@ -485,9 +453,9 @@ export function StoreDetailPage() {
|
|||||||
<div className="line-clamp-2" title={product.brand || '-'}>{product.brand || '-'}</div>
|
<div className="line-clamp-2" title={product.brand || '-'}>{product.brand || '-'}</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap">
|
<td className="whitespace-nowrap">
|
||||||
<span className="badge badge-ghost badge-sm">{product.type || '-'}</span>
|
<span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded">{product.type || '-'}</span>
|
||||||
{product.subcategory && (
|
{product.subcategory && (
|
||||||
<span className="badge badge-ghost badge-sm ml-1">{product.subcategory}</span>
|
<span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded ml-1">{product.subcategory}</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right font-semibold whitespace-nowrap">
|
<td className="text-right font-semibold whitespace-nowrap">
|
||||||
@@ -500,21 +468,14 @@ export function StoreDetailPage() {
|
|||||||
`$${product.regular_price}`
|
`$${product.regular_price}`
|
||||||
) : '-'}
|
) : '-'}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-center whitespace-nowrap">
|
<td className="text-center whitespace-nowrap text-sm text-gray-700">
|
||||||
{product.thc_percentage ? (
|
{product.thc_percentage ? `${product.thc_percentage}%` : '-'}
|
||||||
<span className="badge badge-success badge-sm">{product.thc_percentage}%</span>
|
|
||||||
) : '-'}
|
|
||||||
</td>
|
</td>
|
||||||
<td className="text-center whitespace-nowrap">
|
<td className="text-center whitespace-nowrap text-sm text-gray-700">
|
||||||
{product.stock_status === 'in_stock' ? (
|
{product.stock_status === 'in_stock' ? 'In Stock' :
|
||||||
<span className="badge badge-success badge-sm">In Stock</span>
|
product.stock_status === 'out_of_stock' ? 'Out' : '-'}
|
||||||
) : product.stock_status === 'out_of_stock' ? (
|
|
||||||
<span className="badge badge-error badge-sm">Out</span>
|
|
||||||
) : (
|
|
||||||
<span className="badge badge-warning badge-sm">Unknown</span>
|
|
||||||
)}
|
|
||||||
</td>
|
</td>
|
||||||
<td className="text-center whitespace-nowrap">
|
<td className="text-center whitespace-nowrap text-sm text-gray-700">
|
||||||
{product.total_quantity != null ? product.total_quantity : '-'}
|
{product.total_quantity != null ? product.total_quantity : '-'}
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap text-xs text-gray-500">
|
<td className="whitespace-nowrap text-xs text-gray-500">
|
||||||
|
|||||||
Reference in New Issue
Block a user