Based on the follwoing instruction Test Technical Task: French Gender Learning Game
Objective
Create a game that helps users learn the gender of French words. The game will show a French word with examples of usage and ask the user to select the correct article ("UN" or "UNE", "LE" or "LA" randomly). The user's selection will be checked for correctness and the end of the word will be highlighted in red or green to indicate correctness, if the word does not match any rule from Appendix A highlight the whole word. The game will also allow users to click on a word to view its translation and examples of usage.

!!! IT'S A TEST TASK, and Never will go to production. If something is not clear,  just create how you see the solution.
Technologies
Frontend: Bootstrap 5, Vue.js, Nuxt 3
Backend: NestJS  (or Node.js if not familiar with nestjs)
Database: PostgreSQL (or any other SQL database)
Containerization: Docker (docker-compose)


!!! Feel free to use boilerplates, tutorials, ChatGPT. The goal of the task is to show how you understand technologies and can use them. 
Features
User authentication: Users will be able to create accounts and log in.
Word database: A database of French words and their genders will be created and populated using PostgreSQL. (list of words in Appendix A)
Game interface: The game will have a simple and intuitive interface, with a French word displayed and a prompt to select the correct article. Users can also click on a word to view its translation and examples of usage.
Feedback mechanism: After the user makes their selection, the game will provide immediate feedback by highlighting the end of the word in red or green to indicate correctness.
Progress tracking: User progress will be tracked and saved in the database. Authenticated Users will be able to see their progress in a list of unknown words, well-known words, and words that need to be learned. 
Mobile-responsive design: The game will be mobile-responsive and work well on different screen sizes.
Requirements
The game must be built using NestJS (or NodeJS/Express) for the backend and Vue.js / Nuxt  and Bootstrap 5 for the frontend.
The game must use PostgreSQL as the database.
The game must have a database of French words and their genders.
The game must have a simple and intuitive interface.
The game must provide immediate feedback to the user after making a selection.
The game must be mobile-responsive.
The game must allow users to click on a word to view its translation and examples of usage.
The game must be containerized using Docker.
The code should be Linted (es-lint/ts-lint).
Not required but will be plus to have transitions, animation. Have some unit tests.
Deliverables
A GitHub repository with the source code of the project.
A README file with instructions on how to run the game locally and how to deploy it using Docker. For DEV and PROD (only as example) purposes.
Conclusion
This technical task outlines the requirements and features for a French gender learning game. By following this task, the developers can implement a complete project using NestJS or Node.js/Express.js, Vue.js (Nuxt 3), Bootstrap 5, and PostgreSQL(or any other SQL). The game will be containerized using Docker for easy deployment. The total development time for this project is estimated to be 1 week.


Appendix A


Here is an example JSON object that contains the gender endings for French nouns:


{
  "masculineEndings": [
    "age",
    "aire",
    "isme",
    "ment",
    "oir",
    "sme",
    "é"
  ],
  "feminineEndings": [
    "ade",
    "ance",
    "ence",
    "ette",
    "ie",
    "ine",
    "ion",
    "ique",
    "isse",
    "ité",
    "lle",
    "ure"
  ]
}




asuming the game is built on react and redux with the backend already setup correct the following frontend GamePage component /* eslint-disable no-unused-vars */
/* eslint-disable no-undef */
import { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
  fetchWord,
  selectCurrentWord,
  selectIsCorrect,
  selectScore,
  selectTranslation,
  submitAnswer,
  fetchTranslation,
} from '../slices/wordsSlice';
import '../index.css';

