source: frontend/src/pages/LandingPage.tsx@ 694fc25

main
Last change on this file since 694fc25 was 694fc25, checked in by Filip Gavrilovski <filipgavrilovski28@…>, 5 months ago

add landing page with partial mock data

  • Property mode set to 100644
File size: 7.6 KB
Line 
1import { useEffect, useState } from "react";
2import axiosInstance from "../api/axiosInstance";
3import type { Song } from "../utils/types";
4import Nav from "./Nav";
5
6const LandingPage = () => {
7 const [songs, setSongs] = useState<Song[]>([]);
8 const [loading, setLoading] = useState<boolean>(true);
9 const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true);
10
11 // mock data for recently listened songs
12 const recentlyListened = [
13 { id: 1, title: "Song One", artist: "Artist A", cover: "/favicon.png" },
14 { id: 2, title: "Song Two", artist: "Artist B", cover: "/favicon.png" },
15 { id: 3, title: "Song Three", artist: "Artist C", cover: "/favicon.png" },
16 { id: 4, title: "Song Four", artist: "Artist D", cover: "/favicon.png" },
17 { id: 5, title: "Song Five", artist: "Artist E", cover: "/favicon.png" },
18 ];
19
20 // mock data for my playlists
21 const playlists = [
22 { id: 1, name: "My Favorites", songCount: 25 },
23 { id: 2, name: "Workout Mix", songCount: 18 },
24 { id: 3, name: "Chill Vibes", songCount: 32 },
25 { id: 4, name: "Party Hits", songCount: 45 },
26 ];
27
28 useEffect(() => {
29 const fetchSongs = async () => {
30 try {
31 const response = await axiosInstance.get("/songs");
32 const data = response.data;
33 console.log("Fetched songs:", data);
34 setSongs(data);
35 } catch (error) {
36 console.error("Error fetching songs:", error);
37 } finally {
38 setLoading(false);
39 }
40 };
41 fetchSongs();
42 }, []);
43
44 return (
45 <>
46 <Nav
47 isSidebarOpen={isSidebarOpen}
48 onToggleSidebar={() => setIsSidebarOpen(!isSidebarOpen)}
49 />
50 <div className="min-h-screen bg-linear-to-br from-[#1e1e2e] to-[#0f0f1e] text-white flex pt-20">
51 {/* Sidebar */}
52 <div
53 className={`fixed left-0 top-0 h-full bg-[#121212] border-r border-white/10 transition-transform duration-300 ease-in-out z-100 ${
54 isSidebarOpen ? "translate-x-0" : "-translate-x-full"
55 } w-64 overflow-y-auto`}
56 >
57 <div className="p-6">
58 {/* Sidebar Header */}
59 <div className="flex justify-between items-center mb-6">
60 <h2 className="text-xl font-bold text-white">Library</h2>
61 <button
62 onClick={() => setIsSidebarOpen(false)}
63 className="text-gray-400 hover:text-white transition-colors"
64 aria-label="Close sidebar"
65 >
66 <svg
67 className="w-6 h-6"
68 fill="none"
69 stroke="currentColor"
70 viewBox="0 0 24 24"
71 >
72 <path
73 strokeLinecap="round"
74 strokeLinejoin="round"
75 strokeWidth={2}
76 d="M6 18L18 6M6 6l12 12"
77 />
78 </svg>
79 </button>
80 </div>
81
82 {/* Recently Listened */}
83 <div className="mb-8">
84 <h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">
85 Recently Played
86 </h3>
87 <div className="space-y-3">
88 {recentlyListened.map((song) => (
89 <div
90 key={song.id}
91 className="flex items-center gap-3 p-2 rounded-lg hover:bg-white/5 cursor-pointer transition-colors"
92 >
93 <img
94 src={song.cover}
95 alt={song.title}
96 className="w-10 h-10 rounded object-cover"
97 />
98 <div className="flex-1 min-w-0">
99 <p className="text-sm font-medium text-white truncate">
100 {song.title}
101 </p>
102 <p className="text-xs text-gray-400 truncate">
103 {song.artist}
104 </p>
105 </div>
106 </div>
107 ))}
108 </div>
109 </div>
110
111 {/* Playlists */}
112 <div>
113 <h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">
114 Your Playlists
115 </h3>
116 <div className="space-y-2">
117 {playlists.map((playlist) => (
118 <div
119 key={playlist.id}
120 className="flex items-center justify-between p-3 rounded-lg hover:bg-white/5 cursor-pointer transition-colors"
121 >
122 <div className="flex items-center gap-3">
123 <div className="w-10 h-10 bg-linear-to-br from-purple-500 to-pink-500 rounded flex items-center justify-center">
124 <svg
125 className="w-5 h-5 text-white"
126 fill="currentColor"
127 viewBox="0 0 20 20"
128 >
129 <path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" />
130 </svg>
131 </div>
132 <div>
133 <p className="text-sm font-medium text-white">
134 {playlist.name}
135 </p>
136 <p className="text-xs text-gray-400">
137 {playlist.songCount} songs
138 </p>
139 </div>
140 </div>
141 </div>
142 ))}
143 </div>
144 </div>
145 </div>
146 </div>
147
148 {/* Main Content */}
149 <div
150 className={`flex-1 transition-all duration-300 ${
151 isSidebarOpen ? "ml-64" : "ml-0"
152 }`}
153 >
154 <div className="p-8">
155 {loading ? (
156 <div className="flex flex-col items-center justify-center min-h-[60vh] gap-6">
157 <div className="w-12 h-12 border-4 border-white/10 border-t-[#1db954] rounded-full animate-spin"></div>
158 <p className="text-xl text-gray-400">Loading songs...</p>
159 </div>
160 ) : (
161 <div className="max-w-7xl mx-auto">
162 <div className="mb-12 pb-6 border-b border-white/10">
163 <h1 className="text-5xl font-bold mb-3 pb-1 bg-linear-to-r from-[#1db954] to-[#1ed760] bg-clip-text text-transparent">
164 Top Songs
165 </h1>
166 <p className="text-xl text-gray-400">
167 Listen to the newest tracks on FinkWave
168 </p>
169 </div>
170 <div className="grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-8 py-4">
171 {songs.map((song) => (
172 <div
173 key={song.id}
174 className="bg-[#282828] rounded-xl overflow-hidden cursor-pointer shadow-lg transition-all duration-300 ease-in-out hover:-translate-y-2 hover:shadow-2xl group"
175 >
176 <div className="relative w-full pt-[100%] overflow-hidden bg-[#181818]">
177 <img
178 src={song.cover || "/favicon.png"}
179 alt={song.title}
180 className="absolute top-0 left-0 w-full h-full object-cover"
181 onError={(e) => {
182 (e.target as HTMLImageElement).src = "/favicon.png";
183 }}
184 />
185 <div className="absolute top-0 left-0 w-full h-full bg-black/60 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
186 <div className="w-15 h-15 rounded-full bg-[#1db954] flex items-center justify-center text-2xl text-black shadow-[0_4px_12px_rgba(29,185,84,0.5)]">
187
188 </div>
189 </div>
190 </div>
191 <div className="p-4 flex flex-col items-center">
192 <h3 className="text-lg font-semibold mb-2 text-white overflow-hidden text-ellipsis whitespace-nowrap">
193 {song.title}
194 </h3>
195 <p className="text-sm text-gray-400 mb-3 overflow-hidden text-ellipsis whitespace-nowrap">
196 {"<album>"}
197 </p>
198 <p className="text-sm text-gray-400 mb-3 overflow-hidden text-ellipsis whitespace-nowrap">
199 {song.releasedBy}
200 </p>
201 {/* <div className="inline-block bg-[#1db954]/20 px-3 py-1 rounded-xl border border-[#1db954]/40">
202 <span className="text-xs text-[#1db954] font-medium uppercase tracking-wider">
203 {song.genre}
204 </span>
205 </div> */}
206 </div>
207 </div>
208 ))}
209 </div>
210 </div>
211 )}
212 </div>
213 </div>
214 </div>
215 </>
216 );
217};
218
219export default LandingPage;
Note: See TracBrowser for help on using the repository browser.