Skip to content

Commit

Permalink
🖍️ Implement content highlighting feature in StudySchedule component …
Browse files Browse the repository at this point in the history
…and enhance styles for improved user interaction
  • Loading branch information
RushilMahadevu committed Jan 30, 2025
1 parent 68dc7e8 commit 58e63d2
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 24 deletions.
8 changes: 4 additions & 4 deletions .firebase/hosting.ZGlzdA.cache
Original file line number Diff line number Diff line change
@@ -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
1 change: 0 additions & 1 deletion src/AcademicPlanning/AcademicPlanning.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
import {
BookOpen,
Expand Down
144 changes: 125 additions & 19 deletions src/AcademicPlanning/StudySchedule/StudySchedule.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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)}
Expand Down Expand Up @@ -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.");
Expand All @@ -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,
Expand Down Expand Up @@ -201,32 +253,86 @@ const StudySchedule = () => {

{aiOptimization && (
<div className={styles.optimizationResults}>
{[
{ 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 => (
<div key={section.id} className={styles.optimizationSection}>
<button
className={styles.sectionHeader}
onClick={() => toggleSection(section.id)}
>
<h3>{section.title}</h3>
{expandedSections[section.id] ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
<div key="overview" className={styles.optimizationSection}>
<div className={styles.sectionHeader}>
<button onClick={() => toggleSection('overview')}>
<h3>Schedule Analysis</h3>
{expandedSections['overview'] ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
</button>
{expandedSections[section.id] && (
<div className={styles.sectionContent}>
<p>{section.content}</p>
</div>
)}
</div>
))}
{expandedSections['overview'] && (
<div className={styles.sectionContent}>
{renderSectionContent('overview', aiOptimization.overview)}
</div>
)}
</div>

<div key="optimization" className={styles.optimizationSection}>
<div className={styles.sectionHeader}>
<button onClick={() => toggleSection('optimization')}>
<h3>Optimization Recommendations</h3>
{expandedSections['optimization'] ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
</button>
</div>
{expandedSections['optimization'] && (
<div className={styles.sectionContent}>
{renderSectionContent('optimization', aiOptimization.optimization)}
</div>
)}
</div>

<div key="breakSchedule" className={styles.optimizationSection}>
<div className={styles.sectionHeader}>
<button onClick={() => toggleSection('breakSchedule')}>
<h3>Break Schedule Plan</h3>
{expandedSections['breakSchedule'] ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
</button>
</div>
{expandedSections['breakSchedule'] && (
<div className={styles.sectionContent}>
{renderSectionContent('breakSchedule', aiOptimization.breakSchedule)}
</div>
)}
</div>

<div key="effectiveness" className={styles.optimizationSection}>
<div className={styles.sectionHeader}>
<button onClick={() => toggleSection('effectiveness')}>
<h3>Study Effectiveness Strategies</h3>
{expandedSections['effectiveness'] ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
</button>
</div>
{expandedSections['effectiveness'] && (
<div className={styles.sectionContent}>
{renderSectionContent('effectiveness', aiOptimization.effectiveness)}
</div>
)}
</div>
</div>
)}
</div>
);

const renderSectionContent = (sectionId, content) => (
highlightedContent[sectionId] ? (
<div className={styles.highlightedContent}>
<div
className={styles.formattedContent}
dangerouslySetInnerHTML={{
__html: highlightedContent[sectionId].formattedContent
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
}}
/>
<div className={styles.contentSummary}>
<strong>Summary:</strong> {highlightedContent[sectionId].summary}
</div>
</div>
) : (
<p>{content}</p>
)
);

return (
<div className={styles.container}>
<header className={styles.header}>
Expand Down
71 changes: 71 additions & 0 deletions src/AcademicPlanning/StudySchedule/StudySchedule.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 58e63d2

Please sign in to comment.