diff --git a/.firebase/hosting.ZGlzdA.cache b/.firebase/hosting.ZGlzdA.cache index d1ccf0c..f42aab0 100644 --- a/.firebase/hosting.ZGlzdA.cache +++ b/.firebase/hosting.ZGlzdA.cache @@ -1,4 +1,4 @@ -index.html,1738198381403,617819387bcb7c005fe000ee3f1e44bc4e05e08d505d3de78ec3667183925019 -notepad.png,1738198381217,093b72703014d3121dd8ca8d1938c9d64f2a244e625401af7469fea17eb13b8d -assets/index-DY4wawWc.css,1738198381403,07294ab7e4d3bdb79109b7e3e0cdf2df29df41917fe03315ee90f45b6960d77e -assets/index-W1p8KzX4.js,1738198381404,4a96a1a3ae184e8a13b45dde76fa752a373501c99d018a98b28ba7461426c1c9 +index.html,1738199641680,42d93543836df0e755a752baf4786681ed7b400023f65f7a517c6f92dcbcb239 +notepad.png,1738199641479,093b72703014d3121dd8ca8d1938c9d64f2a244e625401af7469fea17eb13b8d +assets/index-D5L8VQry.css,1738199641680,b72bfe985cc4f8b25de36becb91caa8fd1ba88c549dfec433544d101ffd11b58 +assets/index-DtmNb91O.js,1738199641680,ae83bbc5a2a8f012ec25cb9314b5918508761cb20eb944143efa07b65937c60b diff --git a/src/AcademicPlanning/AcademicPlanning.jsx b/src/AcademicPlanning/AcademicPlanning.jsx index 1b1d30c..03912b4 100644 --- a/src/AcademicPlanning/AcademicPlanning.jsx +++ b/src/AcademicPlanning/AcademicPlanning.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { BookOpen, diff --git a/src/AcademicPlanning/StudySchedule/StudySchedule.jsx b/src/AcademicPlanning/StudySchedule/StudySchedule.jsx index f196938..7339209 100644 --- a/src/AcademicPlanning/StudySchedule/StudySchedule.jsx +++ b/src/AcademicPlanning/StudySchedule/StudySchedule.jsx @@ -15,6 +15,8 @@ const StudySchedule = () => { const [isGeneratingOptimization, setIsGeneratingOptimization] = useState(false); const [selectedSchedule, setSelectedSchedule] = useState(null); const [expandedSections, setExpandedSections] = useState({}); + const [highlightedContent, setHighlightedContent] = useState({}); + const [isHighlighting, setIsHighlighting] = useState(false); useEffect(() => { const unsubscribe = auth.onAuthStateChanged(async (user) => { @@ -126,6 +128,7 @@ const StudySchedule = () => { setIsGeneratingOptimization(true); try { + // First generate the optimization const prompt = ` As a study schedule optimization expert, analyze this schedule and previous performance to provide recommendations. Current/Selected Schedule: ${JSON.stringify(scheduleToAnalyze)} @@ -155,6 +158,18 @@ const StudySchedule = () => { const optimization = jsonMatch[1] ? JSON.parse(jsonMatch[1]) : JSON.parse(text); setAiOptimization(optimization); + + // Then automatically highlight each section + const sections = [ + { id: 'overview', content: optimization.overview }, + { id: 'optimization', content: optimization.optimization }, + { id: 'breakSchedule', content: optimization.breakSchedule }, + { id: 'effectiveness', content: optimization.effectiveness } + ]; + + for (const section of sections) { + await highlightOptimization(section.id, section.content); + } } catch (error) { console.error("Failed to generate optimization:", error); setError("Failed to generate AI optimization. Please try again."); @@ -163,6 +178,43 @@ const StudySchedule = () => { } }; + const highlightOptimization = async (sectionId, content) => { + if (!content || isHighlighting) return; + + setIsHighlighting(true); + try { + const prompt = ` + Analyze this study schedule optimization content and highlight key points: + "${content}" + + Return a JSON response with this structure: + { + "formattedContent": "The content with **key points** marked in bold and *important recommendations* in italics", + "summary": "A 2-sentence summary of the main points" + } + `; + + const genAI = new GoogleGenerativeAI(import.meta.env.VITE_REACT_APP_GEMINI_API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash-exp" }); + const result = await model.generateContent(prompt); + const response = await result.response; + const text = response.text(); + + const jsonMatch = text.match(/```json\n([\s\S]*)\n```/) || [null, text]; + const analysis = jsonMatch[1] ? JSON.parse(jsonMatch[1]) : JSON.parse(text); + + setHighlightedContent(prev => ({ + ...prev, + [sectionId]: analysis + })); + } catch (error) { + console.error('Failed to highlight content:', error); + setError('Failed to analyze content. Please try again.'); + } finally { + setIsHighlighting(false); + } + }; + const toggleSection = (sectionId) => { setExpandedSections(prev => ({ ...prev, @@ -201,32 +253,86 @@ const StudySchedule = () => { {aiOptimization && (
- {[ - { id: 'effectiveness', title: 'Study Effectiveness Tips', content: aiOptimization.effectiveness }, - { id: 'breakSchedule', title: 'Break Schedule', content: aiOptimization.breakSchedule }, - { id: 'overview', title: 'Schedule Analysis', content: aiOptimization.overview }, - { id: 'optimization', title: 'Optimization Suggestions', content: aiOptimization.optimization } - ].map(section => ( -
- - {expandedSections[section.id] && ( -
-

{section.content}

-
- )}
- ))} + {expandedSections['overview'] && ( +
+ {renderSectionContent('overview', aiOptimization.overview)} +
+ )} +
+ +
+
+ +
+ {expandedSections['optimization'] && ( +
+ {renderSectionContent('optimization', aiOptimization.optimization)} +
+ )} +
+ +
+
+ +
+ {expandedSections['breakSchedule'] && ( +
+ {renderSectionContent('breakSchedule', aiOptimization.breakSchedule)} +
+ )} +
+ +
+
+ +
+ {expandedSections['effectiveness'] && ( +
+ {renderSectionContent('effectiveness', aiOptimization.effectiveness)} +
+ )} +
)} ); + const renderSectionContent = (sectionId, content) => ( + highlightedContent[sectionId] ? ( +
+
$1') + .replace(/\*(.*?)\*/g, '$1') + }} + /> +
+ Summary: {highlightedContent[sectionId].summary} +
+
+ ) : ( +

{content}

+ ) + ); + return (
diff --git a/src/AcademicPlanning/StudySchedule/StudySchedule.module.css b/src/AcademicPlanning/StudySchedule/StudySchedule.module.css index fe2b5d1..ec2d875 100644 --- a/src/AcademicPlanning/StudySchedule/StudySchedule.module.css +++ b/src/AcademicPlanning/StudySchedule/StudySchedule.module.css @@ -322,6 +322,26 @@ font-size: 1.2rem; } +.sectionHeader button { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + background: none; + border: none; + padding: 1rem; + cursor: pointer; + color: var(--text-color); +} + +.sectionHeader h3 { + margin: 0; + font-size: 1.1rem; + font-weight: 600; + color: #8a2be2; + text-transform: none; +} + .sectionContent { margin-top: 1rem; padding-top: 1rem; @@ -351,6 +371,57 @@ background: rgba(138, 43, 226, 0.15); } +.highlightButton { + background-color: #4f46e5; + color: white; + padding: 0.5rem 1rem; + border-radius: 0.375rem; + font-size: 0.75rem; + border: none; + transition: all 0.2s; +} + +.highlightButton:hover:not(:disabled) { + background-color: #4338ca; + transform: translateY(-1px); +} + +.highlightButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.highlightedContent { + background-color: #1a2234; + border-radius: 0.5rem; + padding: 1rem; + margin-top: 0.75rem; +} + +.formattedContent { + line-height: 2; + color: #94a3b8; + font-size: 0.875rem; + margin-bottom: 1rem; +} + +.formattedContent strong { + color: #60a5fa; + font-weight: 600; +} + +.formattedContent em { + color: #818cf8; + font-style: italic; +} + +.contentSummary { + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-top: 0.75rem; + color: #94a3b8; + font-size: 0.875rem; +} + @media (max-width: 768px) { .taskRow { grid-template-columns: 1fr;