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 (
|
|
|
|
|
<div className="fixed inset-0 z-[100] 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-01-13 18:47:57 +00:00
|
|
|
<h3 className="font-display font-bold text-xl text-white flex items-center gap-2">
|
aesthetic-improvements: reduce decorative cyan across multiple component categories (80/20 rule, batch 11)
- Social: FeedView, ConnectionsView, GroupsView, ExploreView, GroupDetailView loading spinners and decorative text, CreatePostModal decorative select text and hashtag links, PostCard decorative tag links and waveform bars and view comments link, CreateGroupModal decorative icon (9 instances)
- Settings: DataExportModal decorative icon, LoginHistory decorative IP text, AppearanceSettingsView decorative icon and selected theme checkmark, BackupsView decorative icon, CloudIntegrationView decorative icon, AccessibilitySettingsView decorative icon, SecuritySettings decorative icon, PasskeyModal decorative icon and loading spinner (8 instances)
- Studio: ProjectsManager loading spinner and progress percentage text, CloudFileBrowser decorative music icons, AIToolsView decorative music icon, ConnectivityView decorative icon, CreateProjectModal decorative icon, CloudSettingsView decorative icon (6 instances)
- Admin: AdminDashboardView loading spinner and decorative chart bars and icon, AdminSettingsView decorative icon, AdminModerationView loading spinner, AdminUsersView loading spinner (5 instances)
- Inventory: InventoryView loading spinner, EquipmentCard decorative price icon, EquipmentDetailView loading spinner and decorative icons and price text, AddEquipmentView decorative icon (5 instances)
- Seller: CreateProductView decorative icon, SellerDashboardView loading spinner and decorative icon and sales text (3 instances)
- Live: LiveStreamDetailView decorative streamer name text (1 instance)
- Developer: DeveloperDashboardView loading spinner, WebhooksView decorative icon (2 instances)
- Upload: BulkUploadModal decorative icon, FilePreviewCard decorative audio file icon, MetadataForm decorative button text, CoverArtUploadModal decorative icon, LyricsEditorModal decorative icon (5 instances)
- Notifications: NotificationItem decorative follow icon and mark as read button, NotificationBell decorative mark all read link (3 instances)
- Total: ~46 files, ~46 instances replaced
- Preserved: Active/selected states (CloudFileBrowser selected files checkmarks, CreatePostModal post type active state, GroupCard/GroupDetailView public/private badges - semantic indicators, DataExportModal checkbox accents - focus/interaction, AppearanceSettingsView selected theme - active state, PasskeyModal checkbox accent - focus/interaction, LyricsEditorModal checkbox accent - focus/interaction, FileUploadZone drag active state - active state, EquipmentDetailView support link - functional link, FlashSaleModal link - functional link, EquipmentDetailView image indicator dots - active state), primary actions, design system variants
- Action 11.3.1.3 in progress (eleventh batch: social, settings, studio, admin, inventory, seller, live, developer, upload, notifications components)
2026-01-16 10:26:33 +00:00
|
|
|
<Layers className="w-5 h-5 text-kodo-steel" /> 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 */}
|
|
|
|
|
<div className="px-6 py-2 bg-kodo-slate/30 border-b border-kodo-steel">
|
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
|
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
|
|
|
className="h-full bg-kodo-cyan transition-all duration-[var(--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
|
|
|
};
|