source: frontend/src/components/playlist/CreatePlaylistModal.tsx@ c8baad1

main
Last change on this file since c8baad1 was c8baad1, checked in by Dimitar Arsov <dimitararsov04@…>, 5 months ago

create playlists from sidebar

  • Property mode set to 100644
File size: 5.6 KB
RevLine 
[85512ff]1import { useState, useEffect } from "react";
[c8baad1]2import { toast } from "react-toastify";
3import axiosInstance from "../../api/axiosInstance";
[85512ff]4import type { CreatePlaylistModalProps } from "../../utils/types";
5
6const CreatePlaylistModal = ({
7 isOpen,
8 onClose,
[c8baad1]9 onSuccess,
[85512ff]10}: CreatePlaylistModalProps) => {
11 const [playlistName, setPlaylistName] = useState("");
12 const [isClosing, setIsClosing] = useState(false);
13 const [isOpening, setIsOpening] = useState(false);
[c8baad1]14 const [isSubmitting, setIsSubmitting] = useState(false);
15 const [error, setError] = useState("");
[85512ff]16
17 useEffect(() => {
18 if (isOpen) {
19 setIsClosing(false);
20 setPlaylistName("");
[c8baad1]21 setError("");
[85512ff]22 setIsOpening(true);
23 setTimeout(() => setIsOpening(false), 10);
24 }
25 }, [isOpen]);
26
27 const handleClose = () => {
28 setIsClosing(true);
29 setTimeout(() => {
30 onClose();
31 setIsClosing(false);
32 }, 200);
33 };
34
[c8baad1]35 const handleSubmit = async (e: React.FormEvent) => {
[85512ff]36 e.preventDefault();
[c8baad1]37 if (!playlistName.trim() || isSubmitting) return;
38
39 setIsSubmitting(true);
40 try {
41 await axiosInstance.post(
42 `/playlists?playlistName=${encodeURIComponent(playlistName.trim())}`,
43 );
44 toast.success("Playlist created successfully!");
45 onSuccess();
[85512ff]46 handleClose();
[c8baad1]47 } catch (err: any) {
48 setError(err?.response?.data?.error || "Failed to create playlist");
49 } finally {
50 setIsSubmitting(false);
[85512ff]51 }
52 };
53
54 if (!isOpen && !isClosing) return null;
55
56 return (
57 <div
58 className={`fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 px-4 transition-opacity duration-200 ${
59 isClosing || isOpening ? "opacity-0" : "opacity-100"
60 }`}
61 onClick={handleClose}
62 >
63 <div
64 className={`bg-[#181818] rounded-xl p-8 w-full max-w-md border border-white/10 transition-all duration-200 ${
65 isClosing || isOpening
66 ? "scale-95 opacity-0"
67 : "scale-100 opacity-100"
68 }`}
69 onClick={(e) => e.stopPropagation()}
70 >
71 <div className="flex justify-between items-center mb-6">
72 <h2 className="text-2xl font-bold text-white">Create Playlist</h2>
73 <button
74 onClick={handleClose}
75 className="text-gray-400 hover:text-white transition-colors cursor-pointer"
76 aria-label="Close modal"
77 >
78 <svg
79 className="w-6 h-6"
80 fill="none"
81 stroke="currentColor"
82 viewBox="0 0 24 24"
83 >
84 <path
85 strokeLinecap="round"
86 strokeLinejoin="round"
87 strokeWidth={2}
88 d="M6 18L18 6M6 6l12 12"
89 />
90 </svg>
91 </button>
92 </div>
93
94 <form onSubmit={handleSubmit} className="space-y-6">
95 <div>
96 <label
97 className="block text-sm font-medium text-gray-300 mb-2"
98 htmlFor="playlistName"
99 >
100 Playlist Name
101 </label>
102 <input
103 type="text"
104 id="playlistName"
105 autoFocus
[c8baad1]106 className={`w-full bg-[#282828] border rounded-lg py-3 px-4 text-white placeholder-gray-500 focus:outline-none transition-all ${
107 error
108 ? "border-red-500 focus:border-red-500 focus:ring-1 focus:ring-red-500"
109 : "border-white/10 focus:border-[#1db954] focus:ring-1 focus:ring-[#1db954]"
110 }`}
[85512ff]111 placeholder="Enter playlist name"
112 value={playlistName}
[c8baad1]113 onChange={(e) => {
114 setPlaylistName(e.target.value);
115 }}
[85512ff]116 />
[c8baad1]117 <div
118 className={`overflow-hidden transition-all duration-200 ease-out ${
119 error ? "max-h-20 opacity-100 mt-2" : "max-h-0 opacity-0"
120 }`}
121 >
122 <p className="text-sm text-red-400 flex items-center gap-1.5">
123 <svg
124 className="w-4 h-4 shrink-0"
125 fill="currentColor"
126 viewBox="0 0 20 20"
127 >
128 <path
129 fillRule="evenodd"
130 d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
131 clipRule="evenodd"
132 />
133 </svg>
134 {error}
135 </p>
136 </div>
[85512ff]137 </div>
138
139 <div className="flex gap-3">
140 <button
141 type="button"
142 onClick={handleClose}
[c8baad1]143 disabled={isSubmitting}
144 className="flex-1 py-3 bg-[#282828] rounded-full text-white font-semibold hover:bg-[#3a3a3a] transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
[85512ff]145 >
146 Cancel
147 </button>
148 <button
149 type="submit"
[c8baad1]150 disabled={!playlistName.trim() || isSubmitting}
151 className="flex-1 py-3 bg-[#1db954] rounded-full text-black font-semibold hover:bg-[#1ed760] transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-[#1db954] cursor-pointer flex items-center justify-center gap-2"
[85512ff]152 >
[c8baad1]153 {isSubmitting ? (
154 <>
155 <div className="w-4 h-4 border-2 border-black/20 border-t-black rounded-full animate-spin"></div>
156 Creating...
157 </>
158 ) : (
159 "Create"
160 )}
[85512ff]161 </button>
162 </div>
163 </form>
164 </div>
165 </div>
166 );
167};
168
169export default CreatePlaylistModal;
Note: See TracBrowser for help on using the repository browser.