Merge pull request 'fardeen-dev' (#24) from fardeen-dev into dev

Reviewed-on: https://gitea.maskantech.in/gitea_admin/productcatalogue_frontend/pulls/24
This commit is contained in:
fardeen
2026-08-29 11:29:01 +00:00
@@ -33,9 +33,23 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
const [isBulkMode, setIsBulkMode] = useState(false);
const [selectedVariantIds, setSelectedVariantIds] = useState<string[]>([]);
// Strict product isolation & configured variants filtering
const configuredVariants = useMemo(() => {
const productSpecific = variants.filter((v: any) => {
const pId = v.parentProductId || v.product_id || v.productId;
return !pId || pId === productId;
});
if (productSpecific.length <= 1) return productSpecific;
return productSpecific.filter((v: any) => {
const hasAttrObj = v.attributes && Object.keys(v.attributes).length > 0;
const hasAttrArr = Array.isArray(v.values) && v.values.length > 0;
return hasAttrObj || hasAttrArr;
});
}, [variants, productId]);
const variantFilterOptions = useMemo(() => {
const map = new Map<string, { name: string; values: Set<string> }>();
variants.forEach(v => {
configuredVariants.forEach(v => {
if (v.attributes && typeof v.attributes === 'object') {
Object.entries(v.attributes).forEach(([code, val]) => {
if (val !== undefined && val !== null && val !== '') {
@@ -67,7 +81,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
});
});
return result;
}, [variants]);
}, [configuredVariants]);
const [loading, setLoading] = useState(false);
@@ -164,7 +178,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
const combinedAssets = useMemo(() => {
const list: any[] = [];
const validVariantIds = new Set(variants.map(v => v.id));
const validVariantIds = new Set(configuredVariants.map(v => v.id));
assignedAssets.forEach(a => {
const aProdId = a.product_id || (a as any).productId;
@@ -181,7 +195,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
assignedVariantAssets.forEach(va => {
const vId = va.variantId || va.variant_id || va.variant?.id;
if (vId && (validVariantIds.size === 0 || validVariantIds.has(vId))) {
const matchedVariant = variants.find(v => v.id === vId);
const matchedVariant = configuredVariants.find(v => v.id === vId);
const vName = va.variantName || va.variant?.name || matchedVariant?.name || 'Variant';
const vSku = va.variantSku || va.variant?.sku || matchedVariant?.sku || '';
list.push({
@@ -195,7 +209,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
});
return list;
}, [assignedAssets, assignedVariantAssets, variants, productId]);
}, [assignedAssets, assignedVariantAssets, configuredVariants, productId]);
const [scopeFilter, setScopeFilter] = useState<string>('all');
@@ -234,7 +248,11 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
const sorted = [...data].sort((a, b) => (a.display_order || 0) - (b.display_order || 0));
setAssignedAssets(sorted);
const rawVars = Array.isArray(productVariants) ? productVariants : (productVariants as any)?.data || [];
setVariants(Array.isArray(rawVars) ? rawVars : []);
const productIsolatedVars = (Array.isArray(rawVars) ? rawVars : []).filter((v: any) => {
const pId = v.parentProductId || v.product_id || v.productId;
return !pId || pId === productId;
});
setVariants(productIsolatedVars);
const rawVarAssets = Array.isArray(allVariantAssets) ? allVariantAssets : (allVariantAssets as any)?.data || [];
setAssignedVariantAssets(Array.isArray(rawVarAssets) ? rawVarAssets : []);
} catch (err: any) {
@@ -635,7 +653,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
</div>
{/* Variant Target Selector */}
{variants.length > 0 && (
{configuredVariants.length > 0 && (
<div className="flex-1 min-w-[240px]">
<label className="text-xs font-bold text-foreground block mb-2 flex items-center justify-between">
<span>Variant Target Scope</span>
@@ -664,7 +682,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
className="w-full text-xs font-semibold border-primary/20 bg-primary/5/10 text-primary-dark"
>
<option value="global">Global (Product level)</option>
{variants.map((v) => (
{configuredVariants.map((v) => (
<option key={v.id} value={v.id}>
Variant: {v.name.split(' - ')[1] || v.name} ({v.sku || 'No SKU'})
</option>
@@ -684,7 +702,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
<div className="flex gap-2">
<button
type="button"
onClick={() => setSelectedVariantIds(variants.map(v => v.id))}
onClick={() => setSelectedVariantIds(configuredVariants.map(v => v.id))}
className="px-2 py-1 bg-surface border border-border hover:bg-background rounded text-[10px] font-bold text-muted-foreground cursor-pointer"
>
Select All
@@ -709,7 +727,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
<span className="text-[10px] font-bold text-foreground min-w-[60px]">{option.name}:</span>
<div className="flex gap-1.5 flex-wrap">
{option.values.map(val => {
const matchingIds = variants
const matchingIds = configuredVariants
.filter(v => {
if (v.attributes && String(v.attributes[option.code]) === val) return true;
if (Array.isArray(v.values) && v.values.some((av: any) => av.axis?.code === option.code && String(av.value_text) === val)) return true;
@@ -749,7 +767,7 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
{/* Variants Checkbox Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2 pt-2 border-t border-border max-h-[200px] overflow-y-auto">
{variants.map(v => {
{configuredVariants.map(v => {
const isChecked = selectedVariantIds.includes(v.id);
return (
<label key={v.id} className="flex items-center gap-2 p-1.5 hover:bg-surface-muted rounded-md cursor-pointer transition-colors text-[11px] font-medium text-foreground">
@@ -958,8 +976,8 @@ export const ProductAssetsTab: React.FC<ProductAssetsTabProps> = ({
>
Global ({assignedAssets.length})
</button>
{variants.map(v => {
const count = assignedVariantAssets.filter(va => va.variantId === v.id).length;
{configuredVariants.map(v => {
const count = assignedVariantAssets.filter(va => (va.variantId || va.variant_id) === v.id).length;
if (count === 0) return null;
const isSelected = scopeFilter === v.id;
return (