ChandimaPrabath commited on
Commit
98f877e
·
1 Parent(s): ffc893c

major update

Browse files
frontend/src/app/category/[category]/CategoryPage.css CHANGED
@@ -3,8 +3,7 @@
3
  flex-wrap: wrap;
4
  gap: 10px;
5
  justify-content: center;
6
- overflow-y: auto;
7
- height: 100%;
8
  width: 100%;
9
  }
10
 
@@ -13,38 +12,62 @@
13
  text-align: center;
14
  }
15
 
16
- .music-card-section {
17
  display: flex;
18
- flex-wrap: wrap;
19
- gap: 10px;
20
  justify-content: center;
21
- overflow-y: auto;
22
- height: auto; /* Allow height to adjust to content */
23
- width: 100%;
 
 
 
 
 
 
 
 
24
  }
25
 
26
- .loading {
 
27
  font-size: 1.5rem;
28
  color: #555;
29
  }
30
 
31
  .error {
32
  font-size: 1.2rem;
33
- color: red; /* Change to a more visually impactful color */
 
34
  }
35
 
36
  .load-more {
37
  margin-top: 20px;
38
- padding: 10px 20px;
39
  font-size: 1rem;
40
- background-color: #0070f3; /* Use your preferred button color */
41
  color: white;
42
  border: none;
43
- border-radius: 5px;
44
  cursor: pointer;
45
- transition: background-color 0.3s;
 
 
 
 
46
  }
47
 
48
  .load-more:hover {
49
- background-color: #005bb5; /* Darker shade on hover */
 
 
 
 
 
 
 
 
 
 
50
  }
 
3
  flex-wrap: wrap;
4
  gap: 10px;
5
  justify-content: center;
6
+ height: auto;
 
7
  width: 100%;
8
  }
9
 
 
12
  text-align: center;
13
  }
14
 
15
+ .category-page-loading-container {
16
  display: flex;
17
+ flex-direction: column;
18
+ align-items: center;
19
  justify-content: center;
20
+ height: 100%;
21
+ color: var(--foreground);
22
+ }
23
+
24
+ .category-page-loader {
25
+ border: 8px solid rgba(255, 255, 255, 0.3);
26
+ border-top: 8px solid var(--foreground);
27
+ border-radius: 50%;
28
+ width: 60px;
29
+ height: 60px;
30
+ animation: spin 1s linear infinite;
31
  }
32
 
33
+ .category-page-loading-text {
34
+ margin-top: 10px;
35
  font-size: 1.5rem;
36
  color: #555;
37
  }
38
 
39
  .error {
40
  font-size: 1.2rem;
41
+ color: red;
42
+ margin-top: 20px;
43
  }
44
 
45
  .load-more {
46
  margin-top: 20px;
47
+ padding: 12px 24px;
48
  font-size: 1rem;
49
+ background-color: var(--foreground-2);
50
  color: white;
51
  border: none;
52
+ border-radius: 25px; /* Increased border-radius for a modern look */
53
  cursor: pointer;
54
+ transition: background-color 0.3s, transform 0.3s; /* Added transform transition */
55
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); /* Added shadow for depth */
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center; /* Center text inside button */
59
  }
60
 
61
  .load-more:hover {
62
+ background-color: var(--foreground-3);
63
+ transform: scale(1.05); /* Slightly increase size on hover */
64
+ }
65
+
66
+ .load-more:active {
67
+ transform: scale(0.95); /* Shrink on click for feedback */
68
+ }
69
+
70
+ @keyframes spin {
71
+ 0% { transform: rotate(0deg); }
72
+ 100% { transform: rotate(360deg); }
73
  }
frontend/src/app/category/[category]/page.js CHANGED
@@ -1,8 +1,7 @@
1
- 'use client';
2
  import Card from "@/components/Card";
3
  import './CategoryPage.css';
4
- import { useEffect, useState, useRef } from 'react';
5
- import { use } from 'react';
6
 
