Skip to content
Open
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
26 changes: 26 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# TODO: Fix ESLint Issues

## Step 1: Fix scripts/sitemap-generator.js

- Replace unused 'e' in catch block with underscore.

## Step 2: Fix simulations/test.jsx - Remove unused imports

- Remove 'useMemo' from React imports.
- Remove 'getTimeScale' from Time.js imports.
- Remove 'PHYSICS_CONTROLS' and 'VISUALIZATION_CONTROLS' from config imports.

## Step 3: Fix simulations/test.jsx - Fix useEffect dependencies

- Add 'inputs.timeScale' to the first useEffect dependency array.
- Extract 'simData["Total Energy"]' to a variable for the warnings useEffect.
- Add 'simData' to the warnings useEffect dependency array.

## Step 4: Fix simulations/test.jsx - Refactor warnings logic

- Move warnings calculation from useEffect to useMemo to avoid setState in effect.
- Update the component to use warnings from useMemo.

## Step 5: Run ESLint to verify fixes

- Execute `npm run lint` to check if all issues are resolved.
45 changes: 45 additions & 0 deletions app/(core)/components/CollapsibleSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// CollapsibleSection.tsx
"use client";
import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";

interface CollapsibleSectionProps {
title: string;
children: React.ReactNode;
defaultExpanded?: boolean;
icon?: React.ReactNode;
className?: string;
}

export default function CollapsibleSection({
title,
children,
defaultExpanded = false,
icon,
className = "",
}: CollapsibleSectionProps) {
const [isExpanded, setIsExpanded] = useState(defaultExpanded);

return (
<div
className={`collapsible-section ${className} ${isExpanded ? "expanded" : "collapsed"}`}
>
<button
className="collapsible-header"
onClick={() => setIsExpanded(!isExpanded)}
aria-expanded={isExpanded}
>
<div className="collapsible-header-content">
{icon && <span className="collapsible-icon">{icon}</span>}
<h3 className="collapsible-title">{title}</h3>
</div>
<FontAwesomeIcon
icon={isExpanded ? faChevronUp : faChevronDown}
className="collapsible-arrow"
/>
</button>
{isExpanded && <div className="collapsible-content">{children}</div>}
</div>
);
}
63 changes: 63 additions & 0 deletions app/(core)/components/EducationalTheorySection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// EducationalTheorySection.tsx
"use client";
import React from "react";
import LearningObjectives from "./LearningObjectives.tsx";
import PhysicsEquations from "./PhysicsEquations.tsx";
import PhysicsWarnings from "./PhysicsWarnings.tsx";
import GuidedExperiments from "./GuidedExperiments.tsx";

interface EducationalTheorySectionProps {
learningObjectives: {
title: string;
goals: string[];
variables: string[];
};
physicsEquations: Array<{
name: string;
formula: string;
description?: string;
}>;
warnings: Array<{
id: string;
message: string;
severity: "warning" | "error";
}>;
guidedExperiments: Array<{
id: string;
name: string;
description: string;
instructions: string[];
question?: string;
parameters: Record<string, string | number | boolean>;
}>;
onApplyExperiment: (
params: Record<string, string | number | boolean>
) => void;
}

export default function EducationalTheorySection({
learningObjectives,
physicsEquations,
warnings,
guidedExperiments,
onApplyExperiment,
}: EducationalTheorySectionProps) {
return (
<div className="educational-theory-section">
<LearningObjectives
title={learningObjectives.title}
goals={learningObjectives.goals}
variables={learningObjectives.variables}
/>

<PhysicsEquations equations={physicsEquations} />

{warnings.length > 0 && <PhysicsWarnings warnings={warnings} />}

<GuidedExperiments
experiments={guidedExperiments}
onApplyExperiment={onApplyExperiment}
/>
</div>
);
}
76 changes: 76 additions & 0 deletions app/(core)/components/GuidedExperiments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// GuidedExperiments.tsx
"use client";
import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck } from "@fortawesome/free-solid-svg-icons";

interface Experiment {
id: string;
name: string;
description: string;
instructions: string[];
question?: string;
parameters: Record<string, string | number | boolean>;
}

interface GuidedExperimentsProps {
experiments: Experiment[];
onApplyExperiment: (
params: Record<string, string | number | boolean>
) => void;
}

export default function GuidedExperiments({
experiments,
onApplyExperiment,
}: GuidedExperimentsProps) {
const [activeExperiment, setActiveExperiment] = useState<string | null>(null);

const handleApply = (experiment: Experiment) => {
onApplyExperiment(experiment.parameters);
setActiveExperiment(experiment.id);
};

return (
<div className="guided-experiments">
<div className="experiments-list">
{experiments.map((experiment) => (
<div
key={experiment.id}
className={`experiment-card ${
activeExperiment === experiment.id ? "active" : ""
}`}
>
<div className="experiment-header">
<h4>{experiment.name}</h4>
<button
className="experiment-apply-btn"
onClick={() => handleApply(experiment)}
title="Apply this experiment"
>
<FontAwesomeIcon icon={faCheck} />
Apply
</button>
</div>
<p className="experiment-description">{experiment.description}</p>
<div className="experiment-instructions">
<strong>Instructions:</strong>
<ol>
{experiment.instructions.map((instruction, i) => (
<li key={`${experiment.id}-instruction-${i}`}>
{instruction}
</li>
))}
</ol>
</div>
{experiment.question && (
<div className="experiment-question">
<strong>Question:</strong> {experiment.question}
</div>
)}
</div>
))}
</div>
</div>
);
}
4 changes: 2 additions & 2 deletions app/(core)/components/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export default function Layout({
<Stars
color={starColor}
opacity={starOpacity}
zIndex={1}
starDensity={0.005}
zIndex={-10}
starDensity={0.0003}
/>
)}
{showGradient && <GradientBackground />}
Expand Down
35 changes: 35 additions & 0 deletions app/(core)/components/LearningObjectives.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// LearningObjectives.tsx
"use client";
import React from "react";

interface LearningObjectivesProps {
goals: string[];
variables: string[];
}

export default function LearningObjectives({
goals,
variables,
}: LearningObjectivesProps) {
return (
<div className="learning-objectives">
<div className="learning-section">
<h3 className="learning-section-title">Learning Goals</h3>
<ul className="learning-list">
{goals.map((goal, i) => (
<li key={i}>{goal}</li>
))}
</ul>
</div>

<div className="learning-section">
<h3 className="learning-section-title">Key Variables</h3>
<ul className="learning-list">
{variables.map((variable, i) => (
<li key={`variable-${i}`}>{variable}</li>
))}
</ul>
</div>
</div>
);
}
28 changes: 28 additions & 0 deletions app/(core)/components/PhysicsEquations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// PhysicsEquations.tsx
"use client";
import React from "react";

interface PhysicsEquationsProps {
equations: Array<{
name: string;
formula: string;
description?: string;
}>;
}

export default function PhysicsEquations({ equations }: PhysicsEquationsProps) {
return (
<div className="physics-equations">
<div className="equations-list">
{equations.map((eq, i) => (
<div key={i} className="equation-item">
<div className="equation-formula">{eq.formula}</div>
{eq.description && (
<div className="equation-description">{eq.description}</div>
)}
</div>
))}
</div>
</div>
);
}
33 changes: 33 additions & 0 deletions app/(core)/components/PhysicsWarnings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// PhysicsWarnings.tsx
"use client";
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";

interface Warning {
id: string;
message: string;
severity: "warning" | "error";
}

interface PhysicsWarningsProps {
warnings: Warning[];
}

export default function PhysicsWarnings({ warnings }: PhysicsWarningsProps) {
if (warnings.length === 0) return null;

return (
<div className="physics-warnings">
{warnings.map((warning) => (
<div
key={warning.id}
className={`warning-item warning-${warning.severity}`}
>
<FontAwesomeIcon icon={faExclamationTriangle} />
<span>{warning.message}</span>
</div>
))}
</div>
);
}
17 changes: 10 additions & 7 deletions app/(core)/components/SimulationLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function SimulationLayout({
onLoad,
children,
dynamicInputs,
hideDefaultControls = false,
}) {
const theory = useMemo(() => {
const chapter = chapters.find((ch) => ch.link === simulation);
Expand All @@ -42,13 +43,15 @@ export default function SimulationLayout({
{/* 1. Render the Canvas */}
{children}

{/* 2. Render the Main Controls */}
<Controls
onReset={onReset}
inputs={inputs}
simulation={simulation}
onLoad={onLoad}
/>
{/* 2. Render the Main Controls (unless hidden) */}
{!hideDefaultControls && (
<Controls
onReset={onReset}
inputs={inputs}
simulation={simulation}
onLoad={onLoad}
/>
)}

{/* 3. Render the Dynamic Inputs */}
{dynamicInputs}
Expand Down
Loading