Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/content/StickyPopoverTrigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const StickyPopoverTrigger: React.FC<{ children: React.ReactNode }> = ({ childre
if (!placeholder) return;

const fixedBottom = 20;
const collapsedBottom = 8;
const transitionRange = 20;

const handleScroll = () => {
Expand Down
6 changes: 3 additions & 3 deletions src/content/components/Assignment.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { calculateDueDate, calculateRemainingTime, cn, isWithinSevenDays } from '@/lib/utils';
import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card';
import { calculateDueDate, calculateRemainingTime } from '@/lib/utils';
import { Card, CardFooter, CardHeader } from '@/components/ui/card';
import { BadgeCheck, Clock, Siren, TriangleAlert } from 'lucide-react';
import { Tooltip } from '@radix-ui/react-tooltip';
import { TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
Expand All @@ -26,7 +26,7 @@ export default function Assignment({ courseData }: Props) {
{courseData.map((course, index) => {
if (!course) return null;

let isDueDateSame = true;
const isDueDateSame = true;
const timeDifference = calculateDueDate(course.dueDate!);

return (
Expand Down
1 change: 0 additions & 1 deletion src/content/components/FilterBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { X } from 'lucide-react';

interface FilterBadgeProps {
label: string;
Expand Down
2 changes: 1 addition & 1 deletion src/content/components/QuizTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function QuizTab({ courseData }: Props) {
{courseData.map((course, index) => {
if (!course) return null;

let isDueDateSame = true;
const isDueDateSame = true;
const timeDifference = calculateDueDate(course.dueDate!);

return (
Expand Down
5 changes: 2 additions & 3 deletions src/content/components/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { Vod } from '../types';
import {
calculateRemainingTimeByRange,
calculateTimeDifference,
cn,
formatDateString,
isCurrentDateInRange,
} from '@/lib/utils';
import { AlarmClock, BadgeCheck, ChevronDown, ChevronUp, Clock, Siren, TriangleAlert } from 'lucide-react';
import { BadgeCheck, ChevronDown, ChevronUp, Clock, Siren, TriangleAlert } from 'lucide-react';

import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';

Expand Down Expand Up @@ -126,7 +125,7 @@ export default function Video({ courseData }: Props) {
});

const item = vods[0];
let isDueDateSame = true;
const isDueDateSame = true;
const timeDifference = calculateTimeDifference(item.range);
const isExpanded = expandedCards[`${item.title}-${index}`] || false;

Expand Down
46 changes: 26 additions & 20 deletions src/hooks/useCourseData.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { useState, useEffect, useCallback } from 'react';
import { Vod, Assign, Quiz, TAB_TYPE } from '@/content/types';
import { Vod, Assign, Quiz } from '@/content/types';
import { loadDataFromStorage, saveDataToStorage } from '@/lib/storage';
import { requestData } from '@/lib/fetchCourseData';
import { isCurrentDateByDate, isCurrentDateInRange } from '@/lib/utils';

const makeVodKey = (courseId: string, title: string, week: number) => `${courseId}-${title}-${week}`;
const makeAssignKey = (courseId: string, title: string, dueDate: string) => `${courseId}-${title}-${dueDate}`;
const makeQuizKey = (courseId: string, title: string, dueDate: string) => `${courseId}-${title}-${dueDate}`;

// courses 배열을 받아 vod, assign, quiz 데이터를 관리하는 커스텀 훅
export function useCourseData(courses: any[]) {
const [vods, setVods] = useState<Vod[]>([]);
const [assigns, setAssigns] = useState<Assign[]>([]);
Expand All @@ -24,21 +28,18 @@ export function useCourseData(courses: any[]) {
const tempAssigns: Assign[] = [...assigns];
const tempQuizes: Quiz[] = [...quizes];

// Set을 사용하여 중복 방지 (각 데이터 유형별로 title을 기준으로)
const vodSet = new Set(tempVods.map((vod) => `${vod.courseId}-${vod.title}-${vod.range}-vod`));
const assignSet = new Set(
tempAssigns.map((assign) => `${assign.courseId}-${assign.title}-${assign.dueDate}-assign`)
);
const quizSet = new Set(tempQuizes.map((quiz) => `${quiz.courseId}-${quiz.title}-${quiz.dueDate}-quiz`));
const vodSet = new Set<string>();
const assignSet = new Set<string>();
const quizSet = new Set<string>();

await Promise.all(
courses.map(async (course) => {
const result = await requestData(course.courseId);

result.vodDataArray.forEach((vodData) => {
result.vodAttendanceArray.forEach((vodAttendanceData) => {
const vodKey = `${vodAttendanceData.title}-${vodAttendanceData.week}`;
if (vodAttendanceData.title === vodData.title && vodAttendanceData.week === vodData.week) {
const vodKey = makeVodKey(course.courseId, vodData.title, vodData.week);
if (vodAttendanceData.week === vodData.week) {
if (!vodSet.has(vodKey)) {
vodSet.add(vodKey);
tempVods.push({
Expand All @@ -60,8 +61,9 @@ export function useCourseData(courses: any[]) {
});

result.assignDataArray.forEach((assignData) => {
if (!assignSet.has(assignData.title)) {
assignSet.add(assignData.title);
const assignKey = makeAssignKey(course.courseId, assignData.title, assignData.dueDate);
if (!assignSet.has(assignKey)) {
assignSet.add(assignKey);
tempAssigns.push({
courseId: course.courseId,
prof: course.prof,
Expand All @@ -76,8 +78,9 @@ export function useCourseData(courses: any[]) {
});

result.quizDataArray.forEach((quizData) => {
if (!quizSet.has(quizData.title)) {
quizSet.add(quizData.title);
const quizKey = makeQuizKey(course.courseId, quizData.title, quizData.dueDate);
if (!quizSet.has(quizKey)) {
quizSet.add(quizKey);
tempQuizes.push({
courseId: course.courseId,
prof: course.prof,
Expand Down Expand Up @@ -142,16 +145,19 @@ export function useCourseData(courses: any[]) {
const minutes = (currentTime - parseInt(lastRequestTime, 10)) / (60 * 1000);
setRemainingTime(minutes);
loadDataFromStorage('vod', (data) => {
// setVods((data as Vod[]).filter((vod) => isCurrentDateInRange(vod.range)));
setVods((data as Vod[]) || []);
if (!data) return;
setVods((data as Vod[]).filter((vod) => isCurrentDateInRange(vod.range)));
// setVods((data as Vod[]) || []);
});
loadDataFromStorage('assign', (data) => {
// setAssigns((data as Assign[]).filter((assign) => isCurrentDateByDate(assign.dueDate)));
setAssigns((data as Assign[]) || []);
if (!data) return;
setAssigns((data as Assign[]).filter((assign) => isCurrentDateByDate(assign.dueDate)));
// setAssigns((data as Assign[]) || []);
});
loadDataFromStorage('quiz', (data) => {
// setQuizes((data as Quiz[]).filter((quiz) => isCurrentDateByDate(quiz.dueDate)));
setQuizes((data as Quiz[]) || []);
if (!data) return;
setQuizes((data as Quiz[]).filter((quiz) => isCurrentDateByDate(quiz.dueDate)));
// setQuizes((data as Quiz[]) || []);
});
}
}, [courses, updateData]);
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/useGetCourse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ export const useGetCourses = (): UseCouresResult => {
(item): item is { courseId: string; courseTitle: string; prof: string } =>
item !== null && item.courseId !== '' && item.courseTitle !== '' && item.prof !== ''
);

setCourses(data);
saveDataToStorage('courses', JSON.stringify(data));
console.info('[Dotbugi] 강의 목록:', data);
}, []);

return { courses };
Expand Down
1 change: 1 addition & 0 deletions src/lib/fetchCourseData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const requestData = async (id: string) => {
fetchQuiz(QUIZ_LINK),
]);

console.info('[Dotbugi]', vodAttendanceArray, vodDataArray, assignDataArray, quizDataArray);
return { vodAttendanceArray, vodDataArray, assignDataArray, quizDataArray };
} catch (error) {
console.error('Error while fetching data:', error);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/fetchIndexPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const fetchIndexPage = async (link: string) => {
.filter((item) => !item.closest('.dimmed'))
.map((item) => {
const week = index + 1;
const title = item.querySelector('.instancename')?.textContent?.replace('동영상', '').trim() || null;
const title = item.querySelector('.instancename')?.textContent?.trim() || null;
const url = item.querySelector('a')?.getAttribute('href') || null;
const range = item.querySelector('.text-ubstrap')?.textContent?.trim() || '';
const length = item.querySelector('.text-info')?.textContent?.replace(',', '').trim() || '';
Expand Down
113 changes: 58 additions & 55 deletions src/lib/fetchVodAttendance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,83 +39,86 @@ export const fetchVodAttendance = async (link: string) => {
const vods: VodAttendanceData[] = [];
let idx = 0;
let lastWeeklyAttendance = '';

rows.forEach((row) => {
const cells = Array.from(row.querySelectorAll('td'));
let colIndex = 0;
const rowData: (string | null)[] = [];

while (spanTable[colIndex] && spanTable[colIndex]! > 0) {
rowData.push(cellValues[colIndex] || null);
spanTable[colIndex]! -= 1;
colIndex++;
}
try {
const cells = Array.from(row.querySelectorAll('td'));
let colIndex = 0;
const rowData: (string | null)[] = [];

cells.forEach((cell) => {
while (spanTable[colIndex] && spanTable[colIndex]! > 0) {
rowData.push(cellValues[colIndex] || null);
spanTable[colIndex]! -= 1;
colIndex++;
}

const rowspan = parseInt(cell.getAttribute('rowspan') || '1', 10);
const colspan = parseInt(cell.getAttribute('colspan') || '1', 10);
const cellContent = cell.textContent?.trim() || '';
cells.forEach((cell) => {
while (spanTable[colIndex] && spanTable[colIndex]! > 0) {
rowData.push(cellValues[colIndex] || null);
spanTable[colIndex]! -= 1;
colIndex++;
}

const rowspan = parseInt(cell.getAttribute('rowspan') || '1', 10);
const colspan = parseInt(cell.getAttribute('colspan') || '1', 10);
const cellContent = cell.textContent?.trim() || '';

for (let i = 0; i < colspan; i++) {
rowData.push(cellContent);
for (let i = 0; i < colspan; i++) {
rowData.push(cellContent);

if (rowspan > 1) {
spanTable[colIndex] = rowspan - 1;
cellValues[colIndex] = cellContent;
if (rowspan > 1) {
spanTable[colIndex] = rowspan - 1;
cellValues[colIndex] = cellContent;
} else {
cellValues[colIndex] = null;
}
colIndex++;
}
});

while (colIndex < spanTable.length) {
if (spanTable[colIndex] && spanTable[colIndex]! > 0) {
rowData.push(cellValues[colIndex] || null);
spanTable[colIndex]! -= 1;
} else {
cellValues[colIndex] = null;
rowData.push(null);
}
colIndex++;
}
});

while (colIndex < spanTable.length) {
if (spanTable[colIndex] && spanTable[colIndex]! > 0) {
rowData.push(cellValues[colIndex] || null);
spanTable[colIndex]! -= 1;
let weeklyAttendance =
headerMap['weeklyAttendance'] !== undefined ? rowData[headerMap['weeklyAttendance']] || '' : '';
if (weeklyAttendance) {
lastWeeklyAttendance = weeklyAttendance;
} else {
rowData.push(null);
weeklyAttendance = lastWeeklyAttendance;
}
colIndex++;
}

let weeklyAttendance =
headerMap['weeklyAttendance'] !== undefined ? rowData[headerMap['weeklyAttendance']] || '' : '';
if (weeklyAttendance) {
lastWeeklyAttendance = weeklyAttendance;
} else {
weeklyAttendance = lastWeeklyAttendance;
}

if (weeklyAttendance.includes('일괄출석인정')) weeklyAttendance = 'o';
if (weeklyAttendance.includes('일괄출석인정')) weeklyAttendance = 'o';

const title = headerMap['title'] !== undefined ? rowData[headerMap['title']] || '' : '';
const isAttendance = headerMap['isAttendance'] !== undefined ? rowData[headerMap['isAttendance']] || '' : '';
const title = headerMap['title'] !== undefined ? rowData[headerMap['title']] || '' : '';
const isAttendance = headerMap['isAttendance'] !== undefined ? rowData[headerMap['isAttendance']] || '' : '';

let weekStr = rowData[0] || '';
if (weekStr !== '' && !isNaN(parseInt(weekStr))) {
idx = parseInt(weekStr);
} else {
weekStr = idx.toString();
}
let weekStr = rowData[0] || '';
if (weekStr !== '' && !isNaN(parseInt(weekStr))) {
idx = parseInt(weekStr);
} else {
weekStr = idx.toString();
}

if (!title || !isAttendance) {
return;
if (!title || !isAttendance) {
return;
}
const week = parseInt(weekStr);

vods.push({
title,
isAttendance,
weeklyAttendance,
week,
});
} catch (error) {
console.error(`[Dotbugi] 영상 강의 조회 오류: ${link} ${row}`, error);
}
const week = parseInt(weekStr);

vods.push({
title,
isAttendance,
weeklyAttendance,
week,
});
});

return vods;
Expand Down
4 changes: 2 additions & 2 deletions src/option/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter as Router, Routes, Route } from 'react-router-dom';
import 'src/styles/option.css';
import { HashRouter as Router } from 'react-router-dom';
import '@/styles/option.css';
import App from './App';

const rootElement = document.getElementById('root');
Expand Down
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
"@/*": ["src/*"],
"src/*": ["src/*"]
}
},
"include": ["*/**.ts", "*/**.tsx", "vite.config.ts", "src"],
"include": ["*/**.ts", "*/**.tsx", "vite.config.ts", "*/src"],
"exclude": ["node_modules", "dist"]
}
1 change: 1 addition & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default defineConfig(({ mode }) => {
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
src: path.resolve(__dirname, './src'),
},
},
build: {
Expand Down
Loading