Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
3e9c601
testing
katelynlai Jul 28, 2025
1e6070e
testing
katelynlai Jul 28, 2025
99c72db
Merge development branch into salma branch
katelynlai Jul 28, 2025
1b1a9ce
User dashboard construction
DominicSimpson Jul 28, 2025
098d65a
CSS added, work continuing on user dashboard
DominicSimpson Jul 28, 2025
a3b3b15
adding signup and login pages
salmaelt Jul 28, 2025
a0a82dc
Merge branch 'salma' of https://github.com/katelynlai/SyntaxSchoolers…
DominicSimpson Jul 28, 2025
1cc732f
Updated user dashboard page
DominicSimpson Jul 28, 2025
f492974
Work on user dashboard
DominicSimpson Jul 28, 2025
362c3f2
User dashboard page development
DominicSimpson Jul 28, 2025
ebfa8be
Continuing work on student dashboard
DominicSimpson Jul 28, 2025
2c2fa0f
Tiedied up HTML
DominicSimpson Jul 28, 2025
b63c108
User dashboard provisionally done
DominicSimpson Jul 28, 2025
af2aaae
Font fixed on user dashboard page
DominicSimpson Jul 28, 2025
33d65ba
Added teacher dashboard page
DominicSimpson Jul 28, 2025
c0bfc69
adding css for homepage
salmaelt Jul 28, 2025
c2047ae
Centered text
DominicSimpson Jul 28, 2025
d93147f
adding sections to front end
salmaelt Jul 28, 2025
7040cb5
Resolved conflicts
DominicSimpson Jul 28, 2025
3735f3f
tidied up HTML
DominicSimpson Jul 28, 2025
7c5971b
Cleaning up CSS
DominicSimpson Jul 28, 2025
32f1acb
Working on dashboard pages
DominicSimpson Jul 29, 2025
e15eae7
Student Dashboard page provisionally finished
DominicSimpson Jul 29, 2025
58f666d
Cleaned up HTML and CSS
DominicSimpson Jul 29, 2025
cd812e2
Work on teacher dashboard page
DominicSimpson Jul 29, 2025
8137aec
Continuing work on teacher dashboard
DominicSimpson Jul 29, 2025
a0c3a91
Added root CSS commands to student dashboard
DominicSimpson Jul 29, 2025
b34d717
Teacher dashboard page nearly there
DominicSimpson Jul 29, 2025
7bd6cad
Cleaning up HTML visually
DominicSimpson Jul 29, 2025
40d6db8
Tidying up HTML visually
DominicSimpson Jul 29, 2025
bb8d2f2
Working on stakeholder and solutions analysis in readme
DominicSimpson Jul 29, 2025
6f626c4
Testing readme functionality
DominicSimpson Jul 29, 2025
ea4d2bd
Stakeholders completed
DominicSimpson Jul 29, 2025
8b4fe19
Work commences on ERD
DominicSimpson Jul 29, 2025
5bd9930
Testing Mermaid
DominicSimpson Jul 29, 2025
bf3dd8e
Working on mermaid.js ERD
DominicSimpson Jul 29, 2025
36cb651
Continuing work on ERD in mermaid.js
DominicSimpson Jul 29, 2025
297344b
Tables established in ERD
DominicSimpson Jul 29, 2025
8b8203e
ERD provisionally finished on review
DominicSimpson Jul 29, 2025
869541e
readme updated
DominicSimpson Jul 29, 2025
6af5a7d
Updated readme
DominicSimpson Jul 29, 2025
976b60c
ERD updated
DominicSimpson Jul 30, 2025
af4a73a
Work on expanding number of stakeholders
DominicSimpson Jul 30, 2025
e628576
Work on stakeholder stories
DominicSimpson Jul 30, 2025
33b72bd
Finished stakeholder stories
DominicSimpson Jul 30, 2025
74e2070
Stakeholder analysis diagram updated
DominicSimpson Jul 30, 2025
0af2cac
Rish Assessment grid added
DominicSimpson Jul 30, 2025
3dff91b
Updating READme
DominicSimpson Jul 30, 2025
ae1f78b
Adding readme material
DominicSimpson Jul 30, 2025
0fdb376
Jest testing installed on frontend
DominicSimpson Jul 30, 2025
e1bc409
Jest set up correctly
DominicSimpson Jul 30, 2025
1c417a5
Remove node_modules from remote repository
DominicSimpson Jul 30, 2025
f0a1ae7
Signup page updated
DominicSimpson Jul 30, 2025
d59fae3
Added authentication testing folder
DominicSimpson Jul 30, 2025
4041930
Testing signup form
DominicSimpson Jul 30, 2025
38f92c4
First Jest tests pass
DominicSimpson Jul 30, 2025
f85847e
Got Cypress Testing installed and passing
DominicSimpson Jul 30, 2025
88f18a0
Signup and login tests completed, logout to go
DominicSimpson Jul 30, 2025
9e18182
package.json files updated
DominicSimpson Jul 30, 2025
9a902bf
login.cy.js passes tests
DominicSimpson Jul 30, 2025
33fcef3
Logout testing passes
DominicSimpson Jul 30, 2025
504538a
Front end testing complete with signup page passing
DominicSimpson Jul 30, 2025
bdc6f5d
Modified test to account for whether teacher or student
DominicSimpson Jul 30, 2025
8386dd0
Modified signup testing
DominicSimpson Jul 30, 2025
22df6b8
Frontend testing completed
DominicSimpson Jul 30, 2025
0a33e6f
Changed teacher_dashboard.html to staff_dashboard.html
DominicSimpson Jul 31, 2025
c503cc3
Changed staff_dashboard.html page to have interactivity
DominicSimpson Jul 31, 2025
40042bf
Changed file structure to staff_dashboard and user_dashboard folders
DominicSimpson Jul 31, 2025
237c5fc
Updated Cypress tests all work
DominicSimpson Jul 31, 2025
c55dba1
Setting up backend on branch
DominicSimpson Jul 31, 2025
9454bcc
Remove node_modules from repository and add to .gitignore
DominicSimpson Jul 31, 2025
e3e38ef
node_modules should now be ignored
DominicSimpson Jul 31, 2025
02bbb74
Added .gitignore file
DominicSimpson Jul 31, 2025
2f1d4a6
Placed .gitignore in global root folder of project
DominicSimpson Jul 31, 2025
dc72ae4
Backend files added for dashboard testing
DominicSimpson Jul 31, 2025
0714321
Put .env in correct location
DominicSimpson Jul 31, 2025
c90a3e2
Updated app.js
DominicSimpson Jul 31, 2025
ff616b4
Sorted out backend problems
DominicSimpson Jul 31, 2025
d672705
Installed Jest, started on testing staff dashboard
DominicSimpson Jul 31, 2025
5565619
Tests working on some of staff_dashboard.html in Jest
DominicSimpson Jul 31, 2025
7b8129c
Supabase table up and running
DominicSimpson Jul 31, 2025
728ce4c
Deleted authentication_testing file
DominicSimpson Jul 31, 2025
1578864
All Jest tests pass for staff dashboard
DominicSimpson Jul 31, 2025
ca443dd
login Cypress testing redone and now works
DominicSimpson Jul 31, 2025
b6a87fb
All Cypress Testing tests work
DominicSimpson Jul 31, 2025
b7ef37a
Cypress Testing complete
DominicSimpson Jul 31, 2025
36d1d9a
Added comments. All tests done
DominicSimpson Jul 31, 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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
backend/node_modules/
frontend/node_modules/
.DS_Store
package-lock.json
backend/.env
56 changes: 54 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,54 @@
# Syntax Schoolers
hello
# Syntax Schoolers: Project Alpha

