- {schedule.title}
+ {decodeHtmlEntities(schedule.title)}
- {schedule.time && (
+ {timeStr && (
- {schedule.time.slice(0, 5)}
+ {timeStr}
)}
- {categoryName && (
+ {categoryInfo.name && (
- {categoryName}
+ {categoryInfo.name}
)}
{sourceName && (
diff --git a/frontend-temp/src/components/schedule/MobileScheduleListCard.jsx b/frontend-temp/src/components/schedule/MobileScheduleListCard.jsx
new file mode 100644
index 0000000..543cb21
--- /dev/null
+++ b/frontend-temp/src/components/schedule/MobileScheduleListCard.jsx
@@ -0,0 +1,86 @@
+import { motion } from 'framer-motion';
+import { Clock, Link2 } from 'lucide-react';
+import { decodeHtmlEntities, getDisplayMembers, getCategoryInfo, getScheduleTime } from '@/utils';
+
+/**
+ * Mobile 일정 리스트 카드 컴포넌트 (타임라인용)
+ * 스케줄 페이지에서 날짜별 일정 목록에 사용
+ * 날짜가 이미 헤더에 표시되므로 날짜 없이 표시
+ */
+function MobileScheduleListCard({
+ schedule,
+ onClick,
+ delay = 0,
+ className = '',
+}) {
+ const categoryInfo = getCategoryInfo(schedule);
+ const timeStr = getScheduleTime(schedule);
+ const displayMembers = getDisplayMembers(schedule);
+ const sourceName = schedule.source?.name;
+
+ return (
+
+ {/* 카드 본체 */}
+
+
+ {/* 시간 및 카테고리 뱃지 */}
+
+ {timeStr && (
+
+
+ {timeStr}
+
+ )}
+
+ {categoryInfo.name}
+
+
+
+ {/* 제목 */}
+
+ {decodeHtmlEntities(schedule.title)}
+
+
+ {/* 출처 */}
+ {sourceName && (
+
+
+ {sourceName}
+
+ )}
+
+ {/* 멤버 */}
+ {displayMembers.length > 0 && (
+
+ {displayMembers.map((name, i) => (
+
+ {name}
+
+ ))}
+
+ )}
+
+
+
+ );
+}
+
+export default MobileScheduleListCard;
diff --git a/frontend-temp/src/components/schedule/MobileScheduleSearchCard.jsx b/frontend-temp/src/components/schedule/MobileScheduleSearchCard.jsx
new file mode 100644
index 0000000..285222e
--- /dev/null
+++ b/frontend-temp/src/components/schedule/MobileScheduleSearchCard.jsx
@@ -0,0 +1,115 @@
+import { motion } from 'framer-motion';
+import { Clock, Link2 } from 'lucide-react';
+import { decodeHtmlEntities, getDisplayMembers, getCategoryInfo, getScheduleTime } from '@/utils';
+
+/**
+ * Mobile 일정 검색 카드 컴포넌트
+ * 스케줄 페이지의 검색 결과에서 사용
+ * 날짜를 왼쪽에 표시하는 레이아웃
+ */
+function MobileScheduleSearchCard({
+ schedule,
+ onClick,
+ delay = 0,
+ className = '',
+}) {
+ const scheduleDate = new Date(schedule.date || schedule.datetime);
+ const categoryInfo = getCategoryInfo(schedule);
+ const timeStr = getScheduleTime(schedule);
+ const displayMembers = getDisplayMembers(schedule);
+ const sourceName = schedule.source?.name;
+
+ const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
+ const isSunday = scheduleDate.getDay() === 0;
+ const isSaturday = scheduleDate.getDay() === 6;
+
+ return (
+
+ {/* 카드 본체 */}
+
+
+ {/* 왼쪽 날짜 영역 */}
+
+
+ {scheduleDate.getFullYear()}
+
+
+ {scheduleDate.getMonth() + 1}.{scheduleDate.getDate()}
+
+
+ {dayNames[scheduleDate.getDay()]}요일
+
+
+
+ {/* 오른쪽 콘텐츠 영역 */}
+
+ {/* 시간 및 카테고리 뱃지 */}
+
+ {timeStr && (
+
+
+ {timeStr}
+
+ )}
+
+ {categoryInfo.name}
+
+
+
+ {/* 제목 */}
+
+ {decodeHtmlEntities(schedule.title)}
+
+
+ {/* 출처 */}
+ {sourceName && (
+
+
+ {sourceName}
+
+ )}
+
+ {/* 멤버 */}
+ {displayMembers.length > 0 && (
+
+ {displayMembers.map((name, i) => (
+
+ {name}
+
+ ))}
+
+ )}
+
+
+
+
+ );
+}
+
+export default MobileScheduleSearchCard;
diff --git a/frontend-temp/src/components/schedule/ScheduleCard.jsx b/frontend-temp/src/components/schedule/ScheduleCard.jsx
index 82e1b70..f19d026 100644
--- a/frontend-temp/src/components/schedule/ScheduleCard.jsx
+++ b/frontend-temp/src/components/schedule/ScheduleCard.jsx
@@ -1,8 +1,9 @@
import { Clock, Tag, Link2 } from 'lucide-react';
+import { decodeHtmlEntities, getDisplayMembers, getCategoryInfo, getScheduleTime } from '@/utils';
/**
- * PC 일정 카드 컴포넌트
- * 홈, 스케줄 페이지 등에서 공통으로 사용
+ * PC 일정 카드 컴포넌트 (일반용)
+ * 홈, 스케줄 페이지에서 공통으로 사용
*/
function ScheduleCard({ schedule, onClick, className = '' }) {
const scheduleDate = new Date(schedule.date);
@@ -15,22 +16,13 @@ function ScheduleCard({ schedule, onClick, className = '' }) {
const isCurrentYear = scheduleYear === currentYear;
const isCurrentMonth = isCurrentYear && scheduleMonth === currentMonth;
- const day = scheduleDate.getDate();
- const weekdays = ['일', '월', '화', '수', '목', '금', '토'];
- const weekday = weekdays[scheduleDate.getDay()];
-
- // 멤버 처리
- const memberList = schedule.member_names
- ? schedule.member_names.split(',').map((n) => n.trim()).filter(Boolean)
- : schedule.members?.map((m) => m.name) || [];
-
- // 5명 이상이면 '프로미스나인'으로 표시
- const displayMembers = memberList.length >= 5 ? ['프로미스나인'] : memberList;
-
- const categoryColor = schedule.category_color || '#6366f1';
- const categoryName = schedule.category_name || schedule.category?.name;
+ const categoryInfo = getCategoryInfo(schedule);
+ const timeStr = getScheduleTime(schedule);
+ const displayMembers = getDisplayMembers(schedule);
const sourceName = schedule.source?.name;
+ const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
+
return (
{!isCurrentYear && (
-
+
{scheduleYear}.{scheduleMonth + 1}
)}
{isCurrentYear && !isCurrentMonth && (
-
+
{scheduleMonth + 1}월
)}
- {day}
- {weekday}
+ {scheduleDate.getDate()}
+
+ {dayNames[scheduleDate.getDay()]}
+
- {/* 내용 영역 */}
+ {/* 스케줄 내용 */}
-
{schedule.title}
+
+ {decodeHtmlEntities(schedule.title)}
+
- {schedule.time && (
+ {timeStr && (
- {schedule.time.slice(0, 5)}
+ {timeStr}
)}
- {categoryName && (
+ {categoryInfo.name && (
- {categoryName}
+ {categoryInfo.name}
)}
{sourceName && (
diff --git a/frontend-temp/src/components/schedule/index.js b/frontend-temp/src/components/schedule/index.js
index 94dd3e2..f25b711 100644
--- a/frontend-temp/src/components/schedule/index.js
+++ b/frontend-temp/src/components/schedule/index.js
@@ -1,2 +1,8 @@
+// PC 컴포넌트
export { default as ScheduleCard } from './ScheduleCard';
+export { default as AdminScheduleCard } from './AdminScheduleCard';
+
+// Mobile 컴포넌트
export { default as MobileScheduleCard } from './MobileScheduleCard';
+export { default as MobileScheduleListCard } from './MobileScheduleListCard';
+export { default as MobileScheduleSearchCard } from './MobileScheduleSearchCard';
diff --git a/frontend-temp/src/utils/index.js b/frontend-temp/src/utils/index.js
index 38957df..f7b3625 100644
--- a/frontend-temp/src/utils/index.js
+++ b/frontend-temp/src/utils/index.js
@@ -40,6 +40,7 @@ export {
getScheduleDate,
getScheduleTime,
getMemberList,
+ getDisplayMembers,
isBirthdaySchedule,
groupSchedulesByDate,
countByCategory,
diff --git a/frontend-temp/src/utils/schedule.js b/frontend-temp/src/utils/schedule.js
index b84947f..5aca8d1 100644
--- a/frontend-temp/src/utils/schedule.js
+++ b/frontend-temp/src/utils/schedule.js
@@ -57,23 +57,40 @@ export function getScheduleTime(schedule) {
}
/**
- * 스케줄에서 멤버 목록 추출
- * 다양한 형식 처리 (문자열 배열, 객체 배열)
+ * 스케줄에서 멤버 이름 목록 추출
+ * 다양한 형식 처리 (member_names 문자열, 문자열 배열, 객체 배열)
* @param {object} schedule - 스케줄 객체
- * @returns {Array<{ id: number, name: string }>}
+ * @returns {string[]} 멤버 이름 배열
*/
export function getMemberList(schedule) {
- const members = schedule.members || [];
+ // member_names 문자열이 있으면 사용 (쉼표 구분)
+ if (schedule.member_names) {
+ return schedule.member_names.split(',').map((n) => n.trim()).filter(Boolean);
+ }
+ const members = schedule.members || [];
if (members.length === 0) return [];
// 문자열 배열인 경우 (검색 결과)
if (typeof members[0] === 'string') {
- return members.map((name, idx) => ({ id: idx, name }));
+ return members.filter(Boolean);
}
// 객체 배열인 경우
- return members;
+ return members.map((m) => m.name).filter(Boolean);
+}
+
+/**
+ * 멤버 표시 이름 가져오기 (5명 이상이면 '프로미스나인')
+ * @param {object} schedule - 스케줄 객체
+ * @returns {string[]} 표시할 멤버 이름 배열
+ */
+export function getDisplayMembers(schedule) {
+ const memberList = getMemberList(schedule);
+ if (memberList.length >= 5) {
+ return ['프로미스나인'];
+ }
+ return memberList;
}
/**