grocery-app/frontend/src/components/common/UploadToaster.jsx
Nico 77ae5be445
All checks were successful
Build & Deploy Costco Grocery List / build (push) Successful in 1m10s
Build & Deploy Costco Grocery List / verify-images (push) Successful in 3s
Build & Deploy Costco Grocery List / deploy (push) Successful in 11s
Build & Deploy Costco Grocery List / notify (push) Successful in 1s
refactor
2026-02-22 01:27:03 -08:00

93 lines
3.0 KiB
JavaScript

import useUploadQueue from "../../hooks/useUploadQueue";
import useActionToast from "../../hooks/useActionToast";
import "../../styles/components/UploadToaster.css";
function getStatusLabel(upload, isOnline) {
if (upload.status === "uploading") {
return `Uploading... ${upload.progress || 0}%`;
}
if (upload.status === "success") {
return "Upload complete";
}
if (upload.status === "queued") {
return isOnline ? "Queued for upload..." : "Waiting for network...";
}
return upload.lastError || "Upload failed. Retry or discard.";
}
export default function UploadToaster() {
const { uploads, isOnline, retryUpload, discardUpload } = useUploadQueue();
const { toasts, dismiss } = useActionToast();
if (!uploads.length && !toasts.length) {
return null;
}
const sortedToasts = [...toasts].sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
const sortedUploads = [...uploads].sort(
(a, b) => (b.updatedAt || b.createdAt || 0) - (a.updatedAt || a.createdAt || 0)
);
return (
<div className="upload-toaster" aria-live="polite" aria-atomic="false">
{sortedToasts.map((toast) => (
<div
key={toast.id}
className={`upload-toast action-toast action-toast-${toast.variant}`}
role={toast.variant === "error" ? "alert" : "status"}
>
<div className="action-toast-header">
<div className="upload-toast-title">{toast.title}</div>
<button
type="button"
onClick={() => dismiss(toast.id)}
className="action-toast-close"
aria-label="Dismiss notification"
>
x
</button>
</div>
{toast.message ? <div className="upload-toast-status">{toast.message}</div> : null}
</div>
))}
{sortedUploads.map((upload) => (
<div
key={upload.id}
className={`upload-toast upload-toast-${upload.status}`}
role="status"
>
<div className="upload-toast-title">{upload.itemName}</div>
<div className="upload-toast-status">{getStatusLabel(upload, isOnline)}</div>
<div className="upload-toast-progress">
<div
className="upload-toast-progress-fill"
style={{ width: `${upload.status === "success" ? 100 : upload.progress || 0}%` }}
/>
</div>
{upload.status === "failed" && (
<div className="upload-toast-actions">
<button type="button" onClick={() => retryUpload(upload.id)}>
Retry
</button>
<button type="button" onClick={() => discardUpload(upload.id)}>
Discard
</button>
</div>
)}
{upload.status === "queued" && (
<div className="upload-toast-actions">
<button type="button" onClick={() => discardUpload(upload.id)}>
Discard
</button>
</div>
)}
</div>
))}
</div>
);
}