Skip to content
Snippets Groups Projects
Commit 37797c70 authored by 문경호's avatar 문경호
Browse files

Fix: Fix Routine API

parent de8e7655
Branches
No related tags found
No related merge requests found
Pipeline #10923 failed
const express = require('express');
const router = express.Router();
const minutesToSeconds = require('../utils/timeconvert'); const minutesToSeconds = require('../utils/timeconvert');
//운동 영상 구조
const Video = require('../models/video');
//루틴 구조
const Routine = require('../models/routine'); const Routine = require('../models/routine');
// 기록할 DB 구조 const Video = require('../models/video');
const Record = require('../models/records');
const routineController = { const routineController = {
recordRoutine: async (req, res) => { recordRoutine: async (req, res) => {
...@@ -30,33 +23,30 @@ const routineController = { ...@@ -30,33 +23,30 @@ const routineController = {
getRoutine: async (req, res) => { getRoutine: async (req, res) => {
try { try {
const userRoutine = await Routine.find({ const userRoutine = await Routine.find({
user_id: req.user.user_id, user_id: req.user.user_id
}).populate('routine_exercises.video'); }).populate('routine_exercises.video').exec();
if (userRoutine.length === 0) { if (userRoutine.length === 0) {
// 아무런 루틴도 없다면 null 로 채워진 루틴 하나를 return const dummyRoutine = [{
const dummyRoutine = [
{
routine_id: null, routine_id: null,
routine_name: null, routine_name: null,
routine_exercises: [ routine_exercises: [{
{
video: { video: {
video_id: null, video_id: null,
video_time: null, video_time: null,
video_tag: null, video_tag: null,
}, },
}, }],
], }];
},
];
return res.json(dummyRoutine); return res.json(dummyRoutine);
} }
return res.status(200).json(userRoutine); return res.status(200).json(userRoutine);
} catch (error) { } catch (error) {
console.error(error); console.error('Routine Error:', error);
res.status(500).json({ res.status(500).json({
message: 'Failed to get user routine', message: 'Failed to get user routine',
error: error.message, error: error.message
}); });
} }
}, },
...@@ -161,6 +151,32 @@ const routineController = { ...@@ -161,6 +151,32 @@ const routineController = {
}); });
} }
}, },
getRoutineVideos: async (req, res) => {
const routine_name = req.query.routine_name; // GET 요청의 쿼리 파라미터
try {
const routine = await Routine.findOne({
user_id: req.user.user_id,
routine_name,
}).populate('routine_exercises.video');
if (!routine) {
return res.status(404).json({ message: 'Routine not Found' });
}
// video 정보만 추출하여 반환
const videos = routine.routine_exercises
.map(exercise => exercise.video)
.filter(video => video); // null/undefined 필터링
res.status(200).json(videos);
} catch (error) {
console.error(error);
res.status(500).json({
message: 'Failed to get routine videos',
error: error.message,
});
}
},
}; };
module.exports = routineController; module.exports = routineController;
\ No newline at end of file
...@@ -30,5 +30,4 @@ const routineSchema = new mongoose.Schema( ...@@ -30,5 +30,4 @@ const routineSchema = new mongoose.Schema(
); );
const Routine = mongoose.model('Routine', routineSchema); const Routine = mongoose.model('Routine', routineSchema);
module.exports = Routine;
module.exports = { Routine };
async function fetchWithOptions(url, options) { async function fetchWithOptions(url, options) {
try { try {
const response = await fetch(url, options); const response = await fetch(url, {
...options,
headers: {
"Authorization": `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP 오류! 상태 코드: ${response.status}`); const errorData = await response.json();
throw new Error(errorData.message || `HTTP 오류! 상태 코드: ${response.status}`);
} }
return await response.json(); return await response.json();
} catch (error) { } catch (error) {
console.error("API 요청 중 에러 발생:", error.message); console.error("API 요청 중 에러 발생:", error.message);
...@@ -11,44 +21,62 @@ async function fetchWithOptions(url, options) { ...@@ -11,44 +21,62 @@ async function fetchWithOptions(url, options) {
} }
} }
async function addExerciseRecord(record) { // 개별 함수들을 export
return await fetchWithOptions('/routine/records', { export const addExerciseRecord = async (record) => {
return await fetchWithOptions('/api/routine/records', {
method: 'POST', method: 'POST',
headers: {
"Authorization": `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(record), body: JSON.stringify(record),
}); });
} };
async function getUserRoutines() { export const getUserRoutines = async () => {
return await fetchWithOptions('/routine', { try {
const response = await fetchWithOptions('/api/routine', {
method: 'GET', method: 'GET',
headers: {
"Authorization": `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
}); });
// 빈 루틴도 표시할 수 있도록 변환
return response.map(routine => ({
_id: routine._id,
user_id: routine.user_id,
routine_name: routine.routine_name,
routine_exercises: routine.routine_exercises || [], // 빈 배열 처리
created_at: routine.routine_created_at
}));
} catch (error) {
console.error("루틴 데이터 가져오기 실패:", error);
throw error;
} }
};
async function getRoutineVideos(routineName) { export const getRoutineVideos = async (routineName) => {
return await fetchWithOptions(`/routine/videos?routine_name=${routineName}`, { try {
const response = await fetchWithOptions(`/api/routine/videos?routine_name=${encodeURIComponent(routineName)}`, {
method: 'GET', method: 'GET',
headers: {
"Authorization": `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
}); });
return response.map(video => ({
title: video.video_title || '',
duration: video.video_time || 0,
link: video.video_url || '',
thumbnail: video.video_thumbnail || ''
}));
} catch (error) {
console.error("루틴 비디오 가져오기 실패:", error);
return []; // 비디오가 없을 경우 빈 배열 반환
} }
};
async function deleteRoutine(routineName) { export const createRoutine = async (routineName) => {
return await fetchWithOptions('/routine', { return await fetchWithOptions('/api/routine', {
method: 'POST',
body: JSON.stringify({ routine_name: routineName }),
});
};
export const deleteRoutine = async (routineName) => {
return await fetchWithOptions('/api/routine', {
method: 'DELETE', method: 'DELETE',
headers: {
"Authorization": `Bearer ${localStorage.getItem('accessToken')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ routine_name: routineName }), body: JSON.stringify({ routine_name: routineName }),
}); });
} }
......
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import "./List.css"; import "./List.css";
import truncateText from './truncateText'; import truncateText from './truncateText';
import { getUserRoutines, getRoutineVideos, deleteRoutine } from '../../api/routineAPI'; import routineAPI from '../../api/routineAPI';
const initialRoutines = [
{
id: 1,
name: "전신 루틴",
exercises: [
{ title: "전신 운동 1", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail1", link: "https://www.youtube.com/watch?v=example1", duration: 300, canceled: false },
{ title: "전신 운동 2", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail2", link: "https://www.youtube.com/watch?v=example2", duration: 420, canceled: false },
{ title: "전신 운동 3", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail3", link: "https://www.youtube.com/watch?v=example3", duration: 350, canceled: false },
],
},
{
id: 2,
name: "상체 루틴",
exercises: [
{ title: "상체 운동 1", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail1", link: "https://www.youtube.com/watch?v=example11", duration: 360, canceled: false },
{ title: "상체 운동 2", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail2", link: "https://www.youtube.com/watch?v=example12", duration: 400, canceled: false },
{ title: "상체 운동 3", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail3", link: "https://www.youtube.com/watch?v=example13", duration: 330, canceled: false },
{ title: "상체 운동 4", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail4", link: "https://www.youtube.com/watch?v=example14", duration: 470, canceled: false },
{ title: "상체 운동 5", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail5", link: "https://www.youtube.com/watch?v=example15", duration: 380, canceled: false },
{ title: "상체 운동 6", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail6", link: "https://www.youtube.com/watch?v=example16", duration: 390, canceled: false },
{ title: "상체 운동 7", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail7", link: "https://www.youtube.com/watch?v=example17", duration: 420, canceled: false },
{ title: "상체 운동 8", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail8", link: "https://www.youtube.com/watch?v=example18", duration: 440, canceled: false },
{ title: "상체 운동 9", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail9", link: "https://www.youtube.com/watch?v=example19", duration: 350, canceled: false },
{ title: "상체 운동 10", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail10", link: "https://www.youtube.com/watch?v=example20", duration: 500, canceled: false },
],
},
{
id: 3,
name: "하체 루틴",
exercises: [
{ title: "하체 운동 1", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail1", link: "https://www.youtube.com/watch?v=example21", duration: 350, canceled: false },
{ title: "하체 운동 2", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail2", link: "https://www.youtube.com/watch?v=example22", duration: 450, canceled: false },
{ title: "하체 운동 3", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail3", link: "https://www.youtube.com/watch?v=example23", duration: 400, canceled: false },
{ title: "하체 운동 4", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail4", link: "https://www.youtube.com/watch?v=example24", duration: 500, canceled: false },
{ title: "하체 운동 5", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail5", link: "https://www.youtube.com/watch?v=example25", duration: 480, canceled: false },
{ title: "하체 운동 6", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail6", link: "https://www.youtube.com/watch?v=example26", duration: 420, canceled: false },
{ title: "하체 운동 7", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail7", link: "https://www.youtube.com/watch?v=example27", duration: 430, canceled: false },
{ title: "하체 운동 8", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail8", link: "https://www.youtube.com/watch?v=example28", duration: 360, canceled: false },
{ title: "하체 운동 9", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail9", link: "https://www.youtube.com/watch?v=example29", duration: 490, canceled: false },
{ title: "하체 운동 10", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail10", link: "https://www.youtube.com/watch?v=example30", duration: 460, canceled: false },
],
},
{
id: 4,
name: "복부 루틴",
exercises: [
{ title: "복부 운동 1", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail1", link: "https://www.youtube.com/watch?v=example31", duration: 400, canceled: false },
{ title: "복부 운동 2", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail2", link: "https://www.youtube.com/watch?v=example32", duration: 300, canceled: false },
{ title: "복부 운동 3", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail3", link: "https://www.youtube.com/watch?v=example33", duration: 350, canceled: false },
{ title: "복부 운동 4", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail4", link: "https://www.youtube.com/watch?v=example34", duration: 380, canceled: false },
{ title: "복부 운동 5", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail5", link: "https://www.youtube.com/watch?v=example35", duration: 320, canceled: false },
{ title: "복부 운동 6", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail6", link: "https://www.youtube.com/watch?v=example36", duration: 370, canceled: false },
{ title: "복부 운동 7", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail7", link: "https://www.youtube.com/watch?v=example37", duration: 390, canceled: false },
{ title: "복부 운동 8", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail8", link: "https://www.youtube.com/watch?v=example38", duration: 430, canceled: false },
{ title: "복부 운동 9", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail9", link: "https://www.youtube.com/watch?v=example39", duration: 480, canceled: false },
{ title: "복부 운동 10", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail10", link: "https://www.youtube.com/watch?v=example40", duration: 500, canceled: false },
],
},
{
id: 5,
name: "유산소 루틴",
exercises: [
{ title: "유산소 운동 1", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail1", link: "https://www.youtube.com/watch?v=example41", duration: 600, canceled: false },
{ title: "유산소 운동 2", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail2", link: "https://www.youtube.com/watch?v=example42", duration: 500, canceled: false },
{ title: "유산소 운동 3", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail3", link: "https://www.youtube.com/watch?v=example43", duration: 550, canceled: false },
{ title: "유산소 운동 4", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail4", link: "https://www.youtube.com/watch?v=example44", duration: 510, canceled: false },
{ title: "유산소 운동 5", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail5", link: "https://www.youtube.com/watch?v=example45", duration: 560, canceled: false },
{ title: "유산소 운동 6", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail6", link: "https://www.youtube.com/watch?v=example46", duration: 530, canceled: false },
{ title: "유산소 운동 7", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail7", link: "https://www.youtube.com/watch?v=example47", duration: 480, canceled: false },
{ title: "유산소 운동 8", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail8", link: "https://www.youtube.com/watch?v=example48", duration: 540, canceled: false },
{ title: "유산소 운동 9", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail9", link: "https://www.youtube.com/watch?v=example49", duration: 490, canceled: false },
{ title: "유산소 운동 10", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail10", link: "https://www.youtube.com/watch?v=example50", duration: 520, canceled: false },
],
},
{
id: 6,
name: "스트레칭 루틴",
exercises: [
{ title: "스트레칭 운동 1", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail1", link: "https://www.youtube.com/watch?v=example51", duration: 300, canceled: false },
{ title: "스트레칭 운동 2", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail2", link: "https://www.youtube.com/watch?v=example52", duration: 400, canceled: false },
{ title: "스트레칭 운동 3", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail3", link: "https://www.youtube.com/watch?v=example53", duration: 350, canceled: false },
{ title: "스트레칭 운동 4", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail4", link: "https://www.youtube.com/watch?v=example54", duration: 300, canceled: false },
{ title: "스트레칭 운동 5", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail5", link: "https://www.youtube.com/watch?v=example55", duration: 380, canceled: false },
{ title: "스트레칭 운동 6", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail6", link: "https://www.youtube.com/watch?v=example56", duration: 420, canceled: false },
{ title: "스트레칭 운동 7", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail7", link: "https://www.youtube.com/watch?v=example57", duration: 440, canceled: false },
{ title: "스트레칭 운동 8", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail8", link: "https://www.youtube.com/watch?v=example58", duration: 450, canceled: false },
{ title: "스트레칭 운동 9", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail9", link: "https://www.youtube.com/watch?v=example59", duration: 500, canceled: false },
{ title: "스트레칭 운동 10", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail10", link: "https://www.youtube.com/watch?v=example60", duration: 480, canceled: false },
],
},
{
id: 7,
name: "등 루틴",
exercises: [
{ title: "업그레이드를 위한 새로운 등운동 [ BACK DAY ]", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail1", link: "https://www.youtube.com/watch?v=f7wFbp9BnFs", duration: 300, canceled: false },
{ title: "뚫고 나오는 등 만들고 싶으면 이거 봐", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail2", link: "https://www.youtube.com/watch?v=8SBBp65Sv3c", duration: 400, canceled: false },
{ title: "Try This Back Exercise | Back & Hamstrings Workout", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail3", link: "https://www.youtube.com/watch?v=nRzAV-CYndA", duration: 350, canceled: false },
{ title: "My Title Winning Back Training", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail4", link: "https://www.youtube.com/watch?v=5dp2FUN3mRQ", duration: 300, canceled: false },
{ title: "Back and Delts Workout", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail5", link: "https://www.youtube.com/watch?v=DjfnFj-50b4", duration: 380, canceled: false },
{ title: "INTENSE Back Workout | Mr. Olympia Derek Lunsford", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail6", link: "https://www.youtube.com/watch?v=HYngFKG5YbY&t=477s", duration: 420, canceled: false },
{ title: "Mr. Olympia BACK WORKOUT", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail7", link: "https://www.youtube.com/watch?v=HqZOWPRyck8&t=134s", duration: 440, canceled: false },
{ title: "등 운동 후 몽둥이질 당한 느낌이 나게 하는 방법들", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail8", link: "https://www.youtube.com/watch?v=OD1JMTLJp-A", duration: 450, canceled: false },
{ title: "등 운동 하는 날 반드시 시청해야 할 영상 ( feat.등 운동 루틴 풀버전 ) 등 운동 하는 날 반드시 시청해야 할 영상 ( feat.등 운동 루틴 풀버전 ) 등 운동 하는 날 반드시 시청해야 할 영상 ( feat.등 운동 루틴 풀버전 ) 등 운동 하는 날 반드시 시청해야 할 영상 ( feat.등 운동 루틴 풀버전 ) 등 운동 하는 날 반드시 시청해야 할 영상 ( feat.등 운동 루틴 풀버전 ) 등 운동 하는 날 반드시 시청해야 할 영상 ( feat.등 운동 루틴 풀버전 ) 등 운동 하는 날 반드시 시청해야 할 영상 ( feat.등 운동 루틴 풀버전 ) ", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail9", link: "https://www.youtube.com/watch?v=naxGvgl9pKg&t=1102s", duration: 500, canceled: false },
{ title: "요즘 유행하는 등 운동 루틴", thumbnail: "https://via.placeholder.com/150x100.png?text=Thumbnail10", link: "https://www.youtube.com/watch?v=XLCtwqECMrs&t=279s", duration: 480, canceled: false },
],
},
];
function List({ onRoutineSelect, isActive }) { function List({ onRoutineSelect, isActive }) {
const [routines, setRoutines] = useState([]);
const [routines, setRoutines] = useState(initialRoutines); // 루틴 목록 const [selectedRoutine, setSelectedRoutine] = useState(null);
const [selectedRoutine, setSelectedRoutine] = useState(null); // 선택한 루틴
const [modify, setModify] = useState(false); const [modify, setModify] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
...@@ -149,30 +40,30 @@ function List({ onRoutineSelect, isActive }) { ...@@ -149,30 +40,30 @@ function List({ onRoutineSelect, isActive }) {
cursor: 'pointer', cursor: 'pointer',
}; };
// 루틴 데이터와 비디오 데이터 함께 가져오기
const fetchRoutines = async () => { const fetchRoutines = async () => {
try { try {
const data = await getUserRoutines(); const routineData = await routineAPI.getUserRoutines();
fetchExercises(data); if (routineData) {
} catch (err) { // 각 루틴에 대해 비디오 정보 가져오기
alert(err); const routinesWithVideos = await Promise.all(
} routineData.map(async (routine) => {
const videos = await routineAPI.getRoutineVideos(routine.routine_name);
return {
...routine,
exercises: videos.map(video => ({
title: video.video_title,
duration: video.video_time,
link: video.video_url,
thumbnail: video.video_thumbnail
}))
}; };
const fetchExercises = async (routines) => {
try {
const updatedRoutines = await Promise.all(
routines.map(async (rt) => {
const data = await getRoutineVideos(rt.name);
const updatedExercises = data.map((exercise) => ({
...exercise,
canceled: false,
}));
return { ...rt, exercises: updatedExercises };
}) })
); );
setRoutines(updatedRoutines); setRoutines(routinesWithVideos);
}
} catch (err) { } catch (err) {
alert(err); console.error("루틴 데이터 가져오기 실패:", err);
} }
}; };
...@@ -180,61 +71,63 @@ useEffect(() => { ...@@ -180,61 +71,63 @@ useEffect(() => {
fetchRoutines(); fetchRoutines();
}, []); }, []);
/*
루틴 목록에서 원하는 루틴 클릭 시 해당 루틴을 selectedRoutine으로 설정하여
해당 루틴 운동 목록화면으로 전환
*/
const handleRoutineClick = (routine) => { const handleRoutineClick = (routine) => {
if (!modify) setSelectedRoutine(routine); if (!modify) {
const formattedRoutine = {
name: routine.routine_name,
exercises: []
};
setSelectedRoutine(formattedRoutine);
onRoutineSelect(formattedRoutine);
}
}; };
/*
뒤로가기 클릭 시 selectedRoutine 값을 null로 설정하여 루틴 목록화면으로 전환
*/
const handleBackClick = () => { const handleBackClick = () => {
setSelectedRoutine(null); setSelectedRoutine(null);
}; };
const handledelete = async (name) => { const handledelete = async (name) => {
try { try {
await deleteRoutine(name); await routineAPI.deleteRoutine(name);
setIsModalOpen(false); setIsModalOpen(false);
await fetchRoutines(); // 삭제 후 목록 새로고침
} catch (err) { } catch (err) {
alert(err); alert(err);
} }
}; };
/* const handleAddRoutine = async () => {
토글 클릭 시 해당 운동의 루틴 제외 여부 설정 try {
*/ const routineName = prompt("새로운 루틴의 이름을 입력하세요:");
const toggleCancelExercise = (exercise) => { if (!routineName) return;
const updatedExercises = selectedRoutine.exercises.map((ex) =>
ex.title === exercise.title ? { ...ex, canceled: !ex.canceled } : ex await routineAPI.createRoutine(routineName);
); await fetchRoutines();
setSelectedRoutine({ ...selectedRoutine, exercises: updatedExercises }); } catch (err) {
console.error("루틴 추가 실패:", err);
alert("루틴 추가에 실패했습니다.");
}
}; };
return ( return (
<div id="list-container"> <div id="list-container">
{!selectedRoutine ? ( {!selectedRoutine ? (
<div className="list-head"> <div className="list-head">
{/*selectedRoutine이 null로 루틴 목록 화면 표시*/}
<div> <div>
<span>routine</span> <span>routine</span>
<div className="division-line"></div> <div className="division-line"></div>
</div> </div>
<div id="list-content"> <div id="list-content">
<ul> <ul>
{routines.map((routine) => ( {routines && routines.length > 0 ? (
<li key={routine.id} onClick={() => handleRoutineClick(routine)}> routines.map((routine) => (
<span>{truncateText(routine.name, 10)}</span> <li key={routine._id} onClick={() => handleRoutineClick(routine)}>
<span>{truncateText(routine.routine_name, 10)}</span>
{modify && ( {modify && (
<button onClick={(e) => { <button onClick={(e) => {
e.stopPropagation(); // 이벤트 전파 중단 e.stopPropagation();
setIsModalOpen(true); setIsModalOpen(true);
}}>X</button> }}>X</button>
)} )}
{isModalOpen && ( {isModalOpen && (
<div style={modalStyle}> <div style={modalStyle}>
...@@ -242,7 +135,7 @@ return ( ...@@ -242,7 +135,7 @@ return (
<div> <div>
루틴을 삭제하겠습니까? 루틴을 삭제하겠습니까?
<div> <div>
<button style={buttonStyle} onClick={() => handledelete(routine.title)}>삭제</button> <button style={buttonStyle} onClick={() => handledelete(routine.routine_name)}>삭제</button>
<button style={buttonStyle} onClick={() => setIsModalOpen(false)}>취소</button> <button style={buttonStyle} onClick={() => setIsModalOpen(false)}>취소</button>
</div> </div>
</div> </div>
...@@ -250,49 +143,54 @@ return ( ...@@ -250,49 +143,54 @@ return (
</div> </div>
)} )}
</li> </li>
))} ))
) : (
<li className="no-routine-message">
<span>루틴이 없습니다. 새로운 루틴을 추가해주세요.</span>
</li>
)}
</ul> </ul>
{!modify ? (<button className='back' onClick={() => setModify(true)}>수정</button> <div className="list-foot">
) : (<button className='back' onClick={() => setModify(false)}>뒤로가기</button>)} {!modify ? (
<button className='back' onClick={() => setModify(true)}>수정</button>
) : (
<>
<button className='add' onClick={handleAddRoutine}>추가</button>
<button className='back' onClick={() => setModify(false)}>뒤로가기</button>
</>
)}
</div>
</div> </div>
</div> </div>
) : ( ) : (
<div className="list-head"> <div className="list-head">
{/*selectedRoutine이 설정 되어 운동 목록 화면 표시*/}
<div> <div>
<span>{selectedRoutine.name}</span> <span>{selectedRoutine.name}</span>
<div className="division-line"></div> <div className="division-line"></div>
</div> </div>
<div id="list-content"> <div id="list-content">
<ul> <ul>
{selectedRoutine.exercises.map((exercise, index) => ( {selectedRoutine.exercises && selectedRoutine.exercises.length > 0 ? (
selectedRoutine.exercises.map((exercise, index) => (
<li key={index}> <li key={index}>
<span <span>{truncateText(exercise.title, 11)}</span>
style={{ </li>
textDecoration: exercise.canceled ? "line-through" : "none", ))
}} ) : (
> <li className="no-exercise-message">
{truncateText(exercise.title, 11)} <span>운동이 없습니다. 운동을 추가해주세요.</span>
</span>
<button onClick={() => toggleCancelExercise(exercise)}>
{exercise.canceled ? "X" : "O"}
</button>
</li> </li>
))} )}
</ul> </ul>
<div className="list-foot"> <div className="list-foot">
<button <button
className="pick" className="pick"
onClick={() => { onClick={() => {
if (!isActive) { if (!isActive) {
onRoutineSelect({ onRoutineSelect(selectedRoutine);
...selectedRoutine,
exercises: selectedRoutine.exercises.filter(
(exercise) => !exercise.canceled
),
});
} }
}} }}
disabled={isActive || selectedRoutine.exercises.length === 0}
> >
선택 선택
</button> </button>
...@@ -300,8 +198,7 @@ return ( ...@@ -300,8 +198,7 @@ return (
</div> </div>
</div> </div>
</div> </div>
) )}
}
</div> </div>
); );
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment