diff --git a/Channel-Frontend/src/features/assets/components/AssetCard.tsx b/Channel-Frontend/src/features/assets/components/AssetCard.tsx index fa29a4d..a8db2b1 100644 --- a/Channel-Frontend/src/features/assets/components/AssetCard.tsx +++ b/Channel-Frontend/src/features/assets/components/AssetCard.tsx @@ -13,7 +13,8 @@ import { Download, Clock, AlertCircle, - Globe + Globe, + Sparkles } from 'lucide-react'; import type { Asset } from '../../../types/assets'; import type { User } from '../../../types/auth'; @@ -47,6 +48,7 @@ interface AssetCardProps { onToggleSelect?: (assetId: string) => void; isExpanded?: boolean; onToggleExpand?: () => void; + isRecommended?: boolean; } export const AssetCard: React.FC = ({ @@ -64,7 +66,8 @@ export const AssetCard: React.FC = ({ isSelected = false, onToggleSelect, isExpanded = false, - onToggleExpand + onToggleExpand, + isRecommended = false }) => { const isMenuOpen = activeMenuId === asset.id; const canDirectDownload = user?.role === 'ADMIN' || asset.isDownloadable || asset.downloadRequests?.[0]?.status === 'APPROVED'; @@ -169,6 +172,12 @@ export const AssetCard: React.FC = ({ className="w-3.5 h-3.5 rounded border-ink-300 bg-ink-0 text-ink-900 focus:ring-ink-900/10 cursor-pointer shadow-sm" /> )} + {isRecommended && ( +
+ + Recommended +
+ )}
diff --git a/Channel-Frontend/src/pages/AssetsPage.tsx b/Channel-Frontend/src/pages/AssetsPage.tsx index 57050f6..085450c 100644 --- a/Channel-Frontend/src/pages/AssetsPage.tsx +++ b/Channel-Frontend/src/pages/AssetsPage.tsx @@ -224,13 +224,19 @@ export const AssetsPage = () => { } }; + const assignedGroups = user?.partnerGroup && user.role === "PARTNER_USER" + ? user.partnerGroup.split(',').map(s => s.trim()) + : []; + const CATEGORIES = [ "ALL", + ...assignedGroups, ...Array.from( new Set( assets .map((asset) => asset.categoryId || "General") .filter((catId): catId is string => !!catId) + .filter(catId => !assignedGroups.some(g => g.toLowerCase() === catId.toLowerCase())) ) ).sort() ]; @@ -248,10 +254,13 @@ export const AssetsPage = () => { asset.type.toLowerCase().includes(query) || asset.tags.some((tag) => tag.toLowerCase().includes(query)); + const groupForFilter = groups.find(g => g.name.toLowerCase() === selectedCategory.toLowerCase()); const matchesCategory = selectedCategory === "ALL" || - asset.categoryId === selectedCategory || - (!asset.categoryId && selectedCategory === "General"); + (groupForFilter + ? groupForFilter.assets.some(a => a.id === asset.id) + : (asset.categoryId === selectedCategory || (!asset.categoryId && selectedCategory === "General")) + ); return matchesSearch && matchesCategory; }); @@ -296,19 +305,26 @@ export const AssetsPage = () => {
- {CATEGORIES.map((cat) => ( - - ))} + {CATEGORIES.map((cat) => { + const isGroup = assignedGroups.some(g => g.toLowerCase() === cat.toLowerCase()); + return ( + + ); + })}
@@ -403,9 +419,16 @@ export const AssetsPage = () => {
-
- - Curated for {user?.partnerGroup} +
+
+ + Curated for You +
+ {(user?.partnerGroup ? user.partnerGroup.split(',').map(s => s.trim()) : []).map(g => ( + + {g} + + ))}

Recommended for You

@@ -439,6 +462,7 @@ export const AssetsPage = () => { onToggleSelect={handleToggleSelectAsset} isExpanded={expandedAssetId === asset.id} onToggleExpand={() => setExpandedAssetId(expandedAssetId === asset.id ? null : asset.id)} + isRecommended={true} /> ))} @@ -485,6 +509,7 @@ export const AssetsPage = () => { onToggleSelect={handleToggleSelectAsset} isExpanded={expandedAssetId === asset.id} onToggleExpand={() => setExpandedAssetId(expandedAssetId === asset.id ? null : asset.id)} + isRecommended={recommendedAssets.some(r => r.id === asset.id)} /> ))} diff --git a/Channel-Frontend/src/pages/admin/DirectoryPage.tsx b/Channel-Frontend/src/pages/admin/DirectoryPage.tsx index fb67448..08406b9 100644 --- a/Channel-Frontend/src/pages/admin/DirectoryPage.tsx +++ b/Channel-Frontend/src/pages/admin/DirectoryPage.tsx @@ -136,6 +136,16 @@ export const DirectoryPage: React.FC = () => { const [mfaRequired, setMfaRequired] = useState(true); const [selectedAssetIds, setSelectedAssetIds] = useState([]); const [inviteAssetSearch, setInviteAssetSearch] = useState(""); + const [shareAllAssets, setShareAllAssets] = useState(true); + + // Helper for sync checkbox + const handleSetSelectedAssetIds = (value: React.SetStateAction) => { + setSelectedAssetIds(prev => { + const next = typeof value === 'function' ? (value as Function)(prev) : value; + setShareAllAssets(next.length === allAssets.length && allAssets.length > 0); + return next; + }); + }; // Editing partner states const [editingPartner, setEditingPartner] = useState(null); @@ -146,6 +156,16 @@ export const DirectoryPage: React.FC = () => { const [editAssetIds, setEditAssetIds] = useState([]); const [editAssetSearch, setEditAssetSearch] = useState(""); const [isEditOpen, setIsEditOpen] = useState(false); + const [editShareAll, setEditShareAll] = useState(false); + + // Helper for edit sync checkbox + const handleSetEditAssetIds = (value: React.SetStateAction) => { + setEditAssetIds(prev => { + const next = typeof value === 'function' ? (value as Function)(prev) : value; + setEditShareAll(next.length === allAssets.length && allAssets.length > 0); + return next; + }); + }; const [assetGroups, setAssetGroups] = useState([]); const [selectedPartnerForAssets, setSelectedPartnerForAssets] = useState(null); const [isPartnerAssetsOpen, setIsPartnerAssetsOpen] = useState(false); @@ -601,6 +621,7 @@ export const DirectoryPage: React.FC = () => { setAssignedMsaId(""); setMfaRequired(true); setSelectedAssetIds(allAssets.map(a => a.id)); + setShareAllAssets(true); setInviteAssetSearch(""); setIsInviteOpen(true); }} @@ -894,7 +915,9 @@ export const DirectoryPage: React.FC = () => { setEditNdaId(partner.assignedNdaId === null ? "NONE" : partner.assignedNdaId || ""); setEditMsaId(partner.assignedMsaId === null ? "NONE" : partner.assignedMsaId || ""); setEditMfaEnabled(partner.mfaEnabled); - setEditAssetIds(partner.sharedAssets?.map((sa: any) => sa.assetId) || []); + const sharedIds = partner.sharedAssets?.map((sa: any) => sa.assetId) || []; + setEditAssetIds(sharedIds); + setEditShareAll(sharedIds.length === allAssets.length && allAssets.length > 0); setIsEditOpen(true); }} className="p-1.5 text-ink-600 hover:text-ink-950 hover:bg-ink-100 rounded-lg transition-colors cursor-pointer" @@ -1265,7 +1288,27 @@ export const DirectoryPage: React.FC = () => { )}

- {renderAssetSelector(selectedAssetIds, setSelectedAssetIds, inviteAssetSearch, setInviteAssetSearch, handleToggleInviteGroup)} +
+ { + const checked = e.target.checked; + setShareAllAssets(checked); + if (checked) { + setSelectedAssetIds(allAssets.map(a => a.id)); + } else { + setSelectedAssetIds([]); + } + }} + className="rounded border-ink-300 text-ink-900 focus:ring-ink-950 w-4 h-4 cursor-pointer" + /> + +
+ {renderAssetSelector(selectedAssetIds, handleSetSelectedAssetIds, inviteAssetSearch, setInviteAssetSearch, handleToggleInviteGroup)}
{inviteResult?.error && ( @@ -1429,7 +1472,27 @@ export const DirectoryPage: React.FC = () => { )}
- {renderAssetSelector(editAssetIds, setEditAssetIds, editAssetSearch, setEditAssetSearch, handleToggleEditGroup)} +
+ { + const checked = e.target.checked; + setEditShareAll(checked); + if (checked) { + setEditAssetIds(allAssets.map(a => a.id)); + } else { + setEditAssetIds([]); + } + }} + className="rounded border-ink-300 text-ink-900 focus:ring-ink-950 w-4 h-4 cursor-pointer" + /> + +
+ {renderAssetSelector(editAssetIds, handleSetEditAssetIds, editAssetSearch, setEditAssetSearch, handleToggleEditGroup)}