A simple Spanish language trainer app for anyone that uses the spaced repetition revision technique for a more effective learning experience.
https://spaced-repetition-blue.vercel.app
https://github.com/dionisggr/spaced-repetition-api
- As a prospective user:
- I can register an account so that I can login and use the application
- I'm directed to a registration page
- On that page, I can enter my name, username, and password
- If all of my information is correct, upon clicking the submit button, I'm redirected to my dashboard
- If any of my information is incorrect, I'm given an appropriate error message and the option to correct my information
- I can register an account so that I can login and use the application
- As a registered user:
- I can navigate to the "login" page
- I can login to the application so that I can begin learning
- I can navigate back to the registration page
- I can enter my username and password
- If my submitted username and password are incorrect, I'm given an appropriate error message so that I can attempt to login again
- If my submitted username and password are correct, the app "logs me in" and redirects me to my dashboard
- As a logged in user:
- The application remembers that I'm logged in and doesn't redirect me to the registration page
- I'm directed to a dashboard where I can see my progress learning my language
- The app displays my name and presents a logout button
- The application refreshes my auth token so that I can remain logged in while active on the page
- The app gets my language and words progress from the server
- I'm shown my language
- I'm shown the words to learn for the language
- I'm shown my count for correct and incorrect responses for each word
- I'm given a button/link to start learning
- I'm shown the total score for guessing words correctly
- I can learn words using spaced repetition
- The app gets my next word to learn details from the server
- I'm shown the word to learn
- I'm shown my current total score
- I'm shown the number of correct and incorrect guesses for that word
- I'm presented an input to type my answer/guess for the current words translation
- I can see feedback on my submitted answers.
- The app POSTs my answer for this word to the server
- The server will update my appropriate scores in the database
- After submitting, I get feedback whether I was correct or not
- After submitting, I'm told the correct answer
- My total score is updated
- I'm told how many times I was correct and incorrect for the word
- I can see a button to try another word
- I can learn another word after receiving feedback from my previous answer
- I'm presented with a button that I can click to learn another word
- When clicking on the button I see the next word to learn
- Front-End: React.js, CSS3, HTML5, Javascript, Cypress, API fetch
- Back-End: Javascript, Node.js, Express.js, Knex.js, PostgreSQL, Mocha, Chai, Supertest, Nodemon, Postgrator, Dotenv, JWT, Bcrypt, Morgan, XSS, CORS, Helmet, HTML5, CI scripts
- Development Environment: Vercel, Heroku, DBeaver, Postman
Register Page |
---|
Login Page |
---|
Dashboard |
---|
Word Page |
---|
Correct Answer Page |
---|
Incorrect Answer Page |
---|
The app's functionality includes:
- Any User
- May create an account
- Registered User
- User lands on Dashboard with words and total score information
- May begin quiz and see next words to answer based on previous history
- Is provided correct or incorrect feedback based on answer
- Index.js - (stateless)
- App.js - (stateful)
- Header.js - (stateless)
- DashboardRoute.js - (stateful)
- LearningRoute.js - (stateful)
- LoginForm.js - (stateful)
- Label.js - (stateless)
- Input.js - (stateless)
- Button.js - (stateless)
- LoginForm.js - (stateful)
- RegistrationRoute.js - (stateless)
- RegistrationForm.js - (stateless)
- Required.js - (stateless)
- Label.js - (stateless)
- Input.js - (stateless)
- Button.js - (stateless)
- RegistrationForm.js - (stateless)
- LoginRoute.js - (stateless)
- NotFoundRoute.js - (stateless)
- Footer.js - (stateless)
- App.js - (stateful)
- User (database table)
- id (serial, primary key)
- username (unique text, not null)
- password (text, not null)
- name (text, not null)
- Language (database table)
- id (text, primary key)
- name (text, not null)
- total_score (smallint, default 0)
- user_id (integer, foreign key [user.id])
- Word (database table)
- id (serial, primary key)
- original (text, not null)
- translation (text, not null)
- memory_value (smallint, default 0)
- correct_count (smallint, default 0)
- incorrect_count (smallint, default 0)
- language_id (text, foreign key [language.id])
- next (integer, foreign key [word.id])
Closed endpoints that require a valid username and password to be included in the header body of the request.
- Step 1: (Generate JSON Web Token)
POST /api/token
- 'Admin' credentials
- Username:
dwight
- Password:
pass
- Username:
- 'Admin' credentials
- Step 2: <Use generated JSON Web Token (3 hrs)>
- Step 3 (Optional): Refresh JSON Web Token
PUT /api/token
Closed endpoints that require a valid JSON Web Token to be inlcuded in the header 'Authorization' of the request.
// Add to request header
headers: {'Authorization': 'Bearer <JSON Web Token>'}
If sending content through request body (POST
PUT
), don't forget to add the following in the headers:
// Add to request header
headers" {'Content-Type': 'application/json'}
Each endpoint manipulates information related to users.
- Create User (Register):
POST /api/user
URL: /api/user
Method: POST
Auth required: No
Bearer my-secret-key
Requires headers: {'Content-Type': 'application/json'}
{
"name": "Dwight Schrute"
"username": "dwight",
"password": "pass"
}
Name | Type | In | Description |
---|---|---|---|
id |
integer | header | Primary key |
username |
string | header | Unique username |
name |
string | header | First name of user |
password |
string | header | User password |
Code: 201 Created
Content example
{
"id": 1,
"username": "dwight",
"name": Dwight Schrute
}
Each endpoint manipulates information about languages, words and guess/answer feedback.
- Get Language Words:
GET /api/language
- Get Next Word:
POST /api/language/head
- Get Guess/Answer Feedback:
GET /api/language/guess
URL: /api/language
Method: GET
Auth required: Yes
Bearer <JSON Web Token>
Requires headers: {'Content-Type': 'application/json'}
Code: 200 OK
Content example
{
"nextWord": "casa",
"totalScore": 2,
"wordCorrectCount": 0,
"wordIncorrectCount": 0,
"answer: "house",
isCorrect: true,
}
URL: /api/language/head
Method: GET
Auth required: Yes
Bearer <JSON Web Token>
Requires headers: {'Content-Type': 'application/json'}
Code: 200 OK
Content example
{
"nextWord": "casa",
"totalScore": 2,
"wordCorrectCount": 0,
"wordIncorrectCount": 0,
"answer: "house",
isCorrect: true,
}
URL: /api/language/guess
Method: POST
Auth required: Yes
Bearer <JSON Web Token>
Requires headers: {'Content-Type': 'application/json'}
{
"name": "Dwight Schrute"
"username": "dwight",
"password": "pass"
}
Name | Type | In | Description |
---|---|---|---|
nextWord |
text | header | Next word after guess |
totalScore |
integer | header | Score among all words guess |
wordCorrectCount |
integer | header | Word correct answer score |
wordIncorrectCount |
integer | header | Word incorrect answer score |
answer |
text | header | Correct answer |
isCorrect |
boolean | header | Feedback for previous guess |
Code: 200 OK
Content example
{
"nextWord": "casa",
"totalScore": 2,
"wordCorrectCount": 0,
"wordIncorrectCount": 0,
"answer: "house",
isCorrect: true,
}
Each endpoint manipulates information user registration, login and authorization token refresh.
- Login:
POST /api/auth/token
- Refresh Token:
PUT /api/auth/token
- Registration:
GET /api/user
URL: /api/auth/token
Method: POST
Auth required: Yes
{
"username": "dwight",
"password": "password",
}
Name | Type | In | Description |
---|---|---|---|
username |
string | header | Unique username |
password |
string | header | User password |
Code: 200 OK
Content example
{
"authToken": <JSON Web Token>;
}
URL: /api/auth/token
Method: PUT
Auth required: Yes
Bearer <JSON Web Token>
Code: 200 OK
Content example
{
"authToken": <JSON Web Token>;
}
URL: /api/user
Method: POST
Auth required: No
Bearer my-secret-key
Requires headers: {'Content-Type': 'application/json'}
{
"name": "Dwight Schrute"
"username": "dwight",
"password": "pass"
}
Name | Type | In | Description |
---|---|---|---|
id |
integer | header | Primary key |
username |
string | header | Unique username |
name |
string | header | First name of user |
password |
string | header | User password |
Code: 201 Created
Content example
{
"id": 1,
"username": "dwight",
"name": Dwight Schrute
}
Register Page |
---|
Login Page |
---|
Dashboard |
---|
Word Page |
---|
Correct Answer Page |
---|
Incorrect Answer Page |
---|
This is v1.0 of the app, but future enhancements are expected to include:
- More user account management functionalities
- More languages and words
- User may add words to languages
To setup the application
- Fork and clone the project to your machine
npm install
. This will also install the application Cypress.io for running browser integration tests
The project expects you have the Spaced repetition API project setup and running on http://localhost:8000.
Find instructions to setup the API here https://github.com/Thinkful-Ed/spaced-repetition-api.
This is a create-react-app
project so npm start
will start the project in development mode with hot reloading by default.
This project uses Cypress IO for integration testing using the Chrome browser.
Cypress has the following expectations:
- You have cypress installed (this is a devDependency of the project)
- You have your application running at http://localhost:3000.
- You can change the address of this expectation in the
./cypress.json
file.
- You can change the address of this expectation in the
- Your
./src/config.js
is using http://localhost:8000/api as theAPI_ENDPOINT
To start the tests run the command:
npm run cypress:open
On the first run of this command, the cypress application will verify its install. Any other runs after this, the verification will be skipped.
The command will open up the Cypress application which reads tests from the ./cypress/integration/
directory. You can then run individual tests by clicking on the file names or run all tests by clicking the "run all tests" button in the cypress GUI.
Tests will assert against your running localhost client application.
You can also start all of the tests in the command line only (not using the GUI) by running the command:
npm run cypress:run
This will save video recordings of the test runs in the directory ./cypress/videos/
.
### Configuring Postgres
For tests involving time to run properly, configure your Postgres database to run in the UTC timezone.
1. Locate the `postgresql.conf` file for your Postgres installation.
1. E.g. for an OS X, Homebrew install: `/usr/local/var/postgres/postgresql.conf`
2. E.g. on Windows, _maybe_: `C:\Program Files\PostgreSQL\11.2\data\postgresql.conf`
3. E.g on Ubuntu 18.04 probably: '/etc/postgresql/10/main/postgresql.conf'
2. Find the `timezone` line and set it to `UTC`:
```conf
# - Locale and Formatting -
datestyle = 'iso, mdy'
#intervalstyle = 'postgres'
timezone = 'UTC'
#timezone_abbreviations = 'Default' # Select the set of available time zone