Finish the below code Here’s an updated version of the GameController that uses tables of masculine and feminine nouns fetched from PostgreSQL using TypeORM and a words database with the specified structure:

import { Controller, Get, Req } from ‘@nestjs/common’;
import { InjectRepository } from ‘@nestjs/typeorm’;
import { Repository } from ‘typeorm’;
import { MasculineNoun } from ‘./entities/masculine-noun.entity’;
import { FeminineNoun } from ‘./entities/feminine-noun.entity’;
import { Word } from ‘./entities/word.entity’;

@Controller(‘game’)
export class GameController {
constructor(
@InjectRepository(MasculineNoun)
private masculineNounRepository: Repository<MasculineNoun>,
@InjectRepository(FeminineNoun)
private feminineNounRepository: Repository<FeminineNoun>,
@InjectRepository(Word)
private wordRepository: Repository<Word>,
) {}

@Get(‘word’)
async getWord(@Req() req) {
const randomWord = await this.wordRepository
.createQueryBuilder(‘word’)
.orderBy(‘RANDOM()’)
.getOne();
const wordWithTranslation = { …randomWord };
delete wordWithTranslation.translation_fr;
return JSON.stringify(wordWithTranslation);
}

@Get(‘translation’)
async getTranslation(@Req() req) {
const randomWord = await this.wordRepository
.createQueryBuilder(‘word’)
.orderBy(‘RANDOM()’)
.getOne();
return JSON.stringify({ en: randomWord.translation_en });
}

@Get(‘masculine-nouns’)
async getMasculineNouns(@Req() req) {
const nouns = await this.masculineNounRepository.find();
return JSON.stringify(nouns);
}

@Get(‘feminine-nouns’)
async getFeminineNouns(@Req() req) {
const nouns = await this.feminineNounRepository.find();
return JSON.stringify(nouns);
}
}

The updated GameController uses @InjectRepository to inject the repositories for the MasculineNoun, FeminineNoun, and Word entities. It then uses createQueryBuilder to get a random word from the Word table, strip the translation_fr property, and return it. The getTranslation method does the same but returns only the English translation. Additionally, it includes methods to fetch all the irregular masculine and feminine nouns from their respective tables.
Now update the react and redux based on the above GameController corrections
Here’s an updated version of the React and Redux code based on the corrections made to the GameController:

1. The configureStore.js file should import the new wordSlice reducer:

import { configureStore } from ‘@reduxjs/toolkit’;
import wordsReducer from ‘…/slices/wordsSlice’;

export default configureStore({
reducer: {
words: wordsReducer,
},
});

2. The wordsSlice.js file should be updated to use the new /game/masculine-nouns and /game/feminine-nouns API endpoints to fetch the lists of nouns and update the initial state of the slice accordingly. It should also use the /game/word and /game/translation endpoints to fetch a random word and its translation:

import { createSlice } from ‘@reduxjs/toolkit’;
import axios from ‘…/…/api/api’;

const initialState = {
word: null,
nounList: {
masculine: [],
feminine: [],
},
isCorrect: null,
score: 0,
};

export const wordsSlice = createSlice({
name: ‘words’,
initialState,
reducers: {
setWord: (state, action) => {
state.word = action.payload;
state.isCorrect = null;
},
setNouns: (state, action) => {
state.nounList = action.payload;
},
setCorrect: (state) => {
state.isCorrect = true;
state.score++;
},
setWrong: (state) => {
state.isCorrect = false;
},
},
});

export const { setWord, setNouns, setCorrect, setWrong } = wordsSlice.actions;

export const selectWord = (state) => state.words.word;
export const selectNouns = (state) => state.words.nounList;
export const selectIsCorrect = (state) => state.words.isCorrect;
export const selectScore = (state) => state.words.score;

export const fetchWord = () => async (dispatch) => {
try {
const response = await axios.get(‘/game/word’);
dispatch(setWord(response.data));
} catch (error) {
console.error(error);
}
};

export const fetchNouns = () => async (dispatch) => {
try {
const [masculineResponse, feminineResponse] = await Promise.all([
axios.get(‘/game/masculine-nouns’),
axios.get(‘/game/feminine-nouns’),
]);
dispatch(
setNouns({
masculine: masculineResponse.data,
feminine: feminineResponse.data,
})
);
} catch (error) {
console.error(error);
}
};

export const fetchTranslation = () => async (dispatch) => {
try {
const response = await axios.get(‘/game/translation’);
return response.data.en;
} catch (error) {
console.error(error);
}
};

export const submitAnswer = (selected) => (dispatch, getState) => {
const word = selectWord(getState());
const isCorrect = selected === (word.gender === ‘m’ ? ‘UN/LE’ : ‘UNE/LA’);
if (isCorrect) {
dispatch(setCorrect());
} else {
dispatch(setWrong());
}
};

export default wordsSlice.reducer;

3. The GamePage.js file should be updated to fetch the nouns on load by dispatching the fetchNouns action creator. It should also use the fetchTranslation function to get the translation of the word and display it to the user. Finally, it should update the radio button labels to use the actual articles instead of placeholders:

import { useEffect, useState } from ‘react’;
import { useDispatch, useSelector } from ‘react-redux’;
import api from ‘…/…/api/api’;
import { fetchWord, selectIsCorrect, selectNouns, selectScore, submitAnswer } from ‘…/…/slices/wordsSlice’;

function GamePage() {
const dispatch = useDispatch();
const word = useSelector(selectWord);
const nouns = useSelector(selectNouns);
const isCorrect = useSelector(selectIsCorrect);
const score = useSelector(selectScore);
const [selected, setSelected] = useState(‘’);
const [translation, setTranslation] = useState(‘’);

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

useEffect(() => {
const fetchTranslationPromise = async () => {
const response = await dispatch(fetchTranslation());
setTranslation(response);
};
if (word) {
fetchTranslationPromise();
}
}, [dispatch, word]);

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

return (
<div className=“container mt-5”>
<div className=“row justify-content-center”>
<div className=“col-md-6”>
<h1>French Gender Learning Game</h1>
<h2>Score: {score}</h2>
<div className=“card”>
<div className=“card-body”>
<h3>
{word && word.word}
{translation.length > 0 && (
<small className=“text-muted ml-2”>
({translation})
</small>
)}
</h3>
<div className=“form-check”>
<input
type=“radio”
id=“un-le”
name=“article-group”
value=“UN/LE”
className=“form-check-input”
checked={selected === ‘UN/LE’}
onChange={(e) => setSelected(e.target.value)}
/>
<label htmlFor=“un-le” className=“form-check-label”>
{nouns.masculine.length > 0 &&
nouns.masculine[0].article}
</label>
</div>
<div className=“form-check”>
<input
type=“radio”
id=“une-la”
name=“article-group”
value=“UNE/LA”
className=“form-check-input”
checked={selected === ‘UNE/LA’}
onChange={(e) => setSelected(e.target.value)}
/>
<label htmlFor=“une-la” className=“form-check-label”>
{nouns.feminine.length > 0 &&
nouns.feminine[0].article}
</label>
</div>
<button
className=“btn btn-primary”
disabled={!selected}
onClick={handleAnswer}
>
Submit
</button>
<p className=“mt-2”>
{isCorrect !== null &&
(isCorrect ? ‘Correct!’ : ‘Wrong!’)}
</p>
</div>
</div>
</div>
</div>
</div>
);
}

export default GamePage;

These changes should properly make use of