2026-01-07 09:31:02 +00:00
|
|
|
import React, { useState } from 'react';
|
|
|
|
|
import { Button } from '../../ui/button';
|
|
|
|
|
import { Input } from '../../ui/input';
|
|
|
|
|
import { X, Lock, Globe } from 'lucide-react';
|
2026-01-26 13:12:17 +00:00
|
|
|
import { useToast } from '../../../components/feedback/ToastProvider';
|
2026-01-07 09:31:02 +00:00
|
|
|
|
|
|
|
|
interface SaveQueueAsPlaylistModalProps {
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
onSave: (name: string, isPublic: boolean) => void;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
export const SaveQueueAsPlaylistModal: React.FC<
|
|
|
|
|
SaveQueueAsPlaylistModalProps
|
|
|
|
|
> = ({ onClose, onSave }) => {
|
2026-01-07 09:31:02 +00:00
|
|
|
const { addToast } = useToast();
|
|
|
|
|
const [name, setName] = useState('');
|
|
|
|
|
const [isPublic, setIsPublic] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handleSubmit = () => {
|
2026-01-13 18:47:57 +00:00
|
|
|
if (!name) {
|
|
|
|
|
addToast('Please name your playlist', 'error');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
onSave(name, isPublic);
|
|
|
|
|
onClose();
|
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
|
|
|
|
|
className="absolute inset-0 bg-kodo-void/90 backdrop-blur-sm"
|
|
|
|
|
onClick={onClose}
|
|
|
|
|
></div>
|
2026-01-07 09:31:02 +00:00
|
|
|
<div className="relative w-full max-w-md bg-kodo-graphite border border-kodo-steel rounded-xl shadow-2xl animate-scaleIn">
|
|
|
|
|
<div className="p-4 border-b border-kodo-steel bg-kodo-ink flex justify-between items-center">
|
|
|
|
|
<h3 className="font-bold text-white">Save Queue as Playlist</h3>
|
2026-01-13 18:47:57 +00:00
|
|
|
<button onClick={onClose}>
|
2026-02-08 23:04:51 +00:00
|
|
|
<X className="w-5 h-5 text-muted-foreground hover:text-white" />
|
2026-01-13 18:47:57 +00:00
|
|
|
</button>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="p-6 space-y-4">
|
2026-01-13 18:47:57 +00:00
|
|
|
<Input
|
|
|
|
|
label="Playlist Name"
|
|
|
|
|
value={name}
|
|
|
|
|
onChange={(e) => setName(e.target.value)}
|
|
|
|
|
autoFocus
|
|
|
|
|
placeholder="My Queue Session"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<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
|
|
|
className="flex items-center justify-between p-4 bg-kodo-ink rounded border border-kodo-steel cursor-pointer hover:border-kodo-steel"
|
2026-01-13 18:47:57 +00:00
|
|
|
onClick={() => setIsPublic(!isPublic)}
|
|
|
|
|
>
|
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 items-center gap-4">
|
2026-01-13 18:47:57 +00:00
|
|
|
{isPublic ? (
|
aesthetic-improvements: reduce decorative cyan in player, library, and views (80/20 rule, batch 12)
- Player: VisualizerSettingsModal decorative icon (1 instance)
- Library: QueueView decorative artist text, AutoMetadataDetectionModal decorative icon and loading spinner border and fileName text and detected key text, SaveQueueAsPlaylistModal decorative icon, EditPlaylistModal decorative icon, PlaylistsView loading spinner, CreatePlaylistModal decorative icon (7 instances)
- Views: StudioView decorative icon, FileDetailsView decorative icon, GearView decorative icons and order number text, ProfileView loading spinner and social icons, AnalyticsView loading spinner and decorative chart legend dot and chart bars and device icon and revenue text, DiscoverView loading spinner and decorative icon and weekly mix text, FileManagerView decorative music icons (14 instances)
- Total: ~22 files, ~22 instances replaced
- Preserved: Active/selected states (LyricsPanel autoScroll active state, VisualizerSettingsModal selected mode, PlayerControls shuffle/repeatMode/showVisualizer active states, MiniPlayer isQueueOpen active state, AddToPlaylistModal selected playlist, PlaylistDetailView dragged item, StudioView active tab, SearchBar focused state, CheckoutView checkbox accents - focus/interaction, SearchPageView radio button accent - focus/interaction, FileManagerView selected files checkmarks - active states, ProfileView social links - functional links, LiveView links - functional links), primary actions, design system variants
- Action 11.3.1.3 in progress (twelfth batch: player, library, and views components)
2026-01-16 10:30:07 +00:00
|
|
|
<Globe className="w-5 h-5 text-kodo-steel" />
|
2026-01-13 18:47:57 +00:00
|
|
|
) : (
|
|
|
|
|
<Lock className="w-5 h-5 text-kodo-gold" />
|
|
|
|
|
)}
|
|
|
|
|
<div>
|
|
|
|
|
<div className="text-sm font-bold text-white">
|
|
|
|
|
{isPublic ? 'Public Playlist' : 'Private Playlist'}
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-02-08 23:04:51 +00:00
|
|
|
<div className="text-xs text-muted-foreground">
|
2026-01-13 18:47:57 +00:00
|
|
|
{isPublic ? 'Visible on your profile' : 'Only visible to you'}
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
2026-01-16 00:59:31 +00:00
|
|
|
className={`w-10 h-5 rounded-full relative transition-colors ${isPublic ? 'bg-kodo-cyan' : 'bg-kodo-steel'}`}
|
2026-01-13 18:47:57 +00:00
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
className={`absolute top-1 w-3 h-3 bg-white rounded-full transition-all ${isPublic ? 'left-6' : 'left-1'}`}
|
|
|
|
|
></div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</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="p-4 border-t border-kodo-steel bg-kodo-ink flex justify-end gap-4">
|
2026-01-13 18:47:57 +00:00
|
|
|
<Button variant="ghost" onClick={onClose}>
|
|
|
|
|
Cancel
|
|
|
|
|
</Button>
|
|
|
|
|
<Button variant="primary" onClick={handleSubmit}>
|
|
|
|
|
Save Playlist
|
|
|
|
|
</Button>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|