Compare commits

...

3 Commits

  1. 50
      backend/app/controllers/channel.controller.js
  2. 24
      backend/app/controllers/user.controller.js
  3. 2
      backend/app/routes/channel.route.js
  4. 7
      backend/app/routes/user.route.js
  5. 404
      backend/logs/access.log
  6. 0
      frontend/src/components/ChannelLastVideo.jsx
  7. 20
      frontend/src/components/ChannelLastVideos.jsx
  8. 43
      frontend/src/components/TabLayout.jsx
  9. 26
      frontend/src/components/VideoCard.jsx
  10. 16
      frontend/src/index.css
  11. 113
      frontend/src/pages/Channel.jsx
  12. 17
      frontend/src/routes/routes.jsx
  13. 41
      frontend/src/services/channel.service.js
  14. 18
      frontend/src/services/user.service.js

50
backend/app/controllers/channel.controller.js

@ -30,25 +30,57 @@ export async function getById(req, res) {
const client = await getClient();
const query = `
SELECT *
SELECT channels.*, u.username, u.picture, COUNT(s.id) as subscriptions
FROM channels
JOIN public.users u ON channels.owner = u.id
LEFT JOIN public.subscriptions s ON channels.id = s.channel
WHERE channels.id = $1
GROUP BY channels.id, name, description, channels.owner, u.username, u.picture
`;
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
SELECT
videos.id,
videos.title,
videos.description AS video_description,
videos.thumbnail,
videos.channel,
videos.visibility,
videos.file,
videos.slug,
videos.format,
videos.release_date,
channels.name AS name,
channels.description AS description,
users.picture AS profilePicture,
COUNT(h.id) AS views
FROM public.videos
LEFT JOIN public.channels ON videos.channel = channels.id
LEFT JOIN public.users ON channels.OWNER = users.id
LEFT JOIN public.history h ON h.video = videos.id
WHERE videos.channel = $1
GROUP BY videos.id, channels.name, channels.description, users.username, users.picture
`;
const videoResult = await client.query(videoQuery, [id]);
result.rows[0].videos = videoResult.rows;
const videoReturn = [];
for (const video of videoResult.rows) {
video.creator = {
name: video.name,
profilePicture: video.profilepicture,
description: video.video_description
};
delete video.name;
delete video.profilepicture;
delete video.video_description;
videoReturn.push(video);
}
result.rows[0].videos = videoReturn;
logger.write("Successfully get channel with id " + id, 200);
client.end();

24
backend/app/controllers/user.controller.js

@ -307,4 +307,28 @@ export async function getHistory(req, res) {
logger.write("successfully retrieved history of user " + id, 200);
client.end();
res.status(200).json(videos);
}
export async function isSubscribed(req, res) {
const token = req.headers.authorization.split(" ")[1];
const tokenPayload = jwt.decode(token);
const userId = tokenPayload.id;
const channelId = req.params.id;
const client = await getClient();
const logger = req.body.logger;
logger.action(`check if user ${userId} is subscribed to channel ${channelId}`);
const query = `SELECT * FROM subscriptions WHERE owner = $1 AND channel = $2`;
const result = await client.query(query, [userId, channelId]);
if (result.rows[0]) {
logger.write(`user ${userId} is subscribed to channel ${channelId}`, 200);
client.end();
return res.status(200).json({subscribed: true});
} else {
logger.write(`user ${userId} is not subscribed to channel ${channelId}`, 200);
client.end();
return res.status(200).json({subscribed: false});
}
}

2
backend/app/routes/channel.route.js

@ -18,7 +18,7 @@ const router = Router();
router.post("/", [addLogger, isTokenValid, ChannelCreate.name, ChannelCreate.description, ChannelCreate.owner, validator, doUserExistsBody, doUserHaveChannel, isOwnerBody, doChannelNameExists], create);
// GET CHANNEL BY ID
router.get("/:id", [addLogger, isTokenValid, Channel.id, validator, doChannelExists], getById);
router.get("/:id", [addLogger, Channel.id, validator, doChannelExists], getById);
// GET ALL CHANNEL
router.get("/", [addLogger, isTokenValid], getAll);

7
backend/app/routes/user.route.js

@ -6,7 +6,8 @@ import {
getByUsername,
update,
deleteUser,
getChannel, getHistory
getChannel, getHistory,
isSubscribed
} from "../controllers/user.controller.js";
import {
UserRegister,
@ -22,6 +23,7 @@ import validator from "../middlewares/error.middleware.js";
import {isTokenValid} from "../middlewares/jwt.middleware.js";
import {addLogger} from "../middlewares/logger.middleware.js";
import {profileUpload} from "../middlewares/file.middleware.js";
import {Channel} from "../middlewares/channel.middleware.js";
const router = Router();
@ -49,4 +51,7 @@ router.get("/:id/channel", [addLogger, isTokenValid, User.id, validator], getCha
// GET USER HISTORY
router.get("/:id/history", [addLogger, isTokenValid, User.id, validator], getHistory);
// CHECK IF SUBSCRIBED TO CHANNEL
router.get("/:id/channel/subscribed", [addLogger, isTokenValid, User.id, Channel.id, validator], isSubscribed)
export default router;

404
backend/logs/access.log

@ -4196,3 +4196,407 @@
[2025-08-13 08:35:26.517] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 09:11:32.832] [undefined] POST(/login): try to login with username 'sacha'
[2025-08-13 09:11:32.881] [undefined] POST(/login): Successfully logged in with status 200
[2025-08-13 09:44:49.864] [undefined] GET(/:id): failed due to invalid values with status 400
[2025-08-13 09:44:49.869] [undefined] GET(/:id/stats): failed due to invalid values with status 400
[2025-08-13 09:50:37.371] [undefined] GET(/:id): Invalid token with status 401
[2025-08-13 09:50:54.834] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 09:50:54.845] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 09:51:11.325] [undefined] GET(/:id): failed to retrieve channel because it doesn't exist with status 404
[2025-08-13 09:52:08.315] [undefined] GET(/:id): failed to retrieve channel because it doesn't exist with status 404
[2025-08-13 09:52:27.562] [undefined] GET(/:id): failed to retrieve channel because it doesn't exist with status 404
[2025-08-13 09:52:43.945] [undefined] GET(/:id): failed to retrieve channel because it doesn't exist with status 404
[2025-08-13 09:52:52.674] [undefined] GET(/:id): failed to retrieve channel because it doesn't exist with status 404
[2025-08-13 09:55:55.264] [undefined] GET(/:id): failed to retrieve channel because it doesn't exist with status 404
[2025-08-13 09:55:58.016] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 09:55:58.026] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:15:34.573] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:15:34.584] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:06.997] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:07.005] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:13.919] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:13.929] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:21.290] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:21.299] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:28.301] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:28.310] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:28.981] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:28.990] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:32.974] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:32.983] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:37.751] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:37.760] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:17:45.557] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:17:45.565] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:18:17.597] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:18:17.606] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:18:36.712] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:18:36.721] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:18:41.336] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:18:41.345] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:19:18.861] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:19:18.870] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:19:41.334] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:19:41.343] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:19:46.001] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:19:46.010] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:20:06.172] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:20:06.181] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:20:27.217] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:20:27.227] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:20:35.940] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:20:35.948] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:20:59.348] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:20:59.357] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:21:44.387] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:21:44.396] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:22:16.278] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:22:16.287] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:22:31.423] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:22:31.432] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:22:43.534] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:22:43.543] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:23:28.650] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:23:28.659] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:23:37.983] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:23:37.992] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:24:03.299] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:24:03.309] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:25:13.831] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:25:13.840] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:32:43.366] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:32:43.374] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:34:16.989] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:34:16.998] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:34:45.679] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:34:45.688] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:36:15.170] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:36:15.178] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:36:29.782] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:36:29.791] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:37:17.181] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:37:17.189] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:37:17.964] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:37:17.972] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:37:26.528] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:37:26.538] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:37:40.299] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:37:40.308] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:37:54.433] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:37:54.442] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:38:20.559] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:38:20.567] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:38:38.054] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:38:38.063] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:38:55.609] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:38:55.617] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:39:22.012] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:39:22.021] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:39:26.261] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:39:26.286] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:39:33.959] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:39:33.968] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:40:12.555] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:40:12.565] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:40:43.015] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:40:43.024] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:40:56.107] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:40:56.115] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:41:04.996] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:41:05.004] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:42:02.765] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:42:02.775] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:43:11.048] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:43:11.057] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:43:45.139] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:43:45.148] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:44:51.105] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:44:51.114] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:45:11.302] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:45:11.311] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:45:28.575] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:45:28.584] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:45:45.107] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:45:45.116] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:46:10.361] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:46:10.370] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:47:15.158] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:47:15.168] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:47:34.899] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:47:34.908] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:49:46.911] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:49:46.921] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:50:30.839] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:50:30.848] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:50:44.877] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:50:44.887] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:51:01.975] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:51:01.984] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:51:19.508] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:51:19.517] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:51:33.227] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:51:33.236] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:51:49.695] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:51:49.704] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:51:59.496] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:51:59.505] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:55:23.443] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:55:23.452] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:55:26.441] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:55:26.450] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:55:39.360] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:55:39.370] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 13:56:11.330] [undefined] GET(/:id): failed to retrieve channel because it doesn't exist with status 404
[2025-08-13 13:56:12.602] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 13:56:12.612] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 14:57:50.416] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 14:58:11.475] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 14:58:11.486] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 14:58:42.394] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 14:58:42.405] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 14:59:18.079] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:00:13.921] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:00:15.269] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:00:48.623] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:00:48.633] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:01:51.284] [undefined] GET(/:id/channel): try to retrieve channel of user 2
[2025-08-13 15:01:51.287] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 15:01:51.290] [undefined] GET(/:id/history): try to retrieve history of user 2
[2025-08-13 15:01:51.294] [undefined] GET(/:id/history): successfully retrieved history of user 2 with status 200
[2025-08-13 15:01:51.301] [undefined] GET(/user/:id): Playlists retrieved for user with id 2 with status 200
[2025-08-13 15:01:52.501] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:01:52.510] [undefined] GET(/:id/stats): try to get stats
[2025-08-13 15:01:52.513] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:01:52.520] [undefined] GET(/:id/stats): Successfully get stats with status 200
[2025-08-13 15:01:56.594] [undefined] GET(/:id/channel): try to retrieve channel of user 2
[2025-08-13 15:01:56.597] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 15:02:21.627] [undefined] POST(/): try to upload video with status undefined
[2025-08-13 15:02:21.631] [undefined] POST(/): successfully uploaded video with status 200
[2025-08-13 15:02:21.713] [undefined] POST(/thumbnail): try to add thumbnail to video 2
[2025-08-13 15:02:21.716] [undefined] POST(/thumbnail): successfully uploaded thumbnail with status 200
[2025-08-13 15:02:21.738] [undefined] PUT(/:id/tags): try to add tags to video 2
[2025-08-13 15:02:21.748] [undefined] PUT(/:id/tags): successfully added tags to video 2 with status 200
[2025-08-13 15:02:28.426] [undefined] GET(/:id/channel): try to retrieve channel of user 2
[2025-08-13 15:02:28.429] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 15:02:28.688] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:02:28.696] [undefined] GET(/:id/stats): try to get stats
[2025-08-13 15:02:28.700] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:02:28.708] [undefined] GET(/:id/stats): Successfully get stats with status 200
[2025-08-13 15:02:29.859] [undefined] GET(/:id/channel): try to retrieve channel of user 2
[2025-08-13 15:02:29.861] [undefined] GET(/:id/history): try to retrieve history of user 2
[2025-08-13 15:02:29.863] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 15:02:29.867] [undefined] GET(/:id/history): successfully retrieved history of user 2 with status 200
[2025-08-13 15:02:29.874] [undefined] GET(/user/:id): Playlists retrieved for user with id 2 with status 200
[2025-08-13 15:02:30.251] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:02:30.260] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:03:24.046] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:03:24.055] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:03:30.209] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:03:30.219] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:03:36.600] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:03:36.609] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:03:38.416] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:03:38.425] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:03:40.864] [undefined] GET(/:id/channel): try to retrieve channel of user 2
[2025-08-13 15:03:40.867] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 15:03:40.869] [undefined] GET(/:id/history): try to retrieve history of user 2
[2025-08-13 15:03:40.873] [undefined] GET(/:id/history): successfully retrieved history of user 2 with status 200
[2025-08-13 15:03:40.879] [undefined] GET(/user/:id): Playlists retrieved for user with id 2 with status 200
[2025-08-13 15:03:45.805] [undefined] GET(/:id/channel): try to retrieve channel of user 2
[2025-08-13 15:03:45.808] [undefined] GET(/:id/history): try to retrieve history of user 2
[2025-08-13 15:03:45.815] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 15:03:45.818] [undefined] GET(/:id/history): successfully retrieved history of user 2 with status 200
[2025-08-13 15:03:45.825] [undefined] GET(/user/:id): Playlists retrieved for user with id 2 with status 200
[2025-08-13 15:04:06.758] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:04:06.766] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:04:44.290] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:04:44.299] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:04:52.067] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:04:52.077] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:05:14.138] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:05:14.147] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 15:51:45.615] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 15:51:45.623] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:16:59.269] [undefined] GET(/:id/channel): try to retrieve channel of user 2
[2025-08-13 16:16:59.272] [undefined] GET(/:id/channel): successfully retrieved channel of user 2 with status 200
[2025-08-13 16:16:59.274] [undefined] GET(/:id/history): try to retrieve history of user 2
[2025-08-13 16:16:59.277] [undefined] GET(/:id/history): successfully retrieved history of user 2 with status 200
[2025-08-13 16:16:59.283] [undefined] GET(/user/:id): Playlists retrieved for user with id 2 with status 200
[2025-08-13 16:17:06.262] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:17:06.271] [undefined] GET(/:id/stats): try to get stats
[2025-08-13 16:17:06.274] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:17:06.282] [undefined] GET(/:id/stats): Successfully get stats with status 200
[2025-08-13 16:17:12.950] [undefined] GET(/:id): try to get video 1
[2025-08-13 16:17:12.953] [undefined] GET(/:id/likes/day): try to get likes per day
[2025-08-13 16:17:12.962] [undefined] GET(/:id/likes/day): successfully retrieved likes per day with status 200
[2025-08-13 16:17:12.966] [undefined] GET(/:id): successfully get video 1 with status 200
[2025-08-13 16:17:29.535] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:17:29.544] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:49:34.721] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:49:34.733] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:50:12.390] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:50:12.400] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:50:22.222] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:50:22.231] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:50:54.583] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:50:54.592] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:51:09.969] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:51:09.979] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:51:10.825] [undefined] POST(/:id/subscribe): Invalid token with status 401
[2025-08-13 16:51:51.895] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 16:51:51.904] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 16:51:52.948] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 16:52:06.440] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:04:52.666] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:04:52.676] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:04:53.510] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:05:27.084] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:05:27.093] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:05:29.754] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:06:34.743] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:06:34.752] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:06:36.865] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:07:17.244] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:07:17.253] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:07:18.931] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:07:42.365] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:09:08.819] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:09:08.830] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:09:09.561] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:09:09.569] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:09:15.011] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:09:15.020] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:09:17.626] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:09:17.636] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:09:18.685] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:09:18.694] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:09:19.297] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:09:19.307] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:09:20.241] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:09:20.249] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:09:20.865] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:09:20.875] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:11:41.315] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:11:41.322] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:14:10.866] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:14:10.877] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:14:12.477] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:14:12.486] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:14:14.578] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:14:14.588] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:19:22.939] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:19:22.950] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:19:33.535] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:19:33.545] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:21:08.100] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:21:08.113] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:21:41.263] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:21:41.272] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:21:41.293] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:22:21.218] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:22:21.230] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:22:21.253] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:22:21.255] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:24:13.878] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:24:13.888] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:24:13.911] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:24:13.915] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:24:21.570] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:24:21.579] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:24:21.613] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:24:21.616] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:24:54.126] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:24:54.135] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:24:54.174] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:24:54.177] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:25:05.451] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:25:05.460] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:25:05.478] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:25:05.481] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:25:06.220] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:25:06.230] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:25:06.244] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:25:06.248] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:25:28.777] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:25:28.787] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:25:28.803] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:25:28.806] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:25:30.067] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:25:30.076] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:25:30.620] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:25:30.629] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:25:30.647] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:25:30.650] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:25:31.978] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:25:31.986] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:25:32.000] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:25:32.003] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:25:32.434] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:25:32.443] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:25:32.458] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:25:32.461] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:25:49.395] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:25:49.404] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:25:49.423] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:25:49.426] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:26:15.610] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:26:15.619] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:26:15.637] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:26:15.639] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:26:29.494] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:26:29.503] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:26:32.400] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:26:32.408] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:26:32.429] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:26:32.431] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:26:41.304] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:26:41.315] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:26:41.333] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:26:41.336] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:26:44.237] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:26:44.246] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:26:45.215] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:26:45.224] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:26:45.249] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:26:45.251] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:27:01.095] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:27:01.105] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:27:01.127] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:27:01.130] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:27:02.190] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:27:02.198] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:27:02.987] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:27:02.996] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:27:03.012] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:27:03.014] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:27:04.543] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:27:04.552] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:27:05.236] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:27:05.246] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:27:05.262] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:27:05.265] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:27:54.925] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:27:54.935] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:27:54.964] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:27:54.967] [undefined] GET(/:id/channel/subscribed): user 2 is not subscribed to channel 1 with status 200
[2025-08-13 17:27:55.768] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:27:55.777] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:30:22.936] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:30:22.945] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:30:22.964] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:30:22.967] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:30:24.066] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:30:24.075] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:30:25.190] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:30:25.198] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:30:33.111] [undefined] GET(/:id): try to get channel with id 1
[2025-08-13 17:30:33.121] [undefined] GET(/:id): Successfully get channel with id 1 with status 200
[2025-08-13 17:30:33.139] [undefined] GET(/:id/channel/subscribed): check if user 2 is subscribed to channel 1
[2025-08-13 17:30:33.142] [undefined] GET(/:id/channel/subscribed): user 2 is subscribed to channel 1 with status 200
[2025-08-13 17:30:33.992] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:30:34.001] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:30:34.617] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:30:34.626] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200
[2025-08-13 17:30:37.582] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:30:37.590] [undefined] POST(/:id/subscribe): Successfully unsubscribed from channel with status 200
[2025-08-13 17:30:39.032] [undefined] POST(/:id/subscribe): try to toggle subscription for channel with id 1
[2025-08-13 17:30:39.041] [undefined] POST(/:id/subscribe): Successfully subscribed to channel with status 200

0
frontend/src/components/ChannelLastVideo.jsx

20
frontend/src/components/ChannelLastVideos.jsx

@ -0,0 +1,20 @@
import VideoCard from "./VideoCard.jsx";
export default function ChannelLastVideos({ videos }) {
return (
<div className="grid grid-cols-4 gap-8">
{
videos && videos.length > 0 ? (
videos.map((video) => (
<VideoCard video={video} />
)
)
) : (
<p>Aucune vidéo trouvée</p>
)
}
</div>
)
}

43
frontend/src/components/TabLayout.jsx

@ -0,0 +1,43 @@
import React, { useState } from 'react';
export default function TabLayout({ tabs }) {
const [activeTab, setActiveTab] = useState(tabs[0].id);
const onTabChange = (tabId) => {
setActiveTab(tabId);
};
return (
<div>
{/* TABS */}
<div className='flex items-center mt-8 gap-3 mb-4' >
{
tabs.map((tab) => (
<button
key={tab.id}
onClick={() => onTabChange(tab.id)}
className={` px-4 py-2 font-montserrat font-medium text-lg cursor-pointer ${tab.id === activeTab ? 'bg-white text-black rounded-2xl border-2 border-white' : 'glassmorphism text-white'}`}
>
{tab.label}
</button>
))
}
</div>
{/* ELEMENT */}
<div className="glassmorphism w-full p-4">
{tabs.map((tab) => (
<div key={tab.id} className={`tab-content ${tab.id === activeTab ? 'block' : 'hidden'}`}>
{tab.element()}
</div>
))}
</div>
</div>
)
}

26
frontend/src/components/VideoCard.jsx

@ -1,5 +1,31 @@
import { useNavigate } from 'react-router-dom';
// SUPPORTED JSON FORMAT
// [
// {
// "id": 1,
// "title": "Video minecraft",
// "thumbnail": "/api/media/thumbnail/78438E11ABA5D0C8.webp",
// "video_description": "Cest une super video minecraft",
// "channel": 1,
// "visibility": "public",
// "file": "/api/media/video/78438E11ABA5D0C8.mp4",
// "slug": "78438E11ABA5D0C8",
// "format": "mp4",
// "release_date": "2025-08-11T11:14:01.357Z",
// "channel_id": 1,
// "owner": 2,
// "views": "2",
// "creator": {
// "name": "astria",
// "profilePicture": "/api/media/profile/sacha.jpg",
// "description": "salut tout le monde"
// },
// "type": "video"
// }
// ]
export default function VideoCard({ video }) {
const navigation = useNavigate();
const handleClick = () => {

16
frontend/src/index.css

@ -38,6 +38,22 @@
backdrop-filter: blur(27.5px);
}
.glassmorphism-top-round {
border-top-left-radius: 15px;
border-top-right-radius: 15px;
border: 1px solid rgba(239, 239, 239, 0.60);
background: linear-gradient(93deg, rgba(239, 239, 239, 0.06) 0%, rgba(239, 239, 239, 0.01) 100%);
backdrop-filter: blur(27.5px);
}
.glassmorphism-bottom-round {
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
border: 1px solid rgba(239, 239, 239, 0.60);
background: linear-gradient(93deg, rgba(239, 239, 239, 0.06) 0%, rgba(239, 239, 239, 0.01) 100%);
backdrop-filter: blur(27.5px);
}
.resizable-none {
resize: none;
}

113
frontend/src/pages/Channel.jsx

@ -0,0 +1,113 @@
import Navbar from "../components/Navbar.jsx";
import {useEffect, useState} from "react";
import {useParams} from "react-router-dom";
import {fetchChannelDetails, subscribe} from "../services/channel.service.js";
import TabLayout from "../components/TabLayout.jsx";
import ChannelLastVideos from "../components/ChannelLastVideos.jsx";
import { isSubscribed } from "../services/user.service.js";
export default function Channel() {
const id = useParams().id;
const [alerts, setAlerts] = useState([]);
const [channel, setChannel] = useState(null);
const [isSubscribedToChannel, setIsSubscribedToChannel] = useState(false);
const tabs = [
{ id: 'last', label: 'Dernières vidéos', element: () => <ChannelLastVideos videos={channel && channel.videos.slice(0, 10)} /> },
{ id: 'all', label: 'Toutes les vidéos', element: () => <ChannelLastVideos videos={channel && channel.videos} /> },
];
useEffect(() => {
async function fetchData() {
const chan = await fetchChannelDetails(id, addAlert);
setChannel(chan);
// If not authenticated, isSubscribed may be undefined -> default to false
const subscribed = await isSubscribed(id, addAlert);
setIsSubscribedToChannel(Boolean(subscribed));
}
fetchData();
}, [id])
const addAlert = (type, message) => {
const newAlert = { type, message, id: Date.now() }; // Add unique ID
setAlerts(prev => [...prev, newAlert]);
};
const onCloseAlert = (alertToRemove) => {
setAlerts(alerts.filter(alert => alert !== alertToRemove));
}
const handleSubscribe = async () => {
try {
const result = await subscribe(id, addAlert);
// Update local counter from API response
const newCount = Number(result?.subscriptions ?? (channel?.subscriptions ?? 0));
setChannel(prev => (prev ? { ...prev, subscriptions: newCount } : prev));
// Toggle local subscription state and notify
const next = !isSubscribedToChannel;
setIsSubscribedToChannel(next);
} catch (e) {
// Error alert already handled in service
}
};
return (
<div className="min-w-screen min-h-screen bg-linear-to-br from-left-gradient to-right-gradient">
<Navbar isSearchPage={false} alerts={alerts} onCloseAlert={onCloseAlert} />
<main className="pt-[118px] px-36" >
{/* Channel Header */}
<div className="glassmorphism p-4" >
<div className="flex items-center gap-4" >
<img
src={channel && channel.picture}
alt={channel && channel.name}
className="w-[192px] aspect-square object-cover rounded-full border-2 border-white"
/>
<div>
<h1 className="font-montserrat font-bold text-3xl text-white" >{channel && channel.name}</h1>
<p className="font-montserrat font-medium text-xl text-white" >{channel && channel.subscriptions} abonné(es)</p>
</div>
{
isSubscribedToChannel ? (
<button
className="ml-5 bg-primary text-white font-montserrat font-bold px-4 py-2 rounded-md cursor-pointer"
onClick={handleSubscribe}
>
se désabonner
</button>
) : (
<button
className="ml-5 bg-primary text-white font-montserrat font-bold px-4 py-2 rounded-md cursor-pointer"
onClick={handleSubscribe}
>
s'abonner
</button>
)
}
</div>
<h2 className="font-bold font-montserrat text-white text-xl mt-4" >Description</h2>
<p className="text-white text-lg font-medium font-montserrat pl-[24px]">
{ channel && channel.description }
</p>
</div>
{/* Tab selector */}
<TabLayout tabs={tabs}/>
{/* 10 Last videos */}
</main>
</div>
)
}

17
frontend/src/routes/routes.jsx

@ -8,6 +8,7 @@ import ManageChannel from "../pages/ManageChannel.jsx";
import ManageVideo from "../pages/ManageVideo.jsx";
import AddVideo from "../pages/AddVideo.jsx";
import Search from "../pages/Search.jsx";
import Channel from "../pages/Channel.jsx";
const routes = [
{ path: "/", element: <Home/> },
@ -29,7 +30,11 @@ const routes = [
},
{
path: "/video/:id",
element: <Video />
element: (
<ProtectedRoute requireAuth={false}>
<Video />
</ProtectedRoute>
)
},
{
path: "/profile",
@ -65,7 +70,15 @@ const routes = [
},
{
path: "search",
element: <Search />
element: (
<Search />
)
},
{
path: "channel/:id",
element: (
<Channel/>
)
}
]

41
frontend/src/services/channel.service.js

@ -0,0 +1,41 @@
export function fetchChannelDetails(channelId, addAlert) {
return fetch(`/api/channels/${channelId}`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.catch(error => {
addAlert('error', "Erreur lors de la récupération des détails de la chaîne");
throw error;
});
}
export async function subscribe(channelId, addAlert) {
const token = localStorage.getItem('token');
const user = JSON.parse(localStorage.getItem('user'));
console.log("Subscribing to channel with ID:", channelId, "for user:", user.id);
return fetch(`/api/channels/${channelId}/subscribe`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': "application/json"
},
body: JSON.stringify({
userId: user.id
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.catch(error => {
addAlert('error', "Erreur lors de l'abonnement à la chaîne");
throw error;
});
}

18
frontend/src/services/user.service.js

@ -0,0 +1,18 @@
export async function isSubscribed(channelId, addAlert) {
const token = localStorage.getItem('token');
if (!token) {
return;
}
const headers = {
Authorization: `Bearer ${token}`
};
const request = await fetch(`/api/users/${channelId}/channel/subscribed`, { headers })
const result = await request.json();
console.log("Subscription status for channel ID", channelId, ":", result);
return result.subscribed;
}
Loading…
Cancel
Save