From ef5639484a1f9336fb07847e23409e2b1385a8bc Mon Sep 17 00:00:00 2001 From: Astri4-4 Date: Mon, 21 Jul 2025 21:29:07 +0000 Subject: [PATCH 1/3] ADD DESIGN --- backend/app/controllers/channel.controller.js | 51 ++- backend/app/routes/channel.route.js | 6 +- backend/app/uploads/profiles/sacha.jpg | Bin 50635 -> 0 bytes backend/logs/access.log | 393 ++++++++++++++++++ .../src/components/VideoStatListElement.jsx | 19 + frontend/src/index.css | 4 + frontend/src/pages/Account.jsx | 2 +- frontend/src/pages/ManageChannel.jsx | 179 ++++++++ frontend/src/routes/routes.jsx | 9 + 9 files changed, 659 insertions(+), 4 deletions(-) delete mode 100644 backend/app/uploads/profiles/sacha.jpg create mode 100644 frontend/src/components/VideoStatListElement.jsx create mode 100644 frontend/src/pages/ManageChannel.jsx diff --git a/backend/app/controllers/channel.controller.js b/backend/app/controllers/channel.controller.js index e9a90f4..702ab25 100644 --- a/backend/app/controllers/channel.controller.js +++ b/backend/app/controllers/channel.controller.js @@ -27,10 +27,30 @@ export async function getById(req, res) { const logger = req.body.logger; logger.action("try to get channel with id " + id); const client = await getClient(); - const query = `SELECT * FROM channels WHERE id = $1`; + + const query = ` + SELECT * + FROM channels + WHERE channels.id = $1 + `; + const result = await client.query(query, [id]); + + const videoQuery = ` + SELECT v.*, COUNT(h.video) as views, COUNT(l.id) as likes, COUNT(c.id) as comments + FROM videos v + LEFT JOIN history h ON v.id = h.video + LEFT JOIN likes l ON v.id = l.video + LEFT JOIN comments c ON v.id = c.video + WHERE v.channel = $1 + GROUP BY v.id, title, thumbnail, description, channel, visibility, file, slug, format, release_date + `; + const videoResult = await client.query(videoQuery, [id]); + + result.rows[0].videos = videoResult.rows; + logger.write("Successfully get channel with id " + id, 200); - res.status(200).json(result); + res.status(200).json(result.rows[0]); } @@ -146,4 +166,31 @@ export async function toggleSubscription(req, res) { logger.write("Successfully subscribed to channel", 200); res.status(200).json({message: 'Subscribed successfully', subscriptions: totalSubscriptions}); } +} + +export async function getStats(req, res) { + + try { + const id = req.params.id; + + const logger = req.body.logger; + logger.action("try to get stats"); + + const request = ` + SELECT COUNT(s.id) as subscribers, COUNT(h.user_id) as views + FROM channels + LEFT JOIN public.subscriptions s on channels.id = s.channel + LEFT JOIN public.videos v on channels.id = v.channel + LEFT JOIN public.history h on v.id = h.video + WHERE channels.id = $1 + `; + const client = await getClient(); + const result = await client.query(request, [id]); + logger.write("Successfully get stats", 200); + res.status(200).json(result.rows[0]); + } catch (error) { + console.log(error); + res.status(500).json({error: error.message}); + } + } \ No newline at end of file diff --git a/backend/app/routes/channel.route.js b/backend/app/routes/channel.route.js index 517c28f..4876c6a 100644 --- a/backend/app/routes/channel.route.js +++ b/backend/app/routes/channel.route.js @@ -1,5 +1,5 @@ import {Router} from "express"; -import {create, del, getAll, getById, toggleSubscription, update} from "../controllers/channel.controller.js"; +import {create, del, getAll, getById, getStats, toggleSubscription, update} from "../controllers/channel.controller.js"; import {isTokenValid} from "../middlewares/jwt.middleware.js"; import { Channel, @@ -32,4 +32,8 @@ router.delete("/:id", [addLogger, isTokenValid, Channel.id, validator, doChannel // TOGGLE SUBSCRIPTION router.post("/:id/subscribe", [addLogger, isTokenValid, Channel.id, validator, doChannelExists], toggleSubscription); +// GET TOTAL VIEWS AND SUBSCRIBERS OF THE CHANNEL +router.get("/:id/stats", [addLogger, isTokenValid, Channel.id, validator, doChannelExists, isOwner], getStats); + + export default router; \ No newline at end of file diff --git a/backend/app/uploads/profiles/sacha.jpg b/backend/app/uploads/profiles/sacha.jpg deleted file mode 100644 index 002851620667539340e2fe1613da41f49a86dd0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50635 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 + +
+

{video.title}

+

Vues: {video.views}

+

Likes: {video.likes}

+

Commentaires: {video.comments}

+
+ + ); +} \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index 9a9cfa1..1668683 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -31,6 +31,10 @@ backdrop-filter: blur(27.5px); } +.resizable-none { + resize: none; +} + @theme { /* Fonts */ --font-inter: 'Inter', sans-serif; diff --git a/frontend/src/pages/Account.jsx b/frontend/src/pages/Account.jsx index e0cbe80..e6a3572 100644 --- a/frontend/src/pages/Account.jsx +++ b/frontend/src/pages/Account.jsx @@ -255,7 +255,7 @@ export default function Account() {

{userChannel.channel.name}

diff --git a/frontend/src/pages/ManageChannel.jsx b/frontend/src/pages/ManageChannel.jsx new file mode 100644 index 0000000..20d351d --- /dev/null +++ b/frontend/src/pages/ManageChannel.jsx @@ -0,0 +1,179 @@ +import Navbar from "../components/Navbar.jsx"; +import {useEffect, useState} from "react"; +import {useNavigate, useParams} from "react-router-dom"; +import {useAuth} from "../contexts/AuthContext.jsx"; +import VideoStatListElement from "../components/VideoStatListElement.jsx"; + + +export default function ManageChannel() { + + const {id} = useParams(); + const {user} = useAuth(); + const navigate = useNavigate(); + + const [channel, setChannel] = useState(); + const [channelStats, setChannelStats] = useState(); + const [channelName, setChannelName] = useState(null); + const [description, setDescription] = useState(null); + const [editMode, setEditMode] = useState(false); + + const token = localStorage.getItem("token"); + const nonEditModeClasses = "text-2xl font-bold text-white p-2 focus:text-white focus:outline-none w-full font-montserrat resizable-none text-center"; + const editModeClasses = nonEditModeClasses + " glassmorphism"; + + const nonEditModeClassesTextArea = "text-md font-normal text-white p-2 focus:text-white focus:outline-none w-full font-montserrat resizable-none w-full" + const editModeClassesTextArea = nonEditModeClassesTextArea + " glassmorphism h-48"; + + useEffect(() => { + fetchChannelData() + fetchChannelStats() + }, []); + + const fetchChannelData = async () => { + try { + const request = await fetch(`/api/channels/${id}`, { + method: "GET", + headers: { + "Authorization": `Bearer ${token}` + } + }) + const result = await request.json(); + setChannel(result); + + } catch { + console.error("Error fetching channel data."); + } + + } + const fetchChannelStats = async () => { + try { + const request = await fetch(`/api/channels/${id}/stats`, { + method: "GET", + headers: { + "Authorization": `Bearer ${token}` + } + }) + const result = await request.json(); + setChannelStats(result); + } catch (error) { + console.error("Error fetching channel stats", error); + } + } + + return ( +
+ + +
+ + {/* LEFT SIDE */} +
+ + + setChannelName(e.target.value)} + placeholder="Nom d'utilisateur" + disabled={!editMode} + /> + + + + + { + editMode ? ( +
+ + +
+ ) : ( + + ) + } + +
+ + {/* RIGHT SIDE */} +
+ {/* VIEW / SUBSCRIBERS STATS */} + +
+
+ {/* TOTAL VIEWS */} +

Vues totales

+

{channelStats ? channelStats.views : "0"}

+
+
+ {/* TOTAL SUBSCRIBERS */} +

Abonnés

+

{channelStats ? channelStats.subscribers : "0"}

+
+
+ + {/* VIDEOS */} +
+

Vidéos

+ + +
+ + { channel && channel.videos && channel.videos.length > 0 ? ( +
+ {channel.videos.map((video) => ( + navigate("/manage-video/" + video.id)} + /> + ))} +
+ ) : ( +

Aucune vidéo trouvée pour cette chaîne.

+ )} + + + +
+ +
+ +
+ ) + +} \ No newline at end of file diff --git a/frontend/src/routes/routes.jsx b/frontend/src/routes/routes.jsx index e366840..f84ee4a 100644 --- a/frontend/src/routes/routes.jsx +++ b/frontend/src/routes/routes.jsx @@ -4,6 +4,7 @@ import Register from '../pages/Register.jsx' import Video from '../pages/Video.jsx' import ProtectedRoute from '../components/ProtectedRoute.jsx' import Account from "../pages/Account.jsx"; +import ManageChannel from "../pages/ManageChannel.jsx"; const routes = [ { path: "/", element: }, @@ -34,6 +35,14 @@ const routes = [ ) + }, + { + path: "/manage-channel/:id", + element: ( + + + + ) } ] From 2498caadb062c1eff167590a41025a5d5803f3dd Mon Sep 17 00:00:00 2001 From: Astri4-4 Date: Mon, 21 Jul 2025 22:11:36 +0000 Subject: [PATCH 2/3] ADD update channel --- backend/logs/access.log | 62 ++++++++++++++++++++++++++++ frontend/src/pages/ManageChannel.jsx | 28 +++++++++++++ 2 files changed, 90 insertions(+) diff --git a/backend/logs/access.log b/backend/logs/access.log index a7c3dbd..4ef4665 100644 --- a/backend/logs/access.log +++ b/backend/logs/access.log @@ -1856,3 +1856,65 @@ [2025-07-21 21:24:01.557] [undefined] GET(/:id/stats): try to get stats [2025-07-21 21:24:01.559] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 [2025-07-21 21:24:01.566] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:09:05.554] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:09:05.562] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:09:05.568] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:09:05.574] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:09:22.406] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:09:22.416] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:09:22.417] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:09:22.425] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:09:26.086] [undefined] PUT(/:id): failed due to invalid values with status 400 +[2025-07-21 22:09:34.169] [undefined] PUT(/:id): failed due to invalid values with status 400 +[2025-07-21 22:09:39.872] [undefined] PUT(/:id): failed due to invalid values with status 400 +[2025-07-21 22:10:27.086] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:27.095] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:10:27.096] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:27.104] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:10:32.179] [undefined] PUT(/:id): try to update channel with id 1 +[2025-07-21 22:10:32.181] [undefined] PUT(/:id): Successfully updated channel with status 200 +[2025-07-21 22:10:32.192] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:32.199] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:33.864] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:33.873] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:10:33.875] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:33.882] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:10:34.455] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:34.464] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:10:34.466] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:34.472] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:10:34.943] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:34.951] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:10:34.954] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:34.960] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:10:41.192] [undefined] PUT(/:id): try to update channel with id 1 +[2025-07-21 22:10:41.201] [undefined] PUT(/:id): Successfully updated channel with status 200 +[2025-07-21 22:10:41.210] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:41.217] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:42.177] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:42.186] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:10:42.187] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:42.195] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:10:45.345] [undefined] GET(/:id): try to get video 1 +[2025-07-21 22:10:45.354] [undefined] GET(/:id): successfully get video 1 with status 200 +[2025-07-21 22:10:45.363] [undefined] GET(/:id/similar): try to get similar videos for video 1 +[2025-07-21 22:10:45.372] [undefined] GET(/:id/similar): No tags found for video 1 with status 404 +[2025-07-21 22:10:45.387] [undefined] GET(/:id/views): try to add views for video 1 +[2025-07-21 22:10:45.394] [undefined] GET(/:id/views): successfully added views for video 1 with status 200 +[2025-07-21 22:10:50.080] [undefined] GET(/:id/channel): try to retrieve channel of user 1 +[2025-07-21 22:10:50.081] [undefined] GET(/:id/history): try to retrieve history of user 1 +[2025-07-21 22:10:50.082] [undefined] GET(/:id/channel): successfully retrieved channel of user 1 with status 200 +[2025-07-21 22:10:50.085] [undefined] GET(/:id/history): successfully retrieved history of user 1 with status 200 +[2025-07-21 22:10:50.090] [undefined] GET(/user/:id): Playlists retrieved for user with id 1 with status 200 +[2025-07-21 22:10:53.388] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:10:53.396] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:10:53.399] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:10:53.404] [undefined] GET(/:id/stats): Successfully get stats with status 200 +[2025-07-21 22:11:01.752] [undefined] PUT(/:id): try to update channel with id 1 +[2025-07-21 22:11:01.776] [undefined] PUT(/:id): Successfully updated channel with status 200 +[2025-07-21 22:11:01.787] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:11:01.794] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:11:05.090] [undefined] GET(/:id): try to get channel with id 1 +[2025-07-21 22:11:05.099] [undefined] GET(/:id/stats): try to get stats +[2025-07-21 22:11:05.102] [undefined] GET(/:id): Successfully get channel with id 1 with status 200 +[2025-07-21 22:11:05.107] [undefined] GET(/:id/stats): Successfully get stats with status 200 diff --git a/frontend/src/pages/ManageChannel.jsx b/frontend/src/pages/ManageChannel.jsx index 20d351d..8c58cd3 100644 --- a/frontend/src/pages/ManageChannel.jsx +++ b/frontend/src/pages/ManageChannel.jsx @@ -60,6 +60,33 @@ export default function ManageChannel() { } } + const handleUpdateChannel = async () => { + if (!editMode) return; + + try { + const response = await fetch(`/api/channels/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + }, + body: JSON.stringify({ + name: channelName || channel.name, + description: description || channel.description, + }) + }); + + if (response.ok) { + setEditMode(false); + fetchChannelData(); // Refresh channel data after update + } else { + console.error("Failed to update channel"); + } + } catch (error) { + console.error("Error updating channel:", error); + } + } + return (
@@ -101,6 +128,7 @@ export default function ManageChannel() { From d8aa1c16d5a4d6d408a68716480b0cd68f6a0dae Mon Sep 17 00:00:00 2001 From: Astri4-4 Date: Mon, 21 Jul 2025 22:19:02 +0000 Subject: [PATCH 3/3] RESOLVING issues from PR#8 --- backend/app/controllers/channel.controller.js | 4 +++- frontend/src/pages/ManageChannel.jsx | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/backend/app/controllers/channel.controller.js b/backend/app/controllers/channel.controller.js index 702ab25..c359fe8 100644 --- a/backend/app/controllers/channel.controller.js +++ b/backend/app/controllers/channel.controller.js @@ -177,7 +177,9 @@ export async function getStats(req, res) { logger.action("try to get stats"); const request = ` - SELECT COUNT(s.id) as subscribers, COUNT(h.user_id) as views + SELECT + (SELECT COUNT(*) FROM subscriptions WHERE channel = $1) as subscribers, + (SELECT COUNT(*) FROM history h JOIN videos v ON h.video = v.id WHERE v.channel = $1) as views FROM channels LEFT JOIN public.subscriptions s on channels.id = s.channel LEFT JOIN public.videos v on channels.id = v.channel diff --git a/frontend/src/pages/ManageChannel.jsx b/frontend/src/pages/ManageChannel.jsx index 8c58cd3..68d5b55 100644 --- a/frontend/src/pages/ManageChannel.jsx +++ b/frontend/src/pages/ManageChannel.jsx @@ -40,8 +40,8 @@ export default function ManageChannel() { const result = await request.json(); setChannel(result); - } catch { - console.error("Error fetching channel data."); + } catch (error) { + console.error("Error fetching channel data:", error); } } @@ -113,7 +113,7 @@ export default function ManageChannel() { Description