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
375 changes: 370 additions & 5 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@
"@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-toast": "^1.2.4",
"@radix-ui/react-tooltip": "^1.1.7",
"@types/gapi.client.calendar": "^3.0.12",
"axios": "^1.12.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"framer-motion": "^12.0.6",
"glob": "^11.0.1",
"googleapis": "^160.0.0",
"lucide-react": "^0.471.2",
"node-fetch": "^3.3.2",
"react": "^18.3.1",
Expand Down
2 changes: 1 addition & 1 deletion src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ chrome.alarms.onAlarm.addListener((alarm) => {
/**
* 메시지를 통해 알람 예약 및 취소 요청을 처리하는 리스너
*/
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
chrome.runtime.onMessage.addListener((message, _, sendResponse) => {
if (message.action === 'scheduleAlarm') {
const { alarmId, dateTime, title, message: alarmMessage } = message;
schedulePreEventAlarm(alarmId, dateTime, title, alarmMessage);
Expand Down
13 changes: 7 additions & 6 deletions src/content/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover
import { useEffect, useMemo, useState } from 'react';
import icon from '@/assets/icon.png';
import exit from '@/assets/exit.png';
import { Assign, Filters, Quiz, TAB_TYPE, Vod } from './types';
import { Assign, CourseBase, Filters, Quiz, TAB_TYPE, Vod } from './types';
import { ListFilter, OctagonAlert, RefreshCw, Search } from 'lucide-react';
import filter from '@/assets/filter.svg';
import PopoverFooter from './components/PopoverFooter';
Expand All @@ -29,18 +29,19 @@ const submitOptions = [

export default function App() {
const { courses } = useGetCourses();
const typeCourses: CourseBase[] = courses;

// 데이터 관련 상태를 useCourseData 커스텀 훅으로 관리
const { vods, assigns, quizes, isPending, remainingTime, refreshTime, isError, updateData, setIsPending } =
useCourseData(courses);
const { vods, assigns, quizes, isPending, remainingTime, isError, updateData, setIsPending } =
useCourseData(typeCourses);

// activeTab의 타입을 TAB_TYPE으로 지정
const [activeTab, setActiveTab] = useState<TAB_TYPE>(TAB_TYPE.VIDEO);
const [isOpen, setIsOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
const [vodSortBy, setVodSortBy] = useState<keyof Vod>('isAttendance');
const [assignSortBy, setAssignSortBy] = useState<keyof Assign>('isSubmit');
const [quizSortBy, setQuizSortBy] = useState<keyof Quiz>('dueDate');
const [vodSortBy] = useState<keyof Vod>('isAttendance');
const [assignSortBy] = useState<keyof Assign>('isSubmit');
const [quizSortBy] = useState<keyof Quiz>('dueDate');
const [isFilterOpen, setIsFilterOpen] = useState(false);

// 필터 상태 관리 - Record을 사용하여 TAB_TYPE을 키로 지정
Expand Down
2 changes: 1 addition & 1 deletion src/content/components/PendingDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Loader2 } from 'lucide-react';
import styles from '@/styles/shadow.css?inline';
Expand Down
10 changes: 7 additions & 3 deletions src/content/components/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,13 @@ export default function Video({ courseData }: Props) {
</div>
<div className="font-light text-zinc-500 text-ellipsis line-clamp-1" style={{ fontSize: 10 }}>
{formatDateString(vod.range)},{' '}
<span
className={`font-medium ${vod.isAttendance.toLowerCase() === 'o' ? 'text-green-500' : 'text-amber-500'} `}
>
<span
className={`font-medium ${
vod.isAttendance.toLowerCase().trim() === 'o'
? 'text-green-500'
: 'text-amber-500'
} !text-amber-500`}
>
{vod.length}
</span>
</div>
Expand Down
7 changes: 2 additions & 5 deletions src/hooks/useCardData.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useState, useEffect } from 'react';
import { startOfDay } from 'date-fns';
import { loadDataFromStorage } from '@/lib/storage';
import { removeSquareBrackets } from '@/lib/utils';
import { Vod, Assign, Quiz } from '@/content/types';

export type CardData = {
Expand Down Expand Up @@ -57,7 +55,7 @@ function useCardData() {
);

let done = 0;
Object.entries(groupedData).forEach(([_, vodItems]) => {
Object.values(groupedData).forEach((vodItems) => {
if (vodItems[0].weeklyAttendance.toLowerCase() === 'o') {
done += 1;
}
Expand Down Expand Up @@ -97,8 +95,7 @@ function useCardData() {
'quiz',
(quizzes) => {
const total = quizzes.length;
let done = 0;
// quiz에 대한 done 로직이 있다면 여기에 추가합니다.
const done = 0;
return [
{
type: 'quiz',
Expand Down
17 changes: 11 additions & 6 deletions src/hooks/useCourseData.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect, useCallback } from 'react';
import { Vod, Assign, Quiz } from '@/content/types';
import { Vod, Assign, Quiz, CourseBase } from '@/content/types';
import { loadDataFromStorage, saveDataToStorage } from '@/lib/storage';
import { requestData } from '@/lib/fetchCourseData';
import { isCurrentDateByDate, isCurrentDateInRange } from '@/lib/utils';
Expand All @@ -8,7 +8,7 @@ const makeVodKey = (courseId: string, title: string, week: number) => `${courseI
const makeAssignKey = (courseId: string, title: string, dueDate: string) => `${courseId}-${title}-${dueDate}`;
const makeQuizKey = (courseId: string, title: string, dueDate: string) => `${courseId}-${title}-${dueDate}`;

export function useCourseData(courses: any[]) {
export function useCourseData(courses: CourseBase[]) {
const [vods, setVods] = useState<Vod[]>([]);
const [assigns, setAssigns] = useState<Assign[]>([]);
const [quizes, setQuizes] = useState<Quiz[]>([]);
Expand All @@ -29,8 +29,8 @@ export function useCourseData(courses: any[]) {
const tempQuizes: Quiz[] = [...quizes];

const vodSet = new Set(tempVods.map((v) => makeVodKey(v.courseId, v.title, v.week)));
const assignSet = new Set(tempAssigns.map((a) => makeAssignKey(a.courseId, a.title, a.dueDate)));
const quizSet = new Set(tempQuizes.map((q) => makeQuizKey(q.courseId, q.title, q.dueDate)));
const assignSet = new Set(tempAssigns.map((a) => makeAssignKey(a.courseId, a.title, a.dueDate ? a.dueDate : '')));
const quizSet = new Set(tempQuizes.map((q) => makeQuizKey(q.courseId, q.title, q.dueDate ? q.dueDate : '')));

await Promise.all(
courses.map(async (course) => {
Expand Down Expand Up @@ -61,7 +61,11 @@ export function useCourseData(courses: any[]) {
});

result.assignDataArray.forEach((assignData) => {
const assignKey = makeAssignKey(course.courseId, assignData.title, assignData.dueDate);
const assignKey = makeAssignKey(
course.courseId,
assignData.title,
assignData.dueDate ? assignData.dueDate : ''
);
if (!assignSet.has(assignKey)) {
console.info(assignKey);
assignSet.add(assignKey);
Expand All @@ -79,7 +83,7 @@ export function useCourseData(courses: any[]) {
});

result.quizDataArray.forEach((quizData) => {
const quizKey = makeQuizKey(course.courseId, quizData.title, quizData.dueDate);
const quizKey = makeQuizKey(course.courseId, quizData.title, quizData.dueDate ? quizData.dueDate : '');
if (!quizSet.has(quizKey)) {
console.info(quizKey);
quizSet.add(quizKey);
Expand Down Expand Up @@ -112,6 +116,7 @@ export function useCourseData(courses: any[]) {

setIsPending(false);
} catch (error) {
console.warn(error);
localStorage.removeItem('lastRequestTime');
setIsError(true);
setIsPending(false);
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useGetCourse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { saveDataToStorage } from '@/lib/storage';
import { removeSquareBrackets } from '@/lib/utils';
import { useState, useEffect } from 'react';

interface UseCouresResult {
export interface UseCourseResult {
courses: CourseBase[];
}

export const useGetCourses = (): UseCouresResult => {
export const useGetCourses = (): UseCourseResult => {
const [courses, setCourses] = useState<CourseBase[]>([]);
useEffect(() => {
if (!document) return;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/fetchAssign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const fetchAssign = async (link: string) => {
row.querySelector(headerMap.title)?.textContent?.trim() ||
row.querySelector(headerMap.assign)?.textContent?.trim() ||
null;
let sbj = row.querySelector(headerMap.subject)?.textContent?.trim() || '';
const sbj = row.querySelector(headerMap.subject)?.textContent?.trim() || '';
const url = (row.querySelector(headerMap.url) as HTMLAnchorElement)?.href || null;
const dueDate = row.querySelector(headerMap.dueDate)?.textContent?.trim() || null;
const isSubmit = row.querySelector(headerMap.isSubmit)?.textContent?.trim() === '미제출' ? false : true;
Expand Down
8 changes: 3 additions & 5 deletions src/lib/fetchIndexPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ export const fetchIndexPage = async (link: string) => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');

const isAttendances = Array.from(
doc.querySelectorAll('#region-main > div > div > div.user_attendance.course_box > div > ul > li')
);

const weeks = Array.from(doc.querySelectorAll('#region-main > div > div > div.total_sections > div > ul > li'));

const vods = weeks
Expand All @@ -27,7 +23,9 @@ export const fetchIndexPage = async (link: string) => {
.filter((item) => !item.closest('.dimmed'))
.map((item) => {
const week = index + 1;
const title = item.querySelector('.instancename')?.textContent?.trim() || null;
const instancename = item.querySelector('.instancename');
instancename?.querySelector('.accesshide')?.remove();
const title = 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
2 changes: 1 addition & 1 deletion src/lib/fetchQuiz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const fetchQuiz = async (link: string) => {
const rows = Array.from(doc.querySelectorAll('table.generaltable tbody tr'));
const quizzes = rows
.map((row) => {
let sbj = row.querySelector(headerMap.subject)?.textContent?.trim() || '';
const sbj = row.querySelector(headerMap.subject)?.textContent?.trim() || '';
const title = row.querySelector(headerMap.title)?.textContent?.trim() || null;
let url = (row.querySelector(headerMap.url) as HTMLAnchorElement)?.href || null;
const dueDate = row.querySelector(headerMap.dueDate)?.textContent?.trim() || null;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export const formatDateString = (input: string | null) => {

export const calculateRemainingTimeByRange = (range: string | null) => {
if (!range) return '정보없음';
const [startDateStr, endDateStr] = range.split(' ~ ');
const [, endDateStr] = range.split(' ~ ');
const endDate = new Date(endDateStr);

const now = new Date();
Expand Down
7 changes: 1 addition & 6 deletions src/option/ColorSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function ColorSetting() {
});
setOriginalColors(originals);
}
}, [courses]);
}, [courses, courseColors.length]);

const handleColorChange = (
courseId: string,
Expand Down Expand Up @@ -281,11 +281,6 @@ function CourseCard({ course, colorSetting, originalColor, onColorChange, onRese
onColorChange(course.courseId, value, 'solid', undefined, opacity);
};

const handleCustomGradientChange = (value: string) => {
setCustomGradient(value);
onColorChange(course.courseId, value, 'gradient', value, opacity);
};

const handleGradientColorChange = (startColor: string, endColor: string) => {
const newGradient = `linear-gradient(to right, ${startColor}, ${endColor})`;
setGradientStartColor(startColor);
Expand Down
5 changes: 4 additions & 1 deletion src/option/Labo.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React, { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import { calendar_v3 } from 'googleapis';

type CalendarEvent = calendar_v3.Schema$Event;

const Labo: React.FC = () => {
const [token, setToken] = useState<string | null>(null);
const [events, setEvents] = useState<any[]>([]);
const [events, setEvents] = useState<CalendarEvent[]>([]);

const fetchCalendarEvents = (token: string) => {
fetch(
Expand Down
2 changes: 1 addition & 1 deletion src/option/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { Calendar, Github, LayoutDashboard, NotebookText, Palette, Star, Video, Zap } from 'lucide-react';
import { Calendar, LayoutDashboard, NotebookText, Palette, Video, Zap } from 'lucide-react';
import type React from 'react';
import { Link, useLocation } from 'react-router-dom';
import icon from '@/assets/icon.png';
Expand Down
2 changes: 1 addition & 1 deletion src/option/calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useMemo, useEffect } from 'react';
import { useState, useMemo, useEffect } from 'react';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
Expand Down
12 changes: 5 additions & 7 deletions src/option/components/AssignCard.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import { useEffect, useState } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Assign, Vod } from '@/content/types';
import { Assign } from '@/content/types';

import NotificationSwitch from '@/components/ui/notification-switch';
import { loadDataFromStorage, saveDataToStorage } from '@/lib/storage';
import { useToast } from '@/hooks/use-toast';
import { calculateDueDate, calculateRemainingTime, calculateTimeDifference, removeSquareBrackets } from '@/lib/utils';
import { calculateDueDate, calculateRemainingTime } from '@/lib/utils';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

interface TaskStatusCardProps {
assign: Assign;
}

const AssignCard: React.FC<TaskStatusCardProps> = ({ assign }) => {
if (!assign) return <></>;

const [showRemainingTime, setShowRemainingTime] = useState(false);

useEffect(() => {
Expand All @@ -28,6 +23,9 @@ const AssignCard: React.FC<TaskStatusCardProps> = ({ assign }) => {
return () => clearTimeout(timer);
}, [showRemainingTime]);

if (!assign) {
return <></>;
}
const timeDifference = calculateDueDate(assign.dueDate);

return (
Expand Down
13 changes: 3 additions & 10 deletions src/option/components/AssignContent.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Card, CardContent } from '@/components/ui/card';
import { useEffect, useState } from 'react';
import { Assign } from '@/content/types';
import { loadDataFromStorage } from '@/lib/storage';
Expand All @@ -8,13 +8,6 @@ import thung from '@/assets/thung.png';
import { isCurrentDateByDate } from '@/lib/utils';

export function AssignContent() {
const date = new Date();
const formattedDate = date.toLocaleDateString('ko-KR', {
weekday: 'long',
month: 'short',
day: 'numeric',
});

const [assignArray, setAssignArray] = useState<Assign[]>([]);

useEffect(() => {
Expand All @@ -30,7 +23,7 @@ export function AssignContent() {
return;
}
} else {
parsedData = data as any;
parsedData = data as Assign[];
}

const sortedAssignArray = parsedData.sort((a, b) => {
Expand Down Expand Up @@ -93,7 +86,7 @@ export function AssignContent() {
<ScrollArea className="h-[calc(100vh-12rem)]">
<CardContent className="p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{assignArray.map((assign, index) => {
{assignArray.map((assign) => {
const key = `${assign.courseId}-${assign.title}-${assign.dueDate}`;
return <AssignCard key={key} assign={assign} />;
})}
Expand Down
6 changes: 1 addition & 5 deletions src/option/components/CourseDetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ const CourseDetailModal: React.FC<ModalProps> = ({ vodList, onClose }: ModalProp
}, 300);
};

const toggleRemainingTime = () => {
setShowRemainingTime((prev) => !prev);
};

const timeDifference = calculateTimeDifference(vodList[0].range);

const modalContent = (
Expand Down Expand Up @@ -74,7 +70,7 @@ const CourseDetailModal: React.FC<ModalProps> = ({ vodList, onClose }: ModalProp
<ScrollArea className="h-auto">
<div className="space-y-3 py-2">
{vodList.map((vod, index) => {
const isAttendance = vod.isAttendance.toLowerCase() === 'o';
const isAttendance = vod.isAttendance.toLowerCase().trim() === 'o';
return (
<Card
key={index}
Expand Down
8 changes: 5 additions & 3 deletions src/option/components/QuizCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Quiz } from '@/content/types';

import { calculateDueDate, calculateRemainingTime, calculateTimeDifference, removeSquareBrackets } from '@/lib/utils';
import { calculateDueDate, calculateRemainingTime } from '@/lib/utils';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

interface TaskStatusCardProps {
quiz: Quiz;
}

const QuizCard: React.FC<TaskStatusCardProps> = ({ quiz }) => {
if (!quiz) return <></>;

const [showRemainingTime, setShowRemainingTime] = useState(false);

useEffect(() => {
Expand All @@ -25,6 +23,10 @@ const QuizCard: React.FC<TaskStatusCardProps> = ({ quiz }) => {
return () => clearTimeout(timer);
}, [showRemainingTime]);

if (!quiz) {
return <></>;
}

const timeDifference = calculateDueDate(quiz.dueDate);

return (
Expand Down
Loading
Loading