<img src="frontend/assets/VivaLingo_homepagescreenshot.png">

### For our full-stack project which consolidates all the technical skills that we have learnt in the last four weeks at La Fosse, we have created an MVP (Minimum Viable Product) called VivaLingo - an educational app that allows schoolchildren to learn French.

The aim of the app is to provide fun and engaging way of learning a non-STEM subject. The app allows for the translating of basic concepts between English and French.

## Stakeholder & Solutions Analysis

Here are some examples of stakeholder stories from those who would have an interest in the app.
- [ ] As a student, I want a fun, simple language learning app, so that learning a language can be an engaging, repeatable, and memory-friendly experience, without being bombarded with overwhelming amounts of content and textbooks.
- [ ] As a teacher, I want a simple language learning app for my students to use, so that they can retain key phrases through repetition with increasing challenges, while not getting bored or feeling like learning a foreign language is a chore. This app should provide a respite from facing lots of focus on STEM subjects, to the detriment of a greater knowledge of subjects such as languages and geography.
- [ ] As a product owner, I want to manage the production lifecycle of a simple, fun language learning app for teenagers, so that an alternative is available when compared to other apps that contain an overwhelming amount of content and visuals. I want to ensure that the production lifecycle is delivered on time, and that it receives positive feedback in the Beta phase.
- [ ] As a CEO of the Hive Schools, I want to see my pupils embrace a holistic education that includes retaining key knowledge and critical thinking so that they have a well-rounded education that isn't just focused on STEM subjects. I would hope that this app can achieve that.
- [ ] As a Government Department for Education policy advisor, I want to see students in schools engage in a language learning app that they can enjoy using, so that they can build on their foreign languages skills to reach targets such as key stages 2 and 3 under the national curriculum.
- [ ] As a marketing manager, I want to to see young people enjoy using our app, and find it effective in building on their language skills, so that I can spread the word on the effectiveness of the app in our marketing campaigns.
- [ ] As a parent, I want to see my son/daughter enjoy their education and have fun learning foreign languages while not being overwhelmed with content, so that they can have a well-rounded education that isn't just about technical knowledge in STEM subjects.
- [ ] As a software developer, I want to build a language learning app that offers an enjoyable, intuitive UX experience for students, with a well-structured codebase built around MVC architecture, so that students can enjoy using my app to learn a foreign language, rather than it feeling like a chore. The ease of navigating the app should be important, bearing in mind the target audience.

A visualisation of the importance of each stakeholder can be viewed in this stakeholder analysis diagram. The teacher and student remain the most fundamentally importance in utilising the app accordingly.

<img src="frontend/assets/VivaLingo_stakeholderanalysischart_screenshot.png">


## Risk Analysis

Here are some examples of risks and market analysis to the project.

<img src="frontend/assets/VivaLingo_RiskAssessmentGrid_screenshot.png">

## Entity Relationship Diagram (ERD)

The database structure for this app can be visualised as follows. This ERD was created with Mermaid.js.

<img src="frontend/assets/VivaLingo_ERD_screenshot.png">

## Project Management

We used Trello to structure our work in terms of tasks completed.

<img src="frontend/assets/VivaLingo_Trello_screenshot.png">

## Technical Architecture

User Interface: HTML, CSS, JavaScript, Jest
Backend Services: Node, Express
Data Storage: SQL, Supabase
Data Schema visualisation: Mermaid
Infrastructure: Git

## Development Challenges


13 changes: 13 additions & 0 deletions backend/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const express = require('express');
const cors = require('cors');
const staffRoutes = require('./routers/staffRoutes.js');

const app = express();

app.use(cors());
app.use(express.json());

// Mount all staff routes at /api/staff
app.use('/api/staff', staffRoutes);

module.exports = app;
110 changes: 110 additions & 0 deletions backend/controllers/level1Controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const Level = require('../models/Level1');

class SimpleLevelController {
// POST /api/levels/1/start - Start Level 1 (stateless)
static async startLevel1(req, res) {
try {
const userId = 1;
// Ensure progress row exists and get questions
const result = await Level.startLevel1(userId);
res.status(200).json({
success: true,
message: 'Level 1 started',
data: {
questions: result.questions,
totalQuestions: result.questions.length
}
});
} catch (error) {
console.error('Error starting Level 1:', error);
res.status(500).json({
success: false,
message: 'Failed to start Level 1'
});
}
}

// POST /api/levels/1/submit - Submit all answers at once
static async submitLevel1(req, res) {
try {
const { answers, userId } = req.body; // Array of {englishId, frenchId} pairs
console.log('recieved userId' , userId)
console.log('Received answers:', answers);
//console.log('Question sample:', questions[0]);

// Get questions to validate against
const questions = await Level.getLevel1Questions();

// Count correct answers
let correctCount = 0;
answers.forEach(answer => {
if (answer.englishId === answer.frenchId) {
correctCount++;
}
});

// Save to database
const result = await Level.submitLevel1Answers(userId, correctCount, questions.length);


// Update overallprogress: set level_1_complete = true
const db = require('../database/connect');
await db.query(`
INSERT INTO overallprogress (user_id, level_1_complete)
VALUES ($1, true)
ON CONFLICT (user_id) DO UPDATE SET level_1_complete = true
`, [userId]);




const percentage = Math.round((correctCount / questions.length) * 100);

res.status(200).json({
success: true,
message: 'Level 1 completed!',
data: {
correctAnswers: correctCount,
totalQuestions: questions.length,
percentage: percentage,
...result
}
});
} catch (error) {
console.error('Error submitting Level 1:', error);
res.status(500).json({
success: false,
message: 'Failed to submit Level 1'
});
}
}

// GET /api/levels/1/progress - Get saved progress
static async getLevel1Progress(req, res) {
try {
const userId = req.user.id;
const progress = await Level.getLevel1Progress(userId);

if (!progress) {
return res.status(404).json({
success: false,
message: 'No progress found'
});
}

res.status(200).json({
success: true,
message: 'Progress retrieved',
data: progress
});
} catch (error) {
console.error('Error getting progress:', error);
res.status(500).json({
success: false,
message: 'Failed to get progress'
});
}
}
}

module.exports = SimpleLevelController;
42 changes: 42 additions & 0 deletions backend/controllers/level2Controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const Level2 = require('../models/Level2');
const ProgressL2 = require('../models/ProgressL2')


async function getRandomSentence(req, res) {
try {
const sentence = await Level2.getRandomSentence(); // model function
if (!sentence) {
return res.status(404).json({ error: 'No sentence found' });
}
res.json(sentence);
} catch (err) {
console.error('Error fetching sentence:', err);
res.status(500).json({ error: 'Internal Server Error' });
}
}

async function submitSentence(req, res) {
try {
const { sentenceId, answer, userId, levelId} = req.body;

if (!sentenceId || !answer) {
return res.status(400).json({ error: 'Missing sentenceId or answer' });
}

const isCorrect = await Level2.checkAnswer(sentenceId, answer);

const levelStatus = await Level2.updateLevelProgress(userId, levelId, isCorrect);

// If level is now completed, mark it in overall progress
if (levelStatus === true) {
await ProgressL2.markLevelComplete(userId, levelId);
}

res.json({ correct: isCorrect, levelStatus });
} catch (err) {
console.error('Error checking answer:', err);
res.status(500).json({ error: 'Internal Server Error' });
}
}

module.exports = { getRandomSentence, submitSentence };
63 changes: 63 additions & 0 deletions backend/controllers/level3Controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

const Level3 = require('../models/Level3'); // adjust filename if different
const Progress = require('../models/Progress')

async function getRandomSentence(req, res) {
try {
const sentence = await Level3.getRandomSentence();
if (!sentence) {
return res.status(404).json({ error: 'No sentence found' });
}
res.json({
sentence_id: sentence.sentence_id,
english: sentence.english,
shuffled: sentence.shuffled,
category_id: sentence.category_id
});
} catch (err) {
console.error('Error in getRandomSentence:', err);
res.status(500).json({ error: 'Internal server error' });
}
}

async function submitSentence(req, res) {
try {
const { sentenceId, sentence, userId, levelId } = req.body; // sentence is an array of words

if (!sentenceId || !Array.isArray(sentence)) {
return res.status(400).json({ error: 'Invalid request body' });
}

// Fetch the correct sentence from DB (model function)
const correctSentence = await Level3.getSentenceById(sentenceId);
if (!correctSentence) {
return res.status(404).json({ error: 'Sentence not found' });
}

// Compare user submitted sentence to correct answer (French)
const correctWords = correctSentence.french.trim().split(/\s+/);
const isCorrect = sentence.length === correctWords.length &&
sentence.every((word, i) => word === correctWords[i]);

// Update or create level progress, return updated status (model function)
const levelStatus = await Level3.updateLevelProgress(userId, levelId, isCorrect);

// If level is now completed, mark it in overall progress
if (levelStatus === true) {
await Progress.markLevelComplete(userId, levelId);
}

// Respond with correctness and updated level status
res.json({ correct: isCorrect, levelStatus });

} catch (error) {
console.error('Error in submitSentence:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
}


module.exports = {
getRandomSentence,
submitSentence,
};
Loading