Files
brandly/src/components/brand/BrandTabMedia.tsx
T

186 lines
6.5 KiB
TypeScript

import React, { useState } from 'react';
import { Film } from 'lucide-react';
import { DesignMD, CompanyProfile, BrandAsset } from '../../types';
import { UnifiedMediaLibrary, UnifiedMediaItem } from '../content-grid/UnifiedMediaLibrary';
type AssetFilter = 'all' | 'image' | 'video' | 'audio';
type SourceFilter = 'all' | 'uploaded' | 'generated';
interface BrandTabMediaProps {
company: CompanyProfile;
designMD: DesignMD;
handleDesignChange: (key: keyof DesignMD, value: any) => void;
onEditAsset?: (type: keyof DesignMD, url: string) => void;
onSelectAsset?: (asset: BrandAsset) => void;
selectedAssetId?: string;
}
export const BrandTabMedia: React.FC<BrandTabMediaProps> = ({ company, designMD, handleDesignChange, onSelectAsset, selectedAssetId }) => {
const assets = designMD.brandAssets || [];
const [uploadingBrandAsset, setUploadingBrandAsset] = useState(false);
const [uploadingGenerated, setUploadingGenerated] = useState(false);
const [filterType, setFilterType] = useState<AssetFilter>('all');
const [filterSource, setFilterSource] = useState<SourceFilter>('all');
const [refreshTrigger, setRefreshTrigger] = useState(0);
const handleBrandAssetFiles = async (files: File[]) => {
if (files.length === 0) return;
setUploadingBrandAsset(true);
try {
let currentWorkspacePath = '';
if (window.electronAPI) {
currentWorkspacePath = await window.electronAPI.fs.getWorkspacePath();
}
const newAssets = [...assets];
for (const file of files) {
const baseName = file.name.split('.')[0].replace(/[^a-zA-Z0-9-]/g, '-').toLowerCase();
let uniqueId = baseName;
let counter = 1;
while (newAssets.some(a => a.id === uniqueId)) {
uniqueId = `${baseName}-${counter}`;
counter++;
}
const formData = new FormData();
formData.append('file', file);
let type: 'image' | 'video' | 'audio' = 'image';
let subfolder = 'files/images';
if (file.type.startsWith('video/')) {
type = 'video';
subfolder = 'files/videos';
} else if (file.type.startsWith('audio/')) {
type = 'audio';
subfolder = 'files/audios';
}
if (currentWorkspacePath && company.id) {
formData.append('brandId', company.id);
formData.append('workspacePath', currentWorkspacePath);
formData.append('subfolder', subfolder);
}
const endpoint = (currentWorkspacePath && company.id) ? '/api/upload/brand' : '/api/upload';
const res = await fetch(endpoint, { method: 'POST', body: formData });
if (!res.ok) throw new Error('Upload failed');
const data = await res.json();
if (data.url) {
newAssets.push({
id: uniqueId,
type,
url: data.url,
path: data.path
});
}
}
handleDesignChange('brandAssets', newAssets);
} catch (err) {
console.error('Asset upload failed:', err);
alert('Ocurrió un error al subir los archivos.');
} finally {
setUploadingBrandAsset(false);
}
};
const handleDeleteAsset = (id: string) => {
if (confirm(`¿Estás seguro de que deseas eliminar el asset "${id}"?`)) {
handleDesignChange('brandAssets', assets.filter(a => a.id !== id));
}
};
const handleRenameAsset = (oldId: string, newId: string) => {
if (newId !== oldId && assets.some(a => a.id === newId)) {
alert('Este identificador ya existe.');
return;
}
const newAssets = assets.map(a => {
if (a.id === oldId) return { ...a, id: newId };
return a;
});
handleDesignChange('brandAssets', newAssets);
};
const onSelectUnified = (item: UnifiedMediaItem) => {
if (!onSelectAsset) return;
// Map UnifiedMediaItem back to BrandAsset if it's uploaded
if (item.source === 'uploaded') {
const asset = assets.find(a => a.id === item.id);
if (asset) onSelectAsset(asset);
} else {
// It's generated. We can construct a mock BrandAsset to show in preview
onSelectAsset({
id: item.name || item.id,
type: item.type,
path: item.path,
url: item.url,
date: item.date
});
}
};
return (
<div className="flex flex-col h-full space-y-6">
{/* Encabezado */}
<div className="flex items-center justify-between shrink-0">
<div>
<h3 className="text-sm font-bold text-white flex items-center gap-2 mb-1">
<Film size={16} className="text-violet-400" />
Librería Unificada
</h3>
<p className="text-xs text-neutral-500 leading-relaxed max-w-xl">
Gestiona tus archivos base de marca y tus renders generados en un solo lugar.
</p>
</div>
<div className="flex gap-2">
<select
value={filterSource}
onChange={(e) => setFilterSource(e.target.value as SourceFilter)}
className="bg-neutral-900 border border-neutral-800 text-neutral-300 text-xs rounded-md px-2 py-1 outline-none"
>
<option value="all">Todos los orígenes</option>
<option value="uploaded">Assets Base (Subidos)</option>
<option value="generated">Renders Generados</option>
</select>
<select
value={filterType}
onChange={(e) => setFilterType(e.target.value as AssetFilter)}
className="bg-neutral-900 border border-neutral-800 text-neutral-300 text-xs rounded-md px-2 py-1 outline-none"
>
<option value="all">Todos los tipos</option>
<option value="image">Imágenes</option>
<option value="video">Videos</option>
<option value="audio">Audio</option>
</select>
</div>
</div>
{/* Unified Media Library as Global Drop Zone */}
<div className="flex-1 overflow-hidden">
<UnifiedMediaLibrary
brandId={company.id}
brandAssets={assets}
refreshTrigger={refreshTrigger}
onDeleteAsset={handleDeleteAsset}
onRenameAsset={handleRenameAsset}
onSelect={onSelectUnified}
selectedPath={selectedAssetId}
filterType={filterType}
filterSource={filterSource}
draggable={false} // In this view, dragging is not needed for the timeline
onDropFiles={handleBrandAssetFiles}
isUploading={uploadingBrandAsset}
/>
</div>
</div>
);
};