function GamePage() {
  const dispatch = useDispatch();
  const currentWord = useSelector(selectCurrentWord);
  const isCorrect = useSelector(selectIsCorrect);
  const score = useSelector(selectScore);
  const translation = useSelector(selectTranslation);
  const [selected, setSelected] = useState('');
  const [result, setResult] = useState('');

  useEffect(() => {
    dispatch(fetchWord());
  }, []);

  const handleAnswer = () => {
    dispatch(submitAnswer(selected));
    setSelected('');
  };

  const handleTranslation = () => {
    console.log('Inside handleTranslation', currentWord.word);
    dispatch(fetchTranslation(currentWord?.word));
  };

  return (
    <div className='Game'>
      <h1>French Word Game</h1>
      <p>
        <span onClick={handleTranslation}>{currentWord?.word}</span>
      </p>
      {translation && (
        <div className='translation'>
          <p>{translation}</p>
        </div>
      )}
      <p>Select the correct article:</p>
      <div className='buttons'>
        <button
          className={selected === 'LE' ? 'selected' : ''}
          onClick={() => setSelected('LE')}
        >
          UN/LE
        </button>
        <button
          className={selected === 'LA' ? 'selected' : ''}
          onClick={() => setSelected('LA')}
        >
          UNE/LA
        </button>
      </div>
      <button disabled={!selected} onClick={handleAnswer}>
        Submit
      </button>
      {isCorrect !== null && (
        <div className={result + (isCorrect ? 'correct' : 'wrong')}>
          {isCorrect ? 'Correct!' : 'Wrong!'}
          {!isCorrect && (
            <span className='correct-answer'>
              The correct article{' '}
              <span className='correct-article'>
                {currentWord?.gender === 'M' ? 'LE' : 'LA'}
              </span>{' '}
              {currentWord?.word.endsWith('e') ? 'is' : 'begins with'}{' '}
              <span className='correct-ending'>
                {currentWord?.word?.endsWith('e') ? '' : '…'}
                {currentWord?.word?.slice(-2)}
              </span>
              .
            </span>
          )}
        </div>
      )}
      <p>Score: {score}</p>
    </div>
  );
}

export default GamePage;
and wordSlice import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';

const baseUrl = 'http://localhost:5000/api/v1';

//to test logic from the frontend
const masculineEndings = ['age', 'aire', 'isme', 'ment', 'oir', 'sme', 'é'];
const feminineEndings = [
  'ade',
  'ance',
  'ence',
  'ette',
  'ie',
  'ine',
  'ion',
  'ique',
  'isse',
  'ité',
  'lle',
  'ure',
];

const initialState = {
  word: null,
  currentWordIndex: 0,
  isCorrect: null,
  score: 0,
  correctArticle: null,
  correctEnding: null,
  translation: null,
};

export const wordsSlice = createSlice({
  name: 'words',
  initialState,
  reducers: {
    setWord: (state, action) => {
      console.log('Action', action.payload);
      state.word = action.payload.word;
      state.currentWordIndex = action.payload.index;
      state.isCorrect = null;
      state.correctArticle = null;
      state.correctEnding = null;
      state.translation = null;
    },
    setCorrect: (state) => {
      state.isCorrect = true;
      state.score++;
    },
    setWrong: (state) => {
      state.isCorrect = false;
    },
    setCorrectArticle: (state, action) => {
      state.correctArticle = action.payload.article;
      state.correctEnding = action.payload.ending;
    },
    setTranslation: (state, action) => {
      state.translation = action.payload.translation_en;
    },
  },
});

const config = {
  headers: {
    Authorization: `Bearer ${
      JSON.parse(localStorage.getItem('userInfo')).accessToken
    }`,
  },
};

export const fetchWord = () => async (dispatch) => {
  try {
    const response = await axios.get(`${baseUrl}/words/random`, config);
    dispatch(setWord(response.data));
    console.log('Inside fetch', response.data);
  } catch (error) {
    console.error(error);
  }
};

export const selectCurrentWord = (state) => state.word;
export const selectIsCorrect = (state) => state.isCorrect;
export const selectScore = (state) => state.score;
export const selectTranslation = (state) => state.translation;

export const submitAnswer = (selected) => (dispatch, getState) => {
  const { word, gender } = selectCurrentWord(getState());
  const isCorrect = selected === (gender === 'masculine' ? 'LE' : 'LA');
  if (isCorrect) {
    dispatch(setCorrect());
  } else {
    dispatch(setWrong());
    let correctArticle = gender === 'masculine' ? 'LE' : 'LA';
    let correctEnding = '';
    for (let ending of gender === 'masculine'
      ? masculineEndings
      : feminineEndings) {
      if (word.endsWith(ending)) {
        correctEnding = ending;
        break;
      }
    }
    if (!correctEnding) {
      correctEnding = word;
    }
    dispatch(
      setCorrectArticle({ article: correctArticle, ending: correctEnding })
    );
  }
  dispatch(fetchTranslation(word));
  dispatch(nextWord());
};

export const fetchTranslation = (word) => async (dispatch) => {
  try {
    const response = await axios.get(
      `${baseUrl}/words/example?word=${word}`,
      config
    );
    dispatch(setTranslation(response.data.example.translation_en));
  } catch (error) {
    console.error(error);
  }
};

// actions
export const {
  setWord,
  setCorrect,
  setWrong,
  setCorrectArticle,
  setTranslation,
} = wordsSlice.actions;

// async thunks
export const nextWord = () => (dispatch) => {
  dispatch(fetchWord());
};

export default wordsSlice.reducer;