7
  export default function CategoryPage({ params }) {
8
  const { category } = use(params); // Unwrap params using React.use()
@@ -11,7 +10,7 @@ export default function CategoryPage({ params }) {
11
  const [error, setError] = useState(null);
12
  const [page, setPage] = useState(1);
13
  const [hasMore, setHasMore] = useState(true);
14
- const [maxPage, setMaxPage] = useState(Infinity); // Store the calculated max page
15
 
16
  useEffect(() => {
17
  const fetchMusicByCategory = async () => {
@@ -25,18 +24,13 @@ export default function CategoryPage({ params }) {
25
  }
26
 
27
  const data = await response.json();
28
-
29
- // Update music items based on response, avoiding duplicates
30
  setMusicItems(prevItems => {
31
  const newItems = data.files.filter(file => !prevItems.includes(file));
32
  return [...prevItems, ...newItems];
33
  });
34
 
35
- // Calculate maxPage based on total_files and limit, then set it
36
  const calculatedMaxPage = Math.ceil(data.total_files / data.limit);
37
  setMaxPage(calculatedMaxPage);
38
-
39
- // Update hasMore based on whether we have reached maxPage
40
  setHasMore(page < calculatedMaxPage);
41
  } catch (err) {
42
  setError(err.message);
@@ -48,40 +42,33 @@ export default function CategoryPage({ params }) {
48
  fetchMusicByCategory();
49
  }, [category, page]);
50
 
51
- // Load more when scrolled to the bottom of the app container
52
  useEffect(() => {
53
- const appContainer = document.querySelector('.app-container'); // Get the app-container element
54
  if (!appContainer) return;
55
 
56
  const handleScroll = () => {
57
  const { scrollTop, clientHeight, scrollHeight } = appContainer;
58
-
59
- // Check if we are at the bottom of the scrollable container
60
  if (scrollHeight - scrollTop <= clientHeight + 10 && hasMore && !loading) {
61
  loadMore();
62
  }
63
  };
64
 
65
  appContainer.addEventListener('scroll', handleScroll);
66
-
67
  return () => {
68
  appContainer.removeEventListener('scroll', handleScroll);
69
  };
70
  }, [hasMore, loading]);
71
 
72
  const loadMore = () => {
73
- if (hasMore && page < maxPage) { // Ensure page does not exceed maxPage
74
  setPage(prevPage => prevPage + 1);
75
  }
76
  };
77
 
78
- // Initial loading logic to fill the app-container
79
  useEffect(() => {
80
- const appContainer = document.querySelector('.app-container'); // Get the app-container element
81
  if (appContainer) {
82
  const { scrollHeight, clientHeight } = appContainer;
83
-
84
- // If content height is less than or equal to client height, load more
85
  if (scrollHeight <= clientHeight && hasMore) {
86
  loadMore();
87
  }
@@ -89,7 +76,12 @@ export default function CategoryPage({ params }) {
89
  }, [musicItems, hasMore]);
90
 
91
  if (loading && musicItems.length === 0) {
92
- return <div className="loading">Loading music...</div>;
 
 
 
 
 
93
  }
94
 
95
  if (error) {
@@ -97,16 +89,16 @@ export default function CategoryPage({ params }) {
97
  }
98
 
99
  return (
100
- <div className="category-page font-[family-name:var(--font-geist-sans)]">
101
  <h1>{category.charAt(0).toUpperCase() + category.slice(1)} Music</h1>
102
  <div className="music-card-section">
103
  {musicItems.map((file, index) => (
104
  <Card
105
- key={`${file}-${index}`} // Ensure unique key by using index
106
- src={file} // Assuming the base URL is handled correctly
107
  />
108
  ))}
109
- {loading && <div className="load-more">Loading more...</div>} {/* Loading indicator at the bottom */}
110
  </div>
111
  </div>
112
  );
 
1
+ "use client";
2
  import Card from "@/components/Card";
3
  import './CategoryPage.css';
4
+ import { useEffect, useState, use } from 'react';
 
5
 
6
  export default function CategoryPage({ params }) {
7
  const { category } = use(params); // Unwrap params using React.use()
 
10
  const [error, setError] = useState(null);
11
  const [page, setPage] = useState(1);
12
  const [hasMore, setHasMore] = useState(true);
13
+ const [maxPage, setMaxPage] = useState(Infinity);
14
 
15
  useEffect(() => {
16
  const fetchMusicByCategory = async () => {
 
24
  }
25
 
26
  const data = await response.json();
 
 
27
  setMusicItems(prevItems => {
28
  const newItems = data.files.filter(file => !prevItems.includes(file));
29
  return [...prevItems, ...newItems];
30
  });
31
 
 
32
  const calculatedMaxPage = Math.ceil(data.total_files / data.limit);
33
  setMaxPage(calculatedMaxPage);
 
 
34
  setHasMore(page < calculatedMaxPage);
35
  } catch (err) {
36
  setError(err.message);
 
42
  fetchMusicByCategory();
43
  }, [category, page]);
44
 
 
45
  useEffect(() => {
46
+ const appContainer = document.querySelector('.app-container');
47
  if (!appContainer) return;
48
 
49
  const handleScroll = () => {
50
  const { scrollTop, clientHeight, scrollHeight } = appContainer;
 
 
51
  if (scrollHeight - scrollTop <= clientHeight + 10 && hasMore && !loading) {
52
  loadMore();
53
  }
54
  };
55
 
56
  appContainer.addEventListener('scroll', handleScroll);
 
57
  return () => {
58
  appContainer.removeEventListener('scroll', handleScroll);
59
  };
60
  }, [hasMore, loading]);
61
 
62
  const loadMore = () => {
63
+ if (hasMore && page < maxPage) {
64
  setPage(prevPage => prevPage + 1);
65
  }
66
  };
67
 
 
68
  useEffect(() => {
69
+ const appContainer = document.querySelector('.app-container');
70
  if (appContainer) {
71
  const { scrollHeight, clientHeight } = appContainer;
 
 
72
  if (scrollHeight <= clientHeight && hasMore) {
73
  loadMore();
74
  }
 
76
  }, [musicItems, hasMore]);
77
 
78
  if (loading && musicItems.length === 0) {
79
+ return (
80
+ <div className="category-page-loading-container">
81
+ <div className="category-page-loader"></div>
82
+ <span className="category-page-loading-text">Loading music...</span>
83
+ </div>
84
+ );
85
  }
86
 
87
  if (error) {
 
89
  }
90
 
91
  return (
92
+ <div className="category-page">
93
  <h1>{category.charAt(0).toUpperCase() + category.slice(1)} Music</h1>
94
  <div className="music-card-section">
95
  {musicItems.map((file, index) => (
96
  <Card
97
+ key={`${file}-${index}`}
98
+ src={file}
99
  />
100
  ))}
101
+ {loading && <div className="load-more">Loading more...</div>}
102
  </div>
103
  </div>
104
  );
frontend/src/app/globals.css CHANGED
@@ -4,12 +4,15 @@
4
 
5
  :root {
6
  --background: #080523;
 
 
 
 
7
  --foreground: #ededed;
8
- --background-secondary: #060a52;
9
  --foreground-secondary: #1d85b9;
10
- --background-2: #2a3477;
11
  --foreground-2: #0b5da9;
12
- --foreground-3: #0f0e60;
 
13
  }
14
 
15
  html,
@@ -22,7 +25,7 @@ body {
22
 
23
  body {
24
  color: var(--foreground);
25
- background: var(--background);
26
  font-family: Arial, Helvetica, sans-serif;
27
  display: flex;
28
  flex-direction: column;
 
4
 
5
  :root {
6
  --background: #080523;
7
+ --background-secondary: #121029;
8
+ --background-2: #1e2b57;
9
+ --background-3: #0d0e1e;
10
+ --background-4: #313c51;
11
  --foreground: #ededed;
 
12
  --foreground-secondary: #1d85b9;
 
13
  --foreground-2: #0b5da9;
14
+ --foreground-3: #25253c;
15
+ --foreground-4: #878787;
16
  }
17
 
18
  html,
 
25
 
26
  body {
27
  color: var(--foreground);
28
+ background: var(--background-3);
29
  font-family: Arial, Helvetica, sans-serif;
30
  display: flex;
31
  flex-direction: column;
frontend/src/app/layout.js CHANGED
@@ -6,6 +6,7 @@ import "./globals.css";
6
  import MusicPlayer from "@/components/MusicPlayer";
7
  import { MusicPlayerProvider } from "@/context/MusicPlayerContext";
8
  import { AppProgressBar as ProgressBar } from 'next-nprogress-bar';
 
9
 
10
  const geistSans = localFont({
11
  src: "./fonts/GeistVF.woff",
@@ -31,6 +32,7 @@ export default function RootLayout({ children }) {
31
  options={{ showSpinner: false }}
32
  shallowRouting
33
  />
 
34
  <div className="app-container">{children}</div>
35
  <footer className="bottom-0 flex items-center w-full">
36
  <MusicPlayer />
 
6
  import MusicPlayer from "@/components/MusicPlayer";
7
  import { MusicPlayerProvider } from "@/context/MusicPlayerContext";
8
  import { AppProgressBar as ProgressBar } from 'next-nprogress-bar';
9
+ import Header from "@/components/Header";
10
 
11
  const geistSans = localFont({
12
  src: "./fonts/GeistVF.woff",
 
32
  options={{ showSpinner: false }}
33
  shallowRouting
34
  />
35
+ <Header/>
36
  <div className="app-container">{children}</div>
37
  <footer className="bottom-0 flex items-center w-full">
38
  <MusicPlayer />
frontend/src/components/Card.css CHANGED
@@ -1,7 +1,7 @@
1
  .music-card {
2
  width: 250px;
3
  height: 40;
4
- background-image: linear-gradient(var(--background-2), var(--foreground-3));
5
  border-top-left-radius: 20px;
6
  border-bottom-left-radius: 20px;
7
  border-top-right-radius: 5px;
 
1
  .music-card {
2
  width: 250px;
3
  height: 40;
4
+ background-image: linear-gradient(var(--background-4), var(--foreground-3));
5
  border-top-left-radius: 20px;
6
  border-bottom-left-radius: 20px;
7
  border-top-right-radius: 5px;
frontend/src/components/CategoriesSection.css CHANGED
@@ -1,14 +1,16 @@
 
 
1
  .categories-section {
2
  display: flex;
3
- gap: 10px;
4
  flex-wrap: wrap;
5
- padding: 10px;
6
  justify-content: center;
7
  }
8
 
9
  .category-card {
10
- width: 90px;
11
- height: 70px;
12
  padding: 10px;
13
  background-image: linear-gradient(var(--foreground-secondary), var(--foreground-3));
14
  border-radius: 10px;
@@ -17,17 +19,54 @@
17
  display: flex;
18
  justify-content: center;
19
  align-items: center;
 
 
20
  }
21
 
22
  .category-title {
23
  font-size: 1rem;
24
  text-transform: capitalize;
25
  font-weight: 600;
26
- cursor: pointer;
27
- transition: scale .3s ease;
28
  overflow-wrap: break-word;
29
  }
30
 
31
- .category-card:hover .category-title{
32
- scale: 1.1;
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* CategoriesSection.css */
2
+
3
  .categories-section {
4
  display: flex;
5
+ gap: 15px;
6
  flex-wrap: wrap;
7
+ padding: 20px;
8
  justify-content: center;
9
  }
10
 
11
  .category-card {
12
+ width: 100px;
13
+ height: 80px;
14
  padding: 10px;
15
  background-image: linear-gradient(var(--foreground-secondary), var(--foreground-3));
16
  border-radius: 10px;
 
19
  display: flex;
20
  justify-content: center;
21
  align-items: center;
22
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
23
+ cursor: pointer;
24
  }
25
 
26
  .category-title {
27
  font-size: 1rem;
28
  text-transform: capitalize;
29
  font-weight: 600;
 
 
30
  overflow-wrap: break-word;
31
  }
32
 
33
+ .category-card:hover {
34
+ transform: translateY(-5px);
35
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
36
+ }
37
+
38
+ .loading-container {
39
+ display: flex;
40
+ flex-direction: column;
41
+ align-items: center;
42
+ justify-content: center;
43
+ height: 100px; /* Adjust height as needed */
44
+ }
45
+
46
+ .loading-spinner {
47
+ border: 4px solid rgba(255, 255, 255, 0.3);
48
+ border-top: 4px solid var(--foreground);
49
+ border-radius: 50%;
50
+ width: 40px; /* Size of the spinner */
51
+ height: 40px; /* Size of the spinner */
52
+ animation: spin 1s linear infinite;
53
+ }
54
+
55
+ .loading-text {
56
+ margin-top: 10px;
57
+ color: var(--foreground);
58
+ font-weight: 500;
59
+ }
60
+
61
+ /* Spinner animation */
62
+ @keyframes spin {
63
+ 0% { transform: rotate(0deg); }
64
+ 100% { transform: rotate(360deg); }
65
+ }
66
+
67
+ .error-message {
68
+ color: var(--foreground);
69
+ font-weight: 500;
70
+ text-align: center;
71
+ margin: 20px;
72
+ }
frontend/src/components/CategoriesSection.js CHANGED
@@ -1,3 +1,4 @@
 
1
  "use client";
2
  import { useEffect, useState } from "react";
3
  import "./CategoriesSection.css";
@@ -28,11 +29,16 @@ const CategoriesSection = () => {
28
  }, []);
29
 
30
  if (loading) {
31
- return <div>Loading categories...</div>;
 
 
 
 
 
32
  }
33
 
34
  if (error) {
35
- return <div>Error: {error}</div>;
36
  }
37
 
38
  return (
@@ -47,9 +53,9 @@ const CategoriesSection = () => {
47
  const CategoryCard = ({ label }) => {
48
  return (
49
  <Link href={`/category/${label}`}>
50
- <div className="category-card">
51
- <label className="category-title">{label}</label>
52
- </div>
53
  </Link>
54
  );
55
  };
 
1
+ // CategoriesSection.js
2
  "use client";
3
  import { useEffect, useState } from "react";
4
  import "./CategoriesSection.css";
 
29
  }, []);
30
 
31
  if (loading) {
32
+ return (
33
+ <div className="loading-container">
34
+ <div className="loading-spinner"></div>
35
+ <p className="loading-text">Loading categories...</p>
36
+ </div>
37
+ );
38
  }
39
 
40
  if (error) {
41
+ return <div className="error-message">Error: {error}</div>;
42
  }
43
 
44
  return (
 
53
  const CategoryCard = ({ label }) => {
54
  return (
55
  <Link href={`/category/${label}`}>
56
+ <div className="category-card">
57
+ <label className="category-title">{label}</label>
58
+ </div>
59
  </Link>
60
  );
61
  };
frontend/src/components/Header.css ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Header.css */
2
+
3
+ .header {
4
+ background-color: var(--background);
5
+ padding: 15px 10px;
6
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
7
+ }
8
+
9
+ .header-container {
10
+ display: flex;
11
+ justify-content: space-between;
12
+ align-items: center;
13
+ }
14
+
15
+ .logo {
16
+ color: var(--foreground);
17
+ font-size: 24px;
18
+ font-weight: bold;
19
+ }
20
+
21
+ .header-music{
22
+ font-weight: 300;
23
+ font-size: 1rem;
24
+ color: var(--foreground-4);
25
+ }
26
+
27
+ .nav {
28
+ display: flex;
29
+ gap: 20px;
30
+ }
31
+
32
+ .nav-link {
33
+ color: var(--foreground);
34
+ text-decoration: none;
35
+ transition: color 0.3s ease;
36
+ }
37
+
38
+ .nav-link:hover {
39
+ color: var(--foreground-2);
40
+ }
41
+
42
+ .search-container {
43
+ position: relative;
44
+ }
45
+
46
+ .search-input {
47
+ padding: 8px 12px;
48
+ border: 1px solid var(--foreground-3);
49
+ border-radius: 4px;
50
+ background-color: transparent;
51
+ color: var(--foreground);
52
+ }
53
+
54
+ .search-input::placeholder {
55
+ color: var(--foreground-4);
56
+ }
57
+
58
+ .search-input:focus {
59
+ outline: none;
60
+ border-color: var(--foreground-2);
61
+ }
frontend/src/components/Header.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Header.js
2
+ import React from "react";
3
+ import Link from "next/link";
4
+ import "./Header.css"; // Ensure to import the CSS file
5
+
6
+ const Header = () => {
7
+ return (
8
+ <header className="header">
9
+ <div className="header-container">
10
+ <h1 className="logo">
11
+ Nexus <sup className="header-music"> Music</sup>
12
+ </h1>
13
+ <nav className="nav">
14
+ <Link href="/" className="nav-link">
15
+ Home
16
+ </Link>
17
+ </nav>
18
+ </div>
19
+ </header>
20
+ );
21
+ };
22
+
23
+ export default Header;