diff --git a/Sora b/Sora new file mode 100644 index 0000000..eb8ed2e --- /dev/null +++ b/Sora @@ -0,0 +1,453 @@ +import React from 'react'; +import { useState, useEffect, useMemo, useCallback } from 'react'; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; +import { BookOpen, BrainCircuit, Calculator, CheckCircle, ChevronDown, ChevronUp, Clock, Coffee, Heart, HelpCircle, ListTodo, Play, Pause, RefreshCw, Smile, Target, Zap } from 'lucide-react'; + +// Helper Functions & Hooks +const useLocalStorage = (key, initialValue) => { + const [storedValue, setStoredValue] = useState(() => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + } catch (error) { + console.error(error); + return initialValue; + } + }); + + const setValue = (value) => { + try { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } catch (error) { + console.error(error); + } + }; + + return [storedValue, setValue]; +}; + +// Custom Modal for alerts +const CustomAlert = ({ message, onClose }) => { + if (!message) return null; + + return ( +
+
+

{message}

+ +
+
+ ); +}; + + +// Components + +const PomodoroTimer = ({ setTotalStudyTime }) => { + const [studyDuration, setStudyDuration] = useLocalStorage('pomodoro:studyDuration', 20); + const [timeLeft, setTimeLeft] = useLocalStorage('pomodoro:timeLeft', 20 * 60); + const [isActive, setIsActive] = useLocalStorage('pomodoro:isActive', false); + const [mode, setMode] = useLocalStorage('pomodoro:mode', 'study'); // study, break, longBreak + const [round, setRound] = useLocalStorage('pomodoro:round', 1); + const [alertMessage, setAlertMessage] = useState(''); + + + const breakDuration = useMemo(() => Math.floor(studyDuration * 0.2 * 60), [studyDuration]); + const longBreakDuration = useMemo(() => Math.floor((studyDuration * 4) * 0.2 * 60), [studyDuration]); + + const handleTimerEnd = useCallback(() => { + setIsActive(false); + setAlertMessage('Time is up! Remember to have a glass of water to stay hydrated.'); + if (mode === 'study') { + if (round < 4) { + setMode('break'); + setTimeLeft(breakDuration); + setRound(r => r + 1); + } else { + setMode('longBreak'); + setTimeLeft(longBreakDuration); + setRound(1); + } + } else { + setMode('study'); + setTimeLeft(studyDuration * 60); + } + }, [mode, round, breakDuration, longBreakDuration, studyDuration]); + + useEffect(() => { + let interval = null; + if (isActive && timeLeft > 0) { + interval = setInterval(() => { + setTimeLeft(timeLeft - 1); + if (mode === 'study') { + setTotalStudyTime(prev => prev + 1); + } + }, 1000); + } else if (isActive && timeLeft === 0) { + handleTimerEnd(); + } + return () => clearInterval(interval); + }, [isActive, timeLeft, mode, setTotalStudyTime, handleTimerEnd]); + + const toggleTimer = () => { + setIsActive(!isActive); + }; + + const resetTimer = () => { + setIsActive(false); + setMode('study'); + setRound(1); + setTimeLeft(studyDuration * 60); + }; + + const handleDurationChange = (e) => { + const newDuration = parseInt(e.target.value, 10); + if (!isNaN(newDuration) && newDuration > 0) { + setStudyDuration(newDuration); + if (!isActive) { + setTimeLeft(newDuration * 60); + } + } + } + + const formatTime = (seconds) => { + const minutes = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; + }; + + const progress = (mode === 'study' ? (studyDuration * 60 - timeLeft) / (studyDuration * 60) : + mode === 'break' ? (breakDuration - timeLeft) / breakDuration : + (longBreakDuration - timeLeft) / longBreakDuration) * 100; + + return ( + <> + setAlertMessage('')} /> +
+

Pomodoro Timer

+
+
+ + + + +
+

{formatTime(timeLeft)}

+

{mode} - Round {round}

+
+
+
+ + +
+
+
+ + +
+
+ + ); +}; + +const SpacedRepetition = () => { + const [topics, setTopics] = useLocalStorage('srs:topics', []); + const [newTopic, setNewTopic] = useState(''); + const today = useMemo(() => new Date().setHours(0, 0, 0, 0), []); + + const scheduleReviews = (startDate) => { + const intervals = [1, 3, 5, 7, 14, 21, 28, 30]; + const start = new Date(startDate); + return intervals.map(day => { + const reviewDate = new Date(start); + reviewDate.setDate(start.getDate() + day); + return { date: reviewDate.toISOString().split('T')[0], completed: false }; + }); + }; + + const addTopic = () => { + if (newTopic.trim() === '') return; + const startDate = new Date().toISOString().split('T')[0]; + setTopics([...topics, { + id: Date.now(), + name: newTopic, + startDate, + reviews: scheduleReviews(startDate) + }]); + setNewTopic(''); + }; + + const toggleCompletion = (topicId, reviewDateStr) => { + setTopics(topics.map(topic => { + if (topic.id === topicId) { + const updatedReviews = topic.reviews.map(review => { + if (review.date === reviewDateStr) { + return { ...review, completed: !review.completed }; + } + return review; + }); + return { ...topic, reviews: updatedReviews }; + } + return topic; + })); + }; + + const upcomingRevisions = useMemo(() => { + return topics.flatMap(topic => + topic.reviews + .filter(r => !r.completed && new Date(r.date).setHours(0,0,0,0) >= today) + .map(r => ({ ...r, topicName: topic.name, topicId: topic.id })) + ).sort((a, b) => new Date(a.date) - new Date(b.date)); + }, [topics, today]); + + const pendingRevisions = useMemo(() => { + return topics.flatMap(topic => + topic.reviews + .filter(r => !r.completed && new Date(r.date).setHours(0,0,0,0) < today) + .map(r => ({ ...r, topicName: topic.name, topicId: topic.id })) + ).sort((a, b) => new Date(a.date) - new Date(b.date)); + }, [topics, today]); + + const RevisionList = ({ title, revisions, isPending }) => ( +
+

{title} ({revisions.length})

+ {revisions.length > 0 ? ( + + ) : ( +

No {title.toLowerCase()} for now.

+ )} +
+ ); + + return ( +
+

Spaced Repetition

+
+ setNewTopic(e.target.value)} + placeholder="Enter new topic to study" + className="flex-grow p-2 border border-yellow-300 rounded-lg bg-white/50 focus:ring-2 focus:ring-yellow-500 focus:outline-none" + /> + +
+ + +
+ ); +}; + +const Countdown = () => { + const [endDate, setEndDate] = useLocalStorage('countdown:endDate', new Date(new Date().getFullYear() + 1, 4, 5).toISOString()); + const [timeLeft, setTimeLeft] = useState({}); + + const calculateTimeLeft = useCallback(() => { + const difference = +new Date(endDate) - +new Date(); + let timeLeftData = {}; + + if (difference > 0) { + timeLeftData = { + days: Math.floor(difference / (1000 * 60 * 60 * 24)), + hours: Math.floor((difference / (1000 * 60 * 60)) % 24), + minutes: Math.floor((difference / 1000 / 60) % 60), + seconds: Math.floor((difference / 1000) % 60) + }; + } + return timeLeftData; + }, [endDate]); + + useEffect(() => { + const timer = setTimeout(() => { + setTimeLeft(calculateTimeLeft()); + }, 1000); + return () => clearTimeout(timer); + }); + + const timerComponents = Object.keys(timeLeft).map(interval => { + if (timeLeft[interval] === undefined) { + return null; + } + return ( +
+ {timeLeft[interval].toString().padStart(2, '0')} + {interval} +
+ ); + }); + + return ( +
+

Exam Countdown

+
+ {timerComponents.length ? timerComponents : Time's up!} +
+
+ + setEndDate(new Date(e.target.value).toISOString())} + className="p-2 border border-yellow-300 rounded-lg bg-white/50 focus:ring-2 focus:ring-yellow-500 focus:outline-none" + /> +
+
+ ); +}; + +const TodoList = () => { + const [tasks, setTasks] = useLocalStorage('todo:tasks', []); + const [newTask, setNewTask] = useState(''); + + useEffect(() => { + // Missed-task recovery system + const today = new Date().toISOString().split('T')[0]; + const updatedTasks = tasks.map(task => { + if (!task.completed && task.dueDate && task.dueDate < today) { + return { ...task, dueDate: today }; // Reschedule to today + } + return task; + }); + if (JSON.stringify(tasks) !== JSON.stringify(updatedTasks)) { + setTasks(updatedTasks); + } + }, []); + + const addTask = () => { + if (newTask.trim() === '') return; + const today = new Date().toISOString().split('T')[0]; + setTasks([...tasks, { id: Date.now(), text: newTask, completed: false, dueDate: today }]); + setNewTask(''); + }; + + const toggleTask = (id) => { + setTasks(tasks.map(task => task.id === id ? { ...task, completed: !task.completed } : task)); + }; + + const removeTask = (id) => { + setTasks(tasks.filter(task => task.id !== id)); + }; + + return ( +
+

To-Do List

+
+ setNewTask(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && addTask()} + placeholder="Add a new task..." + className="flex-grow p-2 border border-yellow-300 rounded-lg bg-white/50 focus:ring-2 focus:ring-yellow-500 focus:outline-none" + /> + +
+ +
+ ); +}; + +const MarksCalculator = () => { + const [mode, setMode] = useState('sectional'); // sectional, mock + const [history, setHistory] = useLocalStorage('marks:history', []); + + const SectionalCalculator = () => { + const subjects = ['English', 'Current Affairs/GK', 'Legal Reasoning', 'Logical Reasoning', 'Quantitative Techniques']; + const [data, setData] = useState( + subjects.reduce((acc, subject) => ({ ...acc, [subject]: { total: '', attempted: '', correct: '', wrong: '', time: '' } }), {}) + ); + const [results, setResults] = useState(null); + + const calculate = () => { + let totalMarks = 0; + const newResults = {}; + subjects.forEach(subject => { + const { correct, wrong, time } = data[subject]; + const marks = (Number(correct) || 0) * 1 - (Number(wrong) || 0) * 0.25; + newResults[subject] = { marks }; + + if (correct > 0 && time > 0) { + const timePerCorrect = time / correct; + if (timePerCorrect > 1.5) { // Example threshold: 1.5 mins per correct answer + newResults[subject].feedback = "Good accuracy, but work on your speed."; + } else { + newResults[subject].feedback = "Excellent! Great accuracy and speed."; + } + } else { + newResults[subject].feedback = "Enter data to get feedback."; + } + totalMarks += marks; + }); + newResults.total = totalMarks; + setResults(newResults); + + setHistory([...history, { type: 'sectional', date: new Date().toLocaleDateString(), score: totalMarks, details: data }]); + }; + + return ( +
+ {subjects.map(subject => ( +
+

{subject}

+ setData({...data, [subject]: {...data[subject], attempted: e.target.value}})} /> + setData({...data, [subject]: {...data[subject], correct: e.target.value}})} /> + setData({...data, [subject]: {...data[subject], wrong: e.target.value}})} /> + setData({...data, [subject]: {...data[subject], time: e.target.value}})} /> +
+ ))} +