Changeset 694fc25 for frontend


Ignore:
Timestamp:
02/05/26 18:59:39 (5 months ago)
Author:
Filip Gavrilovski <filipgavrilovski28@…>
Branches:
main
Children:
cb4a1da
Parents:
15782a6
Message:

add landing page with partial mock data

Location:
frontend/src
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • frontend/src/pages/LandingPage.tsx

    r15782a6 r694fc25  
     1import { useEffect, useState } from "react";
     2import axiosInstance from "../api/axiosInstance";
     3import type { Song } from "../utils/types";
     4import Nav from "./Nav";
     5
    16const LandingPage = () => {
    2         return <div>landing page</div>;
     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        );
    3217};
    4218
  • frontend/src/pages/Nav.tsx

    r15782a6 r694fc25  
     1import { useEffect, useRef, useState } from "react";
    12import { Link } from "react-router-dom";
    23import { toast } from "react-toastify";
     
    56import { useAuth } from "../context/authContext";
    67
    7 const Nav = () => {
     8interface NavProps {
     9        isSidebarOpen?: boolean;
     10        onToggleSidebar?: () => void;
     11}
     12
     13const Nav = ({ isSidebarOpen = false, onToggleSidebar }: NavProps) => {
    814        const { user, setUser, isAuthLoading } = useAuth();
     15        const [isDropdownOpen, setIsDropdownOpen] = useState(false);
     16        const dropdownRef = useRef<HTMLDivElement>(null);
    917
    1018        const handleLogout = async (e: React.MouseEvent<HTMLButtonElement>) => {
     
    1321                        await axiosInstance.post("/auth/logout");
    1422                        setUser(undefined);
     23                        setIsDropdownOpen(false);
    1524                        toast.success("Logout successful!");
    1625                } catch (error) {
     
    2029        };
    2130
     31        useEffect(() => {
     32                const handleClickOutside = (event: MouseEvent) => {
     33                        if (
     34                                dropdownRef.current &&
     35                                !dropdownRef.current.contains(event.target as Node)
     36                        ) {
     37                                setIsDropdownOpen(false);
     38                        }
     39                };
     40
     41                document.addEventListener("mousedown", handleClickOutside);
     42                return () => {
     43                        document.removeEventListener("mousedown", handleClickOutside);
     44                };
     45        }, []);
     46
    2247        return (
    23                 <div className="bg-gray-800 p-4 flex justify-between items-center">
    24                         <Link to="/" className="text-white text-lg font-semibold">
    25                                 <img src={Logo} alt="Finkwave Logo" className="h-12 w-auto" />
    26                         </Link>
     48                <div
     49                        className={`bg-gray-800 p-4 flex justify-between items-center fixed top-0 right-0 z-50 transition-all duration-300 ${
     50                                isSidebarOpen ? "left-64" : "left-0"
     51                        }`}
     52                >
     53                        <div className="flex items-center gap-4">
     54                                {onToggleSidebar && (
     55                                        <button
     56                                                onClick={onToggleSidebar}
     57                                                className="text-white hover:text-[#1db954] transition-colors p-2"
     58                                                aria-label="Toggle sidebar"
     59                                        >
     60                                                <svg
     61                                                        className="w-6 h-6"
     62                                                        fill="none"
     63                                                        stroke="currentColor"
     64                                                        viewBox="0 0 24 24"
     65                                                >
     66                                                        <path
     67                                                                strokeLinecap="round"
     68                                                                strokeLinejoin="round"
     69                                                                strokeWidth={2}
     70                                                                d="M4 6h16M4 12h16M4 18h16"
     71                                                        />
     72                                                </svg>
     73                                        </button>
     74                                )}
     75                                <Link to="/" className="text-white text-lg font-semibold">
     76                                        <img src={Logo} alt="Finkwave Logo" className="h-12 w-auto" />
     77                                </Link>
     78                        </div>
    2779
    2880                        <div className="flex items-center space-x-4">
     
    3082                                        <div className="flex items-center space-x-3">
    3183                                                {user ? (
    32                                                         <div className="flex items-center space-x-3">
    33                                                                 <div className="flex items-center space-x-2 bg-slate-700 rounded-lg px-3 py-2">
     84                                                        <div className="relative" ref={dropdownRef}>
     85                                                                <button
     86                                                                        onClick={() => setIsDropdownOpen(!isDropdownOpen)}
     87                                                                        className="focus:outline-none focus:ring-2 focus:ring-blue-500 rounded-full"
     88                                                                >
    3489                                                                        {user.profilePhoto ? (
    3590                                                                                <img
    3691                                                                                        src={`${baseURL}/${user.profilePhoto}`}
    3792                                                                                        alt={`${user.username}'s profile`}
    38                                                                                         className="w-8 h-8 rounded-full object-cover"
     93                                                                                        className="w-10 h-10 rounded-full object-cover cursor-pointer hover:ring-2 hover:ring-blue-400 transition-all"
    3994                                                                                />
    4095                                                                        ) : (
    41                                                                                 <div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
    42                                                                                         <span className="text-white text-sm font-semibold">
     96                                                                                <div className="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center cursor-pointer hover:ring-2 hover:ring-blue-400 transition-all">
     97                                                                                        <span className="text-white text-lg font-semibold">
    4398                                                                                                {user.username.charAt(0).toUpperCase()}
    4499                                                                                        </span>
    45100                                                                                </div>
    46101                                                                        )}
     102                                                                </button>
    47103
    48                                                                         <div className="text-white">
    49                                                                                 <p className="text-sm font-medium">{user.username}</p>
    50                                                                                 <p className="text-xs text-gray-300 capitalize">
    51                                                                                         {user.role}
    52                                                                                 </p>
     104                                                                {isDropdownOpen && (
     105                                                                        <div className="absolute right-0 mt-2 w-48 bg-gray-700 rounded-lg shadow-lg py-1 z-50">
     106                                                                                <Link
     107                                                                                        to="#"
     108                                                                                        className="block px-4 py-2 text-sm text-white hover:bg-gray-600 transition-colors"
     109                                                                                        onClick={() => setIsDropdownOpen(false)}
     110                                                                                >
     111                                                                                        Account
     112                                                                                </Link>
     113                                                                                <button
     114                                                                                        onClick={handleLogout}
     115                                                                                        className="w-full text-left px-4 py-2 text-sm text-white hover:bg-gray-600 transition-colors"
     116                                                                                >
     117                                                                                        Log out
     118                                                                                </button>
    53119                                                                        </div>
    54                                                                 </div>
    55                                                                 <button
    56                                                                         onClick={handleLogout}
    57                                                                         className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg text-sm
    58                                     font-medium transition-colors duration-200 flex items-center space-x-1 cursor-pointer"
    59                                                                 >
    60                                                                         Logout
    61                                                                 </button>
     120                                                                )}
    62121                                                        </div>
    63122                                                ) : (
  • frontend/src/utils/types.ts

    r15782a6 r694fc25  
    2222        type: string;
    2323        releasedBy: string;
     24        cover?: string | null;
    2425        isLikedByCurrentUser?: boolean;
    2526}
Note: See TracChangeset for help on using the changeset viewer.