diff --git a/services/web-ui/public/screens-asset.jsx b/services/web-ui/public/screens-asset.jsx index 12f35a3..f622c4d 100644 --- a/services/web-ui/public/screens-asset.jsx +++ b/services/web-ui/public/screens-asset.jsx @@ -243,7 +243,11 @@ function AssetDetail({ asset, onClose }) { setDownloading(true); window.ZAMPP_API.fetch('/assets/' + assetId + '/hires') .then(function(r) { - if (!r || !r.url) { window.alert('No hi-res source available for this asset.'); return; } + if (!r || !r.url) { + if (window.toast) window.toast.error('No hi-res source available for this asset.'); + else window.alert('No hi-res source available for this asset.'); + return; + } const a = document.createElement('a'); a.href = r.url; a.download = r.filename || (asset.name + '.' + (r.ext || 'mov')); @@ -253,7 +257,10 @@ function AssetDetail({ asset, onClose }) { a.click(); document.body.removeChild(a); }) - .catch(function(e) { window.alert('Download failed: ' + (e.message || 'unknown error')); }) + .catch(function(e) { + if (window.toast) window.toast.error('Download failed: ' + (e.message || 'unknown error')); + else window.alert('Download failed: ' + (e.message || 'unknown error')); + }) .finally(function() { setDownloading(false); }); }; @@ -279,7 +286,10 @@ function AssetDetail({ asset, onClose }) { }))) return; window.ZAMPP_API.fetch('/assets/' + assetId + '?hard=true', { method: 'DELETE' }) .then(function() { onClose && onClose(); }) - .catch(function(e) { window.alert('Delete failed: ' + e.message); }); + .catch(function(e) { + if (window.toast) window.toast.error('Delete failed: ' + e.message); + else window.alert('Delete failed: ' + e.message); + }); }; const retryProcessing = function() { @@ -287,9 +297,13 @@ function AssetDetail({ asset, onClose }) { setRetrying(true); window.ZAMPP_API.fetch('/assets/' + assetId + '/retry', { method: 'POST' }) .then(function() { - window.alert('Re-queued for processing. The proxy worker will pick it up shortly; refresh in a minute to see the player.'); + if (window.toast) window.toast.success('Re-queued for processing. The proxy worker will pick it up shortly; refresh in a minute to see the player.'); + else window.alert('Re-queued for processing. The proxy worker will pick it up shortly; refresh in a minute to see the player.'); + }) + .catch(function(e) { + if (window.toast) window.toast.error('Retry failed: ' + (e.message || 'unknown error')); + else window.alert('Retry failed: ' + (e.message || 'unknown error')); }) - .catch(function(e) { window.alert('Retry failed: ' + (e.message || 'unknown error')); }) .finally(function() { setRetrying(false); }); }; @@ -298,16 +312,26 @@ function AssetDetail({ asset, onClose }) { setReprocessing(type); window.ZAMPP_API.fetch('/assets/' + assetId + '/reprocess?type=' + type, { method: 'POST' }) .then(function() { - window.alert((type === 'proxy' ? 'Proxy' : 'Thumbnail') + ' job queued. Refresh in a moment to see the result.'); + if (window.toast) window.toast.success((type === 'proxy' ? 'Proxy' : 'Thumbnail') + ' job queued. Refresh in a moment to see the result.'); + else window.alert((type === 'proxy' ? 'Proxy' : 'Thumbnail') + ' job queued. Refresh in a moment to see the result.'); + }) + .catch(function(e) { + if (window.toast) window.toast.error('Reprocess failed: ' + (e.message || 'unknown error')); + else window.alert('Reprocess failed: ' + (e.message || 'unknown error')); }) - .catch(function(e) { window.alert('Reprocess failed: ' + (e.message || 'unknown error')); }) .finally(function() { setReprocessing(null); }); }; const regenFilmstrip = function() { window.ZAMPP_API.fetch('/assets/' + assetId + '/reprocess?type=filmstrip', { method: 'POST' }) - .then(function() { window.alert('Filmstrip job queued: it will appear automatically when ready.'); }) - .catch(function(e) { window.alert('Failed to queue filmstrip: ' + (e.message || 'unknown error')); }); + .then(function() { + if (window.toast) window.toast.success('Filmstrip job queued: it will appear automatically when ready.'); + else window.alert('Filmstrip job queued: it will appear automatically when ready.'); + }) + .catch(function(e) { + if (window.toast) window.toast.error('Failed to queue filmstrip: ' + (e.message || 'unknown error')); + else window.alert('Failed to queue filmstrip: ' + (e.message || 'unknown error')); + }); }; // Map a /assets/:id/comments row into the legacy shape the consumer @@ -352,7 +376,8 @@ function AssetDetail({ asset, onClose }) { setComments(function(c) { return [...c, _normalizeComment(row)]; }); }) .catch(function(e) { - window.alert('Could not post comment: ' + (e.message || 'unknown error')); + if (window.toast) window.toast.error('Could not post comment: ' + (e.message || 'unknown error')); + else window.alert('Could not post comment: ' + (e.message || 'unknown error')); setNewComment(text); }); }; @@ -374,7 +399,10 @@ function AssetDetail({ asset, onClose }) { .then(function() { setComments(function(prev) { return prev.filter(x => x.id !== c.id); }); }) - .catch(function(e) { window.alert('Delete failed: ' + e.message); }); + .catch(function(e) { + if (window.toast) window.toast.error('Delete failed: ' + e.message); + else window.alert('Delete failed: ' + e.message); + }); }; const visibleComments = comments.filter(function(c) { return showResolved || !c.resolved; }); diff --git a/services/web-ui/public/screens-library.jsx b/services/web-ui/public/screens-library.jsx index a747c69..0e35fbc 100644 --- a/services/web-ui/public/screens-library.jsx +++ b/services/web-ui/public/screens-library.jsx @@ -35,7 +35,11 @@ function Library({ navigate, onOpenAsset, openProject, onClearProject, onOpenPro const [expandedBins, setExpandedBins] = React.useState(() => new Set((window.ZAMPP_DATA?.BINS||[]).map(b=>b.id))); const createBin = () => { - if (!openProject) { window.alert('Open a project first (Projects → click a project), then create a bin inside it.'); return; } + if (!openProject) { + if (window.toast) window.toast.error('Open a project first (Projects → click a project), then create a bin inside it.'); + else window.alert('Open a project first (Projects → click a project), then create a bin inside it.'); + return; + } setCreatingChildOf(null); setNewBinName(''); setCreatingBin(true); }; const createSubBin = (parentId) => { @@ -55,8 +59,15 @@ function Library({ navigate, onOpenAsset, openProject, onClearProject, onOpenPro body: JSON.stringify({ project_id: openProject.id, name: name.trim(), parent_id: parentId || null }), }) .then(() => window.ZAMPP_API.fetch('/bins?project_id=' + openProject.id)) - .then(list => { const n = (list||[]).map(b=>({...b,count:b.asset_count||0,icon:b.type||'grid'})); setBins(n); if (parentId) setExpandedBins(prev => { const s=new Set(prev); s.add(parentId); return s; }); }) - .catch(e => window.alert('Could not create bin: ' + e.message)); + .then(list => { + const n = (list||[]).map(b=>({...b,count:b.asset_count||0,icon:b.type||'grid'})); + setBins(n); + if (parentId) setExpandedBins(prev => { const s=new Set(prev); s.add(parentId); return s; }); + }) + .catch(e => { + if (window.toast) window.toast.error('Could not create bin: ' + e.message); + else window.alert('Could not create bin: ' + e.message); + }); }; const [view, setView] = React.useState('grid'); const [filter, setFilter] = React.useState('all'); // 'all', 'ready', 'processing', 'live', 'error', 'recent' @@ -599,7 +610,8 @@ function AssetContextMenu({ asset, x, y, bins, onClose, onChanged, onOpen, onRen window.ZAMPP_API.fetch('/assets/' + asset.id + '/promote', { method: 'POST' }) .then(function() { if (onChanged) onChanged(); - window.alert('Promotion job queued. The file is being uploaded to S3 in the background.'); + if (window.toast) window.toast.success('Promotion job queued. The file is being uploaded to S3 in the background.'); + else window.alert('Promotion job queued. The file is being uploaded to S3 in the background.'); }) .catch(function(e) { alert('Promotion failed: ' + e.message); }); };