Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
1876565
adding backend routes
hlpaa Nov 15, 2025
dba030b
adding classes and methos to create, get by id and update a scirpt
hlpaa Nov 15, 2025
e0f7a23
adding frontend infra to implement scripts functionality
hlpaa Nov 16, 2025
2f1ae8b
adjusting frontend formattting
hlpaa Nov 16, 2025
02f3209
Merge pull request #1 from hlpaa/feature/scitps-henrique
tomclima Nov 16, 2025
12d17cd
creation of the "Task" and "TaskSet" helper classes for Script refactor
tomclima Nov 16, 2025
3c9c2d2
Implementation of the routes for Task class in server
tomclima Nov 16, 2025
a32355c
creation of "Task" and "TaskService" classes for frontend
tomclima Nov 16, 2025
8906855
add getallscripts to server
tomclima Nov 18, 2025
b2c9eaa
BREAKING Change task
tomclima Nov 18, 2025
0d79e48
Revamp of Script page to task based content
tomclima Nov 18, 2025
3f01820
correction to taskset
tomclima Nov 18, 2025
6c2d2f5
FEAT: now able to edit tasks
tomclima Nov 18, 2025
aefe552
BUFFIX: now able to FETCH THE TASKS SPECIFIC TO THE SCRIPT BEING EDITED
hlpaa Nov 19, 2025
def036b
Merge pull request #2 from hlpaa/tasks-based-script
hlpaa Nov 20, 2025
16d9bc3
ScriptAnswer interface development
tomclima Nov 27, 2025
fb50ccb
Merge remote-tracking branch 'upstream/main'
tomclima Dec 4, 2025
8d6d296
Merge branch 'main' into corrigir-roteiro
tomclima Dec 4, 2025
08b5584
FEAT: frontend for taskanswer grading + mock data
tomclima Dec 4, 2025
a1398d5
Feat: api endpoints implemented for scriptanswer object
tomclima Dec 4, 2025
aa0bf1c
DOCUMENTATION: added comments to taskanswer endpoints
tomclima Dec 4, 2025
f37c2f7
INTEGRATION: Grading pages connected to api endpoints
tomclima Dec 4, 2025
2675f93
HOTFEAT: removescriptanswer functionality added to answerset object
tomclima Dec 7, 2025
36a701b
FIX: Routes renamed to reflect scenarios + reordering of routes neede…
tomclima Dec 7, 2025
7f8e96f
FIX: now ScriptAnswerSet().getAll() returns a COPY of its internal Sc…
tomclima Dec 7, 2025
09213ef
BUGFIX: moved post method up in the order so that its route isnt shad…
tomclima Dec 7, 2025
5a59d75
FEAT: error handling for taskanswers initialized with invalid grades
tomclima Dec 7, 2025
dd341ef
fix: solve shadowing of /api/scripts/answers/:studentID route by addi…
tomclima Dec 7, 2025
3efd4b1
fix: solved problem where ScriptAnswerSet's internal scriptAnswers ar…
tomclima Dec 7, 2025
f70ef13
feat: grade validation before updating TaskAnswer grade
tomclima Dec 7, 2025
72951ca
feat: error handling for invalid grade values inside ScriptAnswer object
tomclima Dec 7, 2025
234ac8a
testing: scriptanswer tests
tomclima Dec 7, 2025
002c6af
testing: unit tests for corrigir-roteiro service routes and objects
tomclima Dec 7, 2025
6bb97ee
dependencies: testing dependencies added
tomclima Dec 8, 2025
c5b9157
testing: scenarios defined for scriptAnswer functionality
tomclima Dec 8, 2025
e45a8b5
testing/NOT FUNCTIONAL: step layout for corrigir roteiro feature
tomclima Dec 8, 2025
58fdbeb
fix: scriptanswer now references studentid and has scriptid field
tomclima Dec 8, 2025
3297028
fix: scriptanswerset now has stricter type definition (any[] -> Scrip…
tomclima Dec 8, 2025
3f454a8
fix: TaskAnswer now uses task id instead of full embedded task object
tomclima Dec 8, 2025
53bab7e
refactor: routes for scriptanswer, scripts and tasks logic now in sep…
tomclima Dec 8, 2025
14c658d
testing: updated unit tests for types associated with script grading
tomclima Dec 8, 2025
65b3511
testing: scriptgrading backend integration tests
tomclima Dec 8, 2025
fd4a194
testing: mock script, task, and scriptanswer data added to server
tomclima Dec 8, 2025
198ad96
fix: fixed double /, which prevented route access to tasks and scripts
tomclima Dec 8, 2025
70c9522
integration: update scriptanswer service and scriptanswer type to bet…
tomclima Dec 8, 2025
4ed3788
fix: now script titles and task statements are shown correctly on screen
tomclima Dec 8, 2025
41b5a94
Merge pull request #4 from hlpaa/corrigir-roteiro
hlpaa Dec 8, 2025
851e6dc
feat: scriptremoval routes implemented with DELETE keyoword
tomclima Dec 8, 2025
15507ae
testing: server acceptance test for getall scriptanswers scenarios
tomclima Dec 8, 2025
481fbcc
testing: retrieval of answers from specific student acceptance tests
tomclima Dec 8, 2025
896c48c
feat(Answer): implementing Answers
DaviGGuerreiro Dec 8, 2025
41928cc
feat(ScriptResponses): Implementing the Script Responses of the Enrol…
DaviGGuerreiro Dec 8, 2025
ca97069
fix(scriptResponse): uuid in the scriptResponse
DaviGGuerreiro Dec 8, 2025
f24a8bb
Merge remote-tracking branch 'upstream/main' into feat/responder-rote…
DaviGGuerreiro Dec 8, 2025
ad551b0
feat: add taskanswer to scriptanswer object route
tomclima Dec 9, 2025
a99097e
testing/NOT FUNCTIONAL: cureently broken tests for adding taskanswer …
tomclima Dec 9, 2025
7de2f3c
fix: fixed add taskanswer to scriptanswer endpoint
tomclima Dec 9, 2025
5b29347
testing/fix: fixed problem where test as failing because it was expec…
tomclima Dec 9, 2025
0df5ee2
testing/refactor: used regex to make testid and task givens more gene…
tomclima Dec 9, 2025
efdee52
testing: grade updating acceptance tests added
tomclima Dec 9, 2025
3ec9f53
refactor/testing: removed redundant "there is no student with cpf {} …
tomclima Dec 9, 2025
047c8b7
refactor/testing: MAJOR refactoring in scriptanswer acceptance tests
tomclima Dec 9, 2025
b96ef66
testing: comment addition scenarios tests implemented
tomclima Dec 9, 2025
73f5427
testing: retreival by id acceptance tests implemented
tomclima Dec 9, 2025
d9ea32c
feat(TaskAnswer): Adding time sensitive implementation and status to …
DaviGGuerreiro Dec 9, 2025
0826b7f
feat(ScriptAnswers): Adding Time/Status dependencies, Class connectio…
DaviGGuerreiro Dec 9, 2025
71cf05a
fix(mock_scripts): Fixing Mock to implement new objects for postman r…
DaviGGuerreiro Dec 9, 2025
21fe53e
feat(ScriptAnswer): Implementing new routes with validations
DaviGGuerreiro Dec 9, 2025
61f4f6b
Delete(): Erasing old initial implementation
DaviGGuerreiro Dec 9, 2025
621ca95
server doesnt allow new script with title that already exists
hlpaa Nov 20, 2025
6ccf637
server doesnt allow empty script title
hlpaa Nov 20, 2025
54c1c66
client alerts empty script title
hlpaa Nov 20, 2025
ef8310a
Merge pull request #3 from hlpaa/fix/create-script-untitled
hlpaa Dec 9, 2025
b8ee8c3
Merge branch 'main' into corrigir-roteiro-testes
tomclima Dec 9, 2025
b91586d
Merge pull request #5 from hlpaa/corrigir-roteiro
tomclima Dec 9, 2025
7c7afd2
Testing: Testcases working with class reference.
DaviGGuerreiro Dec 9, 2025
34a6c01
new data structure for scripts and new field in ScriptEditor
hlpaa Dec 9, 2025
f40c694
adding "description" to mock
hlpaa Dec 9, 2025
daf4a48
small issue
hlpaa Dec 9, 2025
baad6f4
preventing empty description field
hlpaa Dec 9, 2025
fad5afd
preventing create script with zero tasks
hlpaa Dec 9, 2025
6a1ac8b
BUGFIX: now server check whether or not the scriptanswer being added …
tomclima Dec 9, 2025
229df4a
adding "tasks" to mock
hlpaa Dec 9, 2025
dd23b5d
FEAT: added DELETE route for script deletion
tomclima Dec 9, 2025
82b1c8e
new styling ot TaskListEditor
hlpaa Dec 9, 2025
f7bb2f4
feat(ScriptAnswer): Adding knew implemantation to type and Service fo…
DaviGGuerreiro Dec 9, 2025
79d1046
adding colors to buttons
hlpaa Dec 9, 2025
e637ea5
Merge pull request #7 from hlpaa/feat/create-scripts-improved
tomclima Dec 9, 2025
2429834
fix(TaskAnswer): TaskAnswer cannot be submitted more than once
DaviGGuerreiro Dec 9, 2025
b2519db
fix(ScriptAnswer): scriptAnswers can't receive new taskAnswers Reques…
DaviGGuerreiro Dec 9, 2025
3c58d7b
feat/testing: helper functions for object creation ensure if the obje…
tomclima Dec 9, 2025
3f7b8f3
testing: data-testingid for objects to facilitatde frontend testing o…
tomclima Dec 9, 2025
177a9dc
fix(scriptAnswerService): fixing passage of query elements
DaviGGuerreiro Dec 9, 2025
3175321
feat(Server-scriptAnswer-features): Adding time and Answer features
DaviGGuerreiro Dec 9, 2025
b79d6db
testing: added testids for ease of selection in e2e frontend tests
tomclima Dec 10, 2025
b87b732
fix: uncommented deletion logic in Given('there are no script answers…
tomclima Dec 10, 2025
882d280
testing: functional frontend testing for grade and comment scriptansw…
tomclima Dec 10, 2025
ef86680
fix/testing: removed prepending of ta- in task creation of the server…
tomclima Dec 10, 2025
9a7705f
Merge branch 'main' into corrigir-roteiro
tomclima Dec 10, 2025
fdc5ce7
Merge pull request #8 from hlpaa/corrigir-roteiro
hlpaa Dec 10, 2025
efe097d
feat(ScriptAnswerTest): Adding scriptAnswer test implementation
DaviGGuerreiro Dec 10, 2025
47917e5
feat(scripAnswer): Adding fornt-end implementation
DaviGGuerreiro Dec 10, 2025
311d106
Merge branch 'feat/responder-roteiros'
DaviGGuerreiro Dec 10, 2025
49a79c8
fix(mock): mock difference
DaviGGuerreiro Dec 10, 2025
107300f
HOTFIX: fixed error in which invalid scripts where created by the tes…
tomclima Dec 10, 2025
74a57cf
HOSTFIX: fixed probelm in which scriptgrading unit tests generated in…
tomclima Dec 10, 2025
6c5ae97
fix(AnswerScript): Adding Functions to taskAnswer
DaviGGuerreiro Dec 10, 2025
7f2a75f
uinit test: script and tasks
hlpaa Dec 10, 2025
a5cfd60
integration test scritps routes
hlpaa Dec 10, 2025
b8c82ba
end to end tests scripts
hlpaa Dec 10, 2025
c3f912c
gherkin scenarios script management
hlpaa Dec 10, 2025
3c17635
end to endt tests fix server
hlpaa Dec 10, 2025
4bf1301
adjusting gui end to end test create script
hlpaa Dec 10, 2025
0864439
Merge pull request #9 from hlpaa/test/create-script
hlpaa Dec 11, 2025
6a4b902
Merge branch 'hlpaa:main' into main
DaviGGuerreiro Dec 11, 2025
4fdce1d
fix(scriptAnswerGrid):to contain class id
DaviGGuerreiro Dec 11, 2025
5d87236
fix(): Integrando testes
DaviGGuerreiro Dec 11, 2025
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
16 changes: 16 additions & 0 deletions client/cucumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,21 @@ module.exports = {
},
paths: ['src/features/**/*.feature'],
requireModule: ['ts-node/register']
},
server: {
require: [
'ts-node/register',
'src/step-definitions/server-newScriptAnswer-steps.ts'
],
format: [
'progress-bar',
'json:reports/cucumber_report.json'
],
formatOptions: {
snippetInterface: 'async-await'
},
paths: ['src/features/server-newScriptAnswer-management.feature'],
requireModule: ['ts-node/register'],
timeout: 30000
}
};
406 changes: 205 additions & 201 deletions client/package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"react-scripts": "5.0.1"
},
"devDependencies": {
"@cucumber/cucumber": "^12.2.0",
"@cucumber/cucumber": "^12.3.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
Expand All @@ -18,7 +18,7 @@
"@types/react-dom": "^18.2.7",
"expect": "^27.5.1",
"jest-cucumber": "^4.5.0",
"puppeteer": "^24.30.0",
"puppeteer": "^24.32.0",
"ts-node": "^10.9.2",
"typescript": "^4.9.5"
},
Expand Down
11 changes: 11 additions & 0 deletions client/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ body {
background-color: #fff;
}

.form-group textarea {
width: 100%;
padding: 0.75rem;
border: 2px solid #e2e8f0;
border-radius: 6px;
font-size: 1rem;
transition: all 0.2s ease;
background-color: #fff;
resize: vertical;
}

.form-group input:focus {
outline: none;
border-color: #667eea;
Expand Down
148 changes: 138 additions & 10 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React, { useState, useEffect, useCallback } from 'react';
import { Student } from './types/Student';
import { Class } from './types/Class';
import { studentService } from './services/StudentService';
import ClassService from './services/ClassService';
import StudentList from './components/StudentList';
import StudentForm from './components/StudentForm';
import Evaluations from './components/Evaluations';
import Classes from './components/Classes';
import React, { useCallback, useEffect, useState } from 'react';
import './App.css';
import Classes from './components/Classes';
import Evaluations from './components/Evaluations';
import ScriptsPage from './components/scripts/ScriptsPage';
import GradingPage from './components/ScriptGrading/GradingPage'
import StudentForm from './components/StudentForm';
import StudentList from './components/StudentList';
import StudentScriptAnswerPage from './components/StudentScriptAnswer/StudentScriptAnswerPage';
import ClassService from './services/ClassService';
import { studentService } from './services/StudentService';
import { Class } from './types/Class';
import { Student } from './types/Student';

type TabType = 'students' | 'evaluations' | 'classes';
type TabType = 'students' | 'evaluations' | 'classes' | 'scripts' | 'Script Grading' | 'answer-scripts';

const App: React.FC = () => {
const [students, setStudents] = useState<Student[]>([]);
const [classes, setClasses] = useState<Class[]>([]);
const [selectedClass, setSelectedClass] = useState<Class | null>(null);
const [selectedStudent, setSelectedStudent] = useState<Student | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string>('');
const [editingStudent, setEditingStudent] = useState<Student | null>(null);
Expand Down Expand Up @@ -146,6 +150,25 @@ const App: React.FC = () => {
>
Classes
</button>
<button
className={`tab-button ${activeTab === 'scripts' ? 'active' : ''}`}
onClick={() => setActiveTab('scripts')}
>
Scripts
</button>
<button
className={`tab-button ${activeTab === 'Script Grading' ? 'active' : ''}`}
onClick={() => setActiveTab('Script Grading')}
data-testid="script-grading-tab"
>
Script Grading
</button>
<button
className={`tab-button ${activeTab === 'answer-scripts' ? 'active' : ''}`}
onClick={() => setActiveTab('answer-scripts')}
>
Answer Scripts
</button>
</div>

{/* Tab Content */}
Expand Down Expand Up @@ -215,6 +238,111 @@ const App: React.FC = () => {
onError={handleError}
/>
)}

{activeTab === 'scripts' && (
<ScriptsPage onError={handleError} />
)}

{activeTab === 'Script Grading' && (
<GradingPage onError={handleError} />
)}

{activeTab === 'answer-scripts' && (
<>
{/* Student and Class Selection */}
<div style={{
display: 'flex',
gap: '20px',
marginBottom: '20px',
padding: '20px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: '10px',
color: 'white'
}}>
<div style={{ flex: 1 }}>
<label htmlFor="student-select" style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold' }}>
Select Student:
</label>
<select
id="student-select"
value={selectedStudent?.cpf || ''}
onChange={(e) => {
const student = students.find(s => s.cpf === e.target.value);
setSelectedStudent(student || null);
}}
style={{
width: '100%',
padding: '10px',
borderRadius: '5px',
border: 'none',
fontSize: '16px'
}}
>
<option value="">-- Choose a student --</option>
{students.map((student) => (
<option key={student.cpf} value={student.cpf}>
{student.name} ({student.cpf})
</option>
))}
</select>
</div>

<div style={{ flex: 1 }}>
<label htmlFor="class-select-answer" style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold' }}>
Select Class:
</label>
<select
id="class-select-answer"
value={selectedClass ? `${selectedClass.topic}-${selectedClass.year}-${selectedClass.semester}` : ''}
onChange={(e) => {
const classId = e.target.value;
if (classId) {
const classObj = classes.find(c => `${c.topic}-${c.year}-${c.semester}` === classId);
setSelectedClass(classObj || null);
} else {
setSelectedClass(null);
}
}}
style={{
width: '100%',
padding: '10px',
borderRadius: '5px',
border: 'none',
fontSize: '16px'
}}
>
<option value="">-- Choose a class --</option>
{classes.map((classObj) => (
<option
key={`${classObj.topic}-${classObj.year}-${classObj.semester}`}
value={`${classObj.topic}-${classObj.year}-${classObj.semester}`}
>
{classObj.topic} ({classObj.year}/{classObj.semester})
</option>
))}
</select>
</div>
</div>

{/* Answer Scripts Interface */}
{selectedStudent && selectedClass ? (
<StudentScriptAnswerPage
studentCPF={selectedStudent.cpf}
classId={`${selectedClass.topic}-${selectedClass.year}-${selectedClass.semester}`}
onError={handleError}
/>
) : (
<div style={{
padding: '40px',
textAlign: 'center',
fontSize: '18px',
color: '#666'
}}>
Please select both a student and a class to begin answering scripts.
</div>
)}
</>
)}
</div>
</main>
</div>
Expand Down
58 changes: 58 additions & 0 deletions client/src/components/ScriptGrading/GradingPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// pages/GradingPage.tsx
import React, { useState, useEffect } from "react";
import { ScriptAnswer } from "../../types/ScriptAnswer";
import { ScriptAnswerService } from "../../services/ScriptAnswerService";
import ScriptAnswerGrid from "./ScriptAnswerGrid"
import ScriptGrading from "./ScriptGrading";

interface Props {
onError: (errorMessage: string) => void;
}

export default function GradingPage({onError}: Props) {
const [scriptAnswers, setScriptAnswers] = useState<ScriptAnswer[]>([]);
const [selected, setSelected] = useState<ScriptAnswer | null>(null);

useEffect(() => {
async function fetchData() {
try {
const data = await ScriptAnswerService.getAllScriptAnswers();
setScriptAnswers(data);
} catch (error) {
onError("Failed to load script answers.");
}
}
fetchData();
}, []);

if (selected) {
return (
<ScriptGrading
scriptAnswer={selected}
onClose={() => {
async function setData() {
try {
const data = await ScriptAnswerService.getAllScriptAnswers();
setScriptAnswers(data);
} catch (error) {
onError("Failed to load script answers.");
}
}
setData().then(() => {
setSelected(null);
});
}}
/>
);
}

return (
<div>
<h1 data-testid="grading-page-title">Grade Scripts</h1>
<ScriptAnswerGrid
scriptAnswers={scriptAnswers}
onSelect={(sa) => setSelected(sa)}
/>
</div>
);
}
66 changes: 66 additions & 0 deletions client/src/components/ScriptGrading/ScriptAnswerGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// components/ScriptAnswerGrid.tsx
import React, { useEffect, useState } from "react";
import { ScriptAnswer } from "../../types/ScriptAnswer";
import ScriptService from "../../services/ScriptService";

interface Props {
scriptAnswers: ScriptAnswer[];
onSelect: (s: ScriptAnswer) => void;
}

export default function ScriptAnswerGrid({ scriptAnswers, onSelect }: Props) {
const [scriptTitles, setScriptTitles] = useState<Record<string, string>>({});
const [loading, setLoading] = useState(false);

useEffect(() => {
const loadScriptTitles = async () => {
setLoading(true);
const titles: Record<string, string> = {};

for (const sa of scriptAnswers) {
try {
const script = await ScriptService.getScriptById(sa.scriptId);
titles[sa.scriptId] = script.title || sa.scriptId;
} catch (error) {
console.error(`Failed to load script ${sa.scriptId}:`, error);
titles[sa.scriptId] = sa.scriptId; // Fallback to ID if fetch fails
}
}

setScriptTitles(titles);
setLoading(false);
};

if (scriptAnswers.length > 0) {
loadScriptTitles();
}
}, [scriptAnswers]);

return (
<div style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, 200px)",
gap: "10px"
}} data-testid="script-answer-grid">
{scriptAnswers.map(sa => (
<div
key={sa.id}
data-testid={`script-answer-row-${sa.id}`}
onClick={() => onSelect(sa)}
style={{
padding: "12px",
border: "1px solid #ccc",
borderRadius: "8px",
cursor: "pointer",
opacity: loading ? 0.6 : 1,
}}
>
<strong>Script: {loading ? "Loading..." : scriptTitles[sa.scriptId] || sa.scriptId}</strong><br />
Student: {sa.student}<br />
Class: {sa.classId}<br />
Grade: {sa.grade ?? "—"}
</div>
))}
</div>
);
}
Loading