2026-01-07 09:31:02 +00:00
|
|
|
import React from 'react';
|
|
|
|
|
import { X, Play, Upload, Layers } from 'lucide-react';
|
|
|
|
|
import { Button } from '../ui/button';
|
|
|
|
|
import { FilePreviewCard, UploadFile } from './FilePreviewCard';
|
|
|
|
|
|
|
|
|
|
interface BulkUploadModalProps {
|
|
|
|
|
files: UploadFile[];
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
onStartUpload: () => void;
|
|
|
|
|
onCancelFile: (id: string) => void;
|
|
|
|
|
onPauseFile: (id: string) => void;
|
|
|
|
|
onResumeFile: (id: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
export const BulkUploadModal: React.FC<BulkUploadModalProps> = ({
|
|
|
|
|
files,
|
|
|
|
|
onClose,
|
2026-01-07 09:31:02 +00:00
|
|
|
onStartUpload,
|
|
|
|
|
onCancelFile,
|
|
|
|
|
onPauseFile,
|
2026-01-13 18:47:57 +00:00
|
|
|
onResumeFile,
|
2026-01-07 09:31:02 +00:00
|
|
|
}) => {
|
2026-01-13 18:47:57 +00:00
|
|
|
const totalProgress =
|
|
|
|
|
files.reduce((acc, f) => acc + f.progress, 0) / (files.length || 1);
|
|
|
|
|
const uploadingCount = files.filter((f) => f.status === 'uploading').length;
|
|
|
|
|
const completedCount = files.filter((f) => f.status === 'completed').length;
|
2026-01-07 09:31:02 +00:00
|
|
|
|
|
|
|
|
return (
|
fix: UI remediation Phase 1 (S0-S5) + Phase 2 Sprint 6 shadow system
Phase 1:
- S0: Fix open redirect (safeNavigate), delete AuthContext/legacy auth, encrypt API keys, gitignore .env files
- S1: Split client.ts god object into 5 modules, unify toast system, delete unused Sidebar
- S2: Add glass button variant, migrate 32 z-index to SUMI tokens, fix card dark mode
- S3: Skip nav link, aria-hidden on icons, focus-visible ring fixes, alt attrs, aria-live regions
- S4: React.memo on list items, fix key={index}, loading=lazy on images
- S5: Branded loading screen, page transitions respect reduced-motion, LikeButton micro-interaction, i18n sidebar/header
Phase 2 Sprint 6:
- Wire Tailwind shadow utilities to SUMI tokens in @theme block (fixes 50+ files)
- Define shadow-card/shadow-card-hover tokens
- Remove dark:shadow-none workarounds from card.tsx (SUMI handles per-theme shadows)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 09:13:44 +00:00
|
|
|
<div className="fixed inset-0 z-[var(--sumi-z-modal)] flex items-center justify-center p-4">
|
2026-01-13 18:47:57 +00:00
|
|
|
<div
|
2026-02-07 15:07:09 +00:00
|
|
|
className="absolute inset-0 bg-background/90 backdrop-blur-sm"
|
2026-01-13 18:47:57 +00:00
|
|
|
onClick={onClose}
|
|
|
|
|
></div>
|
2026-02-08 21:47:41 +00:00
|
|
|
<div className="relative w-full max-w-3xl bg-card border border-border rounded-2xl shadow-2xl overflow-hidden flex flex-col max-h-layout-modal-sm animate-scaleIn">
|
2026-01-07 09:31:02 +00:00
|
|
|
{/* Header */}
|
2026-02-07 15:07:09 +00:00
|
|
|
<div className="p-8 border-b border-border bg-muted flex justify-between items-center">
|
2026-01-07 09:31:02 +00:00
|
|
|
<div>
|
2026-02-12 01:09:29 +00:00
|
|
|
<h3 className="font-heading font-bold text-xl text-foreground flex items-center gap-2">
|
refactor: Phase 3a — Global color class migration to SUMI semantics
- Replace all kodo-* color classes across ~100 TSX files:
kodo-void → background, kodo-ink → card, kodo-graphite → muted,
kodo-steel → muted-foreground, kodo-cyan → primary, kodo-magenta → destructive,
kodo-lime → success, kodo-red → destructive, kodo-gold → warning
- Replace cyan-500, magenta-500, lime-500 default Tailwind colors with
semantic equivalents (primary, destructive, success)
- Fix WaveformVisualizer hardcoded hex colors to SUMI values
- Delete global-effects.css (conflicting, redundant with index.css)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 00:51:49 +00:00
|
|
|
<Layers className="w-5 h-5 text-muted-foreground" /> Bulk Upload Manager
|
2026-01-13 18:47:57 +00:00
|
|
|
</h3>
|
2026-02-07 15:07:09 +00:00
|
|
|
<p className="text-xs text-muted-foreground mt-1">
|
2026-01-13 18:47:57 +00:00
|
|
|
{completedCount} / {files.length} files uploaded •{' '}
|
|
|
|
|
{files.length - completedCount} remaining
|
|
|
|
|
</p>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
consistency: replace custom buttons with Button component (partial)
- Replaced custom button implementations with Button component in 14 files
- Files updated: LiveStreamDetailView, DashboardPage, CommentItem, PostCard, SocialPage, SocialView, AdminUsersView, UserTableRow, ProjectsManager, CloudFileBrowser, FileManagerView, CreatorModal, ImageCropper, BulkUploadModal
- ~31 buttons replaced across high-priority files
- Used appropriate Button variants: ghost, outline, default, secondary, link
- Preserved visual appearance with className overrides where needed
- Action 9.2.1.2 in progress (partial completion)
2026-01-16 01:06:14 +00:00
|
|
|
<Button variant="ghost" size="icon" onClick={onClose}>
|
2026-01-13 18:47:57 +00:00
|
|
|
<X className="w-5 h-5" />
|
consistency: replace custom buttons with Button component (partial)
- Replaced custom button implementations with Button component in 14 files
- Files updated: LiveStreamDetailView, DashboardPage, CommentItem, PostCard, SocialPage, SocialView, AdminUsersView, UserTableRow, ProjectsManager, CloudFileBrowser, FileManagerView, CreatorModal, ImageCropper, BulkUploadModal
- ~31 buttons replaced across high-priority files
- Used appropriate Button variants: ghost, outline, default, secondary, link
- Preserved visual appearance with className overrides where needed
- Action 9.2.1.2 in progress (partial completion)
2026-01-16 01:06:14 +00:00
|
|
|
</Button>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Global Progress */}
|
refactor: Phase 3a — Global color class migration to SUMI semantics
- Replace all kodo-* color classes across ~100 TSX files:
kodo-void → background, kodo-ink → card, kodo-graphite → muted,
kodo-steel → muted-foreground, kodo-cyan → primary, kodo-magenta → destructive,
kodo-lime → success, kodo-red → destructive, kodo-gold → warning
- Replace cyan-500, magenta-500, lime-500 default Tailwind colors with
semantic equivalents (primary, destructive, success)
- Fix WaveformVisualizer hardcoded hex colors to SUMI values
- Delete global-effects.css (conflicting, redundant with index.css)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 00:51:49 +00:00
|
|
|
<div className="px-6 py-2 bg-muted/30 border-b border-border">
|
feat(web): UI premium Discord/Spotify-like — tokens, shadows, focus, layout
Plan UI premium 6–8 semaines (design system, shell, Storybook, a11y):
- Design system: DESIGN_TOKENS.md, APP_SHELL.md, FULL_LAYOUT_PAGE.md. Single source
for layout/shell (index.css), shadows (design-system.css), durations/easing.
- Tokens: shadow-cover-depth, shadow-gold-glow, shadow-fab-glow; layout max-height
(max-h-layout-drawer, max-h-layout-panel, max-h-layout-list). All duration-200/300/500
replaced by --duration-fast/normal/slow. Arbitrary shadows replaced by token classes.
- Shell & player: Sidebar, Header, GlobalPlayer, MiniPlayer, PlayerQueue, PlayerControls,
AudioPlayer use tokens; focus-visible on Sidebar, PlayerQueue, DropdownMenuTrigger/Item,
TabsTrigger. Typography: text-[10px]/[9px] → text-xs where applicable.
- ESLint: no-restricted-syntax (warn) for w-/h-/rounded-/shadow-/text-/spacing arbitrary.
- Scripts: report-arbitrary-values.mjs, capture/compare/generate visual; visual-complete.spec.ts.
- Stories full layout: Dashboard, Playlists, Library, Settings, Profile in DashboardLayout.stories.
- .cursorrules + README: DESIGN_TOKENS, APP_SHELL, visual commands, no arbitrary without justification.
- apps/web/.gitignore: e2e test artifacts (test-results-visual, playwright-report-visual).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 16:15:58 +00:00
|
|
|
<div className="flex justify-between text-xs font-bold text-muted-foreground uppercase mb-1">
|
2026-01-13 18:47:57 +00:00
|
|
|
<span>Overall Progress</span>
|
|
|
|
|
<span>{Math.round(totalProgress)}%</span>
|
|
|
|
|
</div>
|
2026-02-07 15:07:09 +00:00
|
|
|
<div className="h-1 bg-muted rounded-full overflow-hidden">
|
2026-01-13 18:47:57 +00:00
|
|
|
<div
|
2026-02-12 01:09:29 +00:00
|
|
|
className="h-full bg-primary transition-all duration-[var(--sumi-duration-normal)]"
|
2026-01-13 18:47:57 +00:00
|
|
|
style={{ width: `${totalProgress}%` }}
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* File List */}
|
2026-02-07 15:07:09 +00:00
|
|
|
<div className="flex-1 overflow-y-auto p-8 space-y-4 bg-muted/50">
|
2026-01-13 18:47:57 +00:00
|
|
|
{files.map((file) => (
|
|
|
|
|
<FilePreviewCard
|
|
|
|
|
key={file.id}
|
|
|
|
|
fileData={file}
|
|
|
|
|
onCancel={() => onCancelFile(file.id)}
|
|
|
|
|
onPause={() => onPauseFile(file.id)}
|
|
|
|
|
onResume={() => onResumeFile(file.id)}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Footer Actions */}
|
2026-02-07 15:07:09 +00:00
|
|
|
<div className="p-8 border-t border-border bg-muted flex justify-between items-center">
|
2026-01-13 18:47:57 +00:00
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
2026-02-07 15:07:09 +00:00
|
|
|
className="text-muted-foreground hover:text-foreground"
|
2026-01-13 18:47:57 +00:00
|
|
|
onClick={onClose}
|
|
|
|
|
>
|
|
|
|
|
Cancel All
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
aesthetic-improvements: align spacing to 8px grid (Action 11.2.1.3)
- Created automated script (scripts/align-8px-grid.py) to align all spacing to 8px grid
- Replaced non-8px-aligned spacing: gap-3/p-3/m-3 (12px) → gap-4/p-4/m-4 (16px), gap-5/p-5/m-5 (20px) → gap-6/p-6/m-6 (24px), gap-10/p-10/m-10 (40px) → gap-12/p-12/m-12 (48px), gap-20/p-20/m-20 (80px) → gap-24/p-24/m-24 (96px)
- Preserved: 4px values (gap-1, p-1, m-1) as they may be intentional fine-tuning, responsive breakpoints (sm:, md:, lg:), test files, documentation
- Modified files across all components to ensure consistent 8px grid alignment
- Action 11.2.1.3: Align all elements to 8px grid - COMPLETE
2026-01-16 10:50:46 +00:00
|
|
|
<div className="flex gap-4">
|
2026-01-13 18:47:57 +00:00
|
|
|
<Button
|
|
|
|
|
variant="secondary"
|
|
|
|
|
size="sm"
|
|
|
|
|
icon={<Upload className="w-4 h-4" />}
|
|
|
|
|
>
|
|
|
|
|
Apply Metadata to All
|
|
|
|
|
</Button>
|
|
|
|
|
{uploadingCount === 0 && completedCount < files.length && (
|
|
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
|
|
|
|
size="sm"
|
|
|
|
|
icon={<Play className="w-4 h-4" />}
|
|
|
|
|
onClick={onStartUpload}
|
|
|
|
|
>
|
|
|
|
|
Start Upload
|
2026-01-07 09:31:02 +00:00
|
|
|
</Button>
|
2026-01-13 18:47:57 +00:00
|
|
|
)}
|
|
|
|
|
{completedCount === files.length && (
|
|
|
|
|
<Button variant="primary" size="sm" onClick={onClose}>
|
|
|
|
|
Done
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-01-13 18:47:57 +00:00
|
|
|
};
|