diff --git a/frontend/pronunciationAppFront/package-lock.json b/frontend/pronunciationAppFront/package-lock.json index 3267be42b..8a874dff3 100644 --- a/frontend/pronunciationAppFront/package-lock.json +++ b/frontend/pronunciationAppFront/package-lock.json @@ -11,7 +11,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@fontsource/roboto": "^5.1.1", - "@mui/icons-material": "^6.4.0", + "@mui/icons-material": "^6.4.3", "@mui/material": "^6.4.0", "axios": "^1.7.9", "react": "^18.3.1", @@ -935,18 +935,20 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.0.tgz", - "integrity": "sha512-6u74wi+9zeNlukrCtYYET8Ed/n9AS27DiaXCZKAD3TRGFaqiyYSsQgN2disW83pI/cM1Q2lJY1JX4YfwvNtlNw==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.3.tgz", + "integrity": "sha512-hlyOzo2ObarllAOeT1ZSAusADE5NZNencUeIvXrdQ1Na+FL1lcznhbxfV5He1KqGiuR8Az3xtCUcYKwMVGFdzg==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.0.tgz", - "integrity": "sha512-zF0Vqt8a+Zp2Oz8P+WvJflba6lLe3PhxIz1NNqn+n4A+wKLPbkeqY8ShmKjPyiCTg0RMbPrp993oUDl9xGsDlQ==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.3.tgz", + "integrity": "sha512-3IY9LpjkwIJVgL/SkZQKKCUcumdHdQEsJaIavvsQze2QEztBt0HJ17naToN0DBBdhKdtwX5xXrfD6ZFUeWWk8g==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" }, @@ -958,7 +960,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.4.0", + "@mui/material": "^6.4.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -969,15 +971,16 @@ } }, "node_modules/@mui/material": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.0.tgz", - "integrity": "sha512-hNIgwdM9U3DNmowZ8mU59oFmWoDKjc92FqQnQva3Pxh6xRKWtD2Ej7POUHMX8Dwr1OpcSUlT2+tEMeLb7WYsIg==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.3.tgz", + "integrity": "sha512-ubtQjplbWneIEU8Y+4b2VA0CDBlyH5I3AmVFGmsLyDe/bf0ubxav5t11c8Afem6rkSFWPlZA2DilxmGka1xiKQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.0", - "@mui/system": "^6.4.0", + "@mui/core-downloads-tracker": "^6.4.3", + "@mui/system": "^6.4.3", "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.3", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", @@ -996,7 +999,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.0", + "@mui/material-pigment-css": "^6.4.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1022,12 +1025,13 @@ "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" }, "node_modules/@mui/private-theming": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.0.tgz", - "integrity": "sha512-rNHci8MP6NOdEWAfZ/RBMO5Rhtp1T6fUDMSmingg9F1T6wiUeodIQ+NuTHh2/pMoUSeP9GdHdgMhMmfsXxOMuw==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.3.tgz", + "integrity": "sha512-7x9HaNwDCeoERc4BoEWLieuzKzXu5ZrhRnEM6AUcRXUScQLvF1NFkTlP59+IJfTbEMgcGg1wWHApyoqcksrBpQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.3", "prop-types": "^15.8.1" }, "engines": { @@ -1048,9 +1052,10 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz", - "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.3.tgz", + "integrity": "sha512-OC402VfK+ra2+f12Gef8maY7Y9n7B6CZcoQ9u7mIkh/7PKwW/xH81xwX+yW+Ak1zBT3HYcVjh2X82k5cKMFGoQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", "@emotion/cache": "^11.13.5", @@ -1081,15 +1086,16 @@ } }, "node_modules/@mui/system": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.0.tgz", - "integrity": "sha512-wTDyfRlaZCo2sW2IuOsrjeE5dl0Usrs6J7DxE3GwNCVFqS5wMplM2YeNiV3DO7s53RfCqbho+gJY6xaB9KThUA==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.3.tgz", + "integrity": "sha512-Q0iDwnH3+xoxQ0pqVbt8hFdzhq1g2XzzR4Y5pVcICTNtoCLJmpJS3vI4y/OIM1FHFmpfmiEC2IRIq7YcZ8nsmg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.4.0", - "@mui/styled-engine": "^6.4.0", + "@mui/private-theming": "^6.4.3", + "@mui/styled-engine": "^6.4.3", "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.0", + "@mui/utils": "^6.4.3", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1123,6 +1129,7 @@ "version": "7.2.21", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", + "license": "MIT", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1133,9 +1140,10 @@ } }, "node_modules/@mui/utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.0.tgz", - "integrity": "sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.3.tgz", + "integrity": "sha512-jxHRHh3BqVXE9ABxDm+Tc3wlBooYz/4XPa0+4AI+iF38rV1/+btJmSUgG4shDtSWVs/I97aDn5jBCt6SF2Uq2A==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", "@mui/types": "^7.2.21", @@ -1164,7 +1172,8 @@ "node_modules/@mui/utils/node_modules/react-is": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", - "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "license": "MIT" }, "node_modules/@popperjs/core": { "version": "2.11.8", @@ -2041,6 +2050,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } diff --git a/frontend/pronunciationAppFront/package.json b/frontend/pronunciationAppFront/package.json index 4341f7257..e69c78b7f 100644 --- a/frontend/pronunciationAppFront/package.json +++ b/frontend/pronunciationAppFront/package.json @@ -13,7 +13,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@fontsource/roboto": "^5.1.1", - "@mui/icons-material": "^6.4.0", + "@mui/icons-material": "^6.4.3", "@mui/material": "^6.4.0", "axios": "^1.7.9", "react": "^18.3.1", diff --git a/frontend/pronunciationAppFront/src/App.jsx b/frontend/pronunciationAppFront/src/App.jsx index 11318844a..935a8f7cb 100644 --- a/frontend/pronunciationAppFront/src/App.jsx +++ b/frontend/pronunciationAppFront/src/App.jsx @@ -1,15 +1,13 @@ import { BrowserRouter, Route, Routes } from "react-router-dom"; -import About from './About'; -import './App.css'; -import Home from './Home.jsx'; -import Layout from './Layout'; -import NoPage from './NoPage'; -import Practice from './Practice'; - +import About from "./pages/About.jsx"; +import "./styles/App.css"; +import Home from "./pages/Home.jsx"; +import Layout from "./layout/Layout.jsx"; +import NoPage from "./pages/NoPage.jsx"; +import Practice from "./pages/Practice.jsx"; +import MyProfile from "./pages/MyProfile.jsx"; export default function App() { - - return ( @@ -17,10 +15,10 @@ export default function App() { } /> } /> } /> + } /> } /> - ); -}; - + ); +} diff --git a/frontend/pronunciationAppFront/src/Cards.jsx b/frontend/pronunciationAppFront/src/Cards.jsx deleted file mode 100644 index d7bf080f7..000000000 --- a/frontend/pronunciationAppFront/src/Cards.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useState, useEffect } from "react"; -import { - Box, - Card, - CardContent, - Typography, - Container, - Grid, -} from "@mui/material"; -import { fetchWords } from "./data-api"; - -export default function WordList() { - const [words, setWords] = useState([]); - - useEffect(() => { - const getWords = async () => { - try { - const data = await fetchWords(); - setWords(data); - } catch (error) { - console.error("Failed to fetch words:", error); - } - }; - - getWords(); - }, []); - - return ( - - - - Word List - - - {words.map((word) => ( - - - - - {word.word} - - - Pronunciation: {word.pronunciation} - - - - - ))} - - - - ); -} \ No newline at end of file diff --git a/frontend/pronunciationAppFront/src/components/Cards.jsx b/frontend/pronunciationAppFront/src/components/Cards.jsx new file mode 100644 index 000000000..038d319aa --- /dev/null +++ b/frontend/pronunciationAppFront/src/components/Cards.jsx @@ -0,0 +1,173 @@ +import { useState, useEffect } from "react"; +import { + Box, + Card, + CardActions, + CardContent, + Collapse, + Typography, + Container, + Grid, + FormControl, + InputLabel, + Select, + MenuItem, + FormHelperText, + IconButton, +} from "@mui/material"; +import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; +import { fetchWords } from "../middleware/words-data-api"; + +export default function WordList() { + const [words, setWords] = useState([]); + const [expandedStates, setExpandedStates] = useState([]); + const [currentLevel, setCurrentLevel] = useState(""); + + useEffect(() => { + const getWords = async () => { + try { + const data = await fetchWords(); + setWords(data); + setExpandedStates(Array(data.length).fill(false)); + } catch (error) { + console.error("Failed to fetch words:", error); + } + }; + + getWords(); + }, []); + + const handleExpandClick = (index) => { + setExpandedStates((prevStates) => { + const newStates = [...prevStates]; + newStates[index] = !newStates[index]; + return newStates; + }); + }; + + const handleChangeCurrentLevel = (e) => { + setCurrentLevel(e.target.value); + setExpandedStates(Array(words.length).fill(false)); + }; + + const getColorLevel = (level) => { + switch (+level) { + case 1: + return "#00FF00"; + case 2: + return "#FFA500"; + case 3: + return "#FF0000"; + default: + return "#222222"; + } + }; + + // TODO: Improve InputLabel display + return ( + + + + Word List + + + Level + + + Select your level! + + + + {words + .filter((word) => { + return currentLevel === "All" || word.level === +currentLevel; + }) + .map((word, index) => ( + + + + + {word.wordName} + + + Pronunciation: {word.phoneticSpelling} + + + {word.synonyms.length != 0 && ( + <> + + + + Synonyms + + handleExpandClick(index)}> + + + + + + {word.synonyms.map((synonym) => ( + + {synonym} + + ))} + + + )} + + + ))} + + + + ); +} diff --git a/frontend/pronunciationAppFront/src/Header.jsx b/frontend/pronunciationAppFront/src/components/Header.jsx similarity index 100% rename from frontend/pronunciationAppFront/src/Header.jsx rename to frontend/pronunciationAppFront/src/components/Header.jsx diff --git a/frontend/pronunciationAppFront/src/components/UserProfile.jsx b/frontend/pronunciationAppFront/src/components/UserProfile.jsx new file mode 100644 index 000000000..161fd27df --- /dev/null +++ b/frontend/pronunciationAppFront/src/components/UserProfile.jsx @@ -0,0 +1,59 @@ +import { useState, useEffect } from "react"; +import { fetchUserById } from "../middleware/users-data-api"; +import { Avatar, Box, Card, Typography } from "@mui/material"; + +export default function UserProfile({ userId }) { + const [user, setUser] = useState(null); + + useEffect(() => { + const getUser = async () => { + try { + const data = await fetchUserById(userId); + setUser(data); + } catch (error) { + console.error("Failed to fetch user:", error); + } + }; + + getUser(); + }, []); + return ( + <> + {user ? ( + + + + + + + {user.name} + + + Email: {user.email} + + + + ) : ( + Loading... + )} + + ); +} diff --git a/frontend/pronunciationAppFront/src/Layout.jsx b/frontend/pronunciationAppFront/src/layout/Layout.jsx similarity index 93% rename from frontend/pronunciationAppFront/src/Layout.jsx rename to frontend/pronunciationAppFront/src/layout/Layout.jsx index cccda1c87..c0d2ff415 100644 --- a/frontend/pronunciationAppFront/src/Layout.jsx +++ b/frontend/pronunciationAppFront/src/layout/Layout.jsx @@ -37,6 +37,9 @@ export default function Layout() { + diff --git a/frontend/pronunciationAppFront/src/main.jsx b/frontend/pronunciationAppFront/src/main.jsx index c4851b232..4064ba739 100644 --- a/frontend/pronunciationAppFront/src/main.jsx +++ b/frontend/pronunciationAppFront/src/main.jsx @@ -1,5 +1,5 @@ import { createRoot } from 'react-dom/client' -import './index.css' +import './styles/index.css' import App from './App.jsx' import "@fontsource/roboto/300.css"; import "@fontsource/roboto/400.css"; diff --git a/frontend/pronunciationAppFront/src/middleware/users-data-api.js b/frontend/pronunciationAppFront/src/middleware/users-data-api.js new file mode 100644 index 000000000..95d2b8759 --- /dev/null +++ b/frontend/pronunciationAppFront/src/middleware/users-data-api.js @@ -0,0 +1,60 @@ +import axios from "axios"; + +const BASE_URL = "https://88e4a00b-25b1-441e-873c-274719894460.mock.pstmn.io"; + +// READ: Fetch all users +export const fetchUsers = async () => { + try { + const response = await axios.get(`${BASE_URL}/users`); + return response.data.users; + } catch (error) { + console.error("Error fetching users:", error); + throw error; + } +}; + +// READ: Fetch a user by ID +export const fetchUserById = async (id) => { + try { + const response = await axios.get(`${BASE_URL}/users/${id}`); + return response.data; + } catch (error) { + console.error(`Error fetching the user with ID ${id}:`, error); + throw error; + } +}; + +// CREATE: Add a new user +export const createUser = async (newUser) => { + try { + const response = await axios.post(`${BASE_URL}/users`, { user: newUser }); + return response.data.user; + } catch (error) { + console.error("Error creating a new user:", error); + throw error; + } +}; + +// UPDATE: Update an existing user by ID +export const updateUser = async (id, updatedUser) => { + try { + const response = await axios.put(`${BASE_URL}/users/${id}`, { + user: updatedUser, + }); + return response.data.user; + } catch (error) { + console.error(`Error updating the user with ID ${id}:`, error); + throw error; + } +}; + +// DELETE: Delete a user by ID +export const deleteUser = async (id) => { + try { + const response = await axios.delete(`${BASE_URL}/users/${id}`); + return response.data; + } catch (error) { + console.error(`Error deleting the user with ID ${id}:`, error); + throw error; + } +}; diff --git a/frontend/pronunciationAppFront/src/data-api.js b/frontend/pronunciationAppFront/src/middleware/words-data-api.js similarity index 73% rename from frontend/pronunciationAppFront/src/data-api.js rename to frontend/pronunciationAppFront/src/middleware/words-data-api.js index 0bcdfadc7..0bb00d50c 100644 --- a/frontend/pronunciationAppFront/src/data-api.js +++ b/frontend/pronunciationAppFront/src/middleware/words-data-api.js @@ -1,7 +1,6 @@ -// api.js import axios from "axios"; -const BASE_URL = "https://1e84671c-879b-423f-b798-dfa33a0482f6.mock.pstmn.io"; +const BASE_URL = "https://88e4a00b-25b1-441e-873c-274719894460.mock.pstmn.io"; // READ: Fetch all words export const fetchWords = async () => { @@ -14,11 +13,22 @@ export const fetchWords = async () => { } }; +// READ: Fetch a word by ID +export const fetchWordById = async (id) => { + try { + const response = await axios.get(`${BASE_URL}/words/${id}`); + return response.data; + } catch (error) { + console.error(`Error fetching the word with ID ${id}:`, error); + throw error; + } +}; + // CREATE: Add a new word export const createWord = async (newWord) => { try { const response = await axios.post(`${BASE_URL}/words`, { word: newWord }); - return response.data; + return response.data.word; } catch (error) { console.error("Error creating a new word:", error); throw error; @@ -28,8 +38,10 @@ export const createWord = async (newWord) => { // UPDATE: Update an existing word by ID export const updateWord = async (id, updatedWord) => { try { - const response = await axios.put(`${BASE_URL}/words/${id}`, { word: updatedWord }); - return response.data; + const response = await axios.put(`${BASE_URL}/words/${id}`, { + word: updatedWord, + }); + return response.data.word; } catch (error) { console.error(`Error updating the word with ID ${id}:`, error); throw error; @@ -45,4 +57,4 @@ export const deleteWord = async (id) => { console.error(`Error deleting the word with ID ${id}:`, error); throw error; } -}; \ No newline at end of file +}; diff --git a/frontend/pronunciationAppFront/src/About.jsx b/frontend/pronunciationAppFront/src/pages/About.jsx similarity index 100% rename from frontend/pronunciationAppFront/src/About.jsx rename to frontend/pronunciationAppFront/src/pages/About.jsx diff --git a/frontend/pronunciationAppFront/src/Home.jsx b/frontend/pronunciationAppFront/src/pages/Home.jsx similarity index 77% rename from frontend/pronunciationAppFront/src/Home.jsx rename to frontend/pronunciationAppFront/src/pages/Home.jsx index fb85b4f26..93a23416e 100644 --- a/frontend/pronunciationAppFront/src/Home.jsx +++ b/frontend/pronunciationAppFront/src/pages/Home.jsx @@ -1,4 +1,4 @@ -import Header from './Header.jsx' +import Header from '../components/Header.jsx' export default function Home() { diff --git a/frontend/pronunciationAppFront/src/pages/MyProfile.jsx b/frontend/pronunciationAppFront/src/pages/MyProfile.jsx new file mode 100644 index 000000000..6888a7419 --- /dev/null +++ b/frontend/pronunciationAppFront/src/pages/MyProfile.jsx @@ -0,0 +1,5 @@ +import UserProfile from "../components/UserProfile"; + +export default function MyProfile() { + return ; +} diff --git a/frontend/pronunciationAppFront/src/NoPage.jsx b/frontend/pronunciationAppFront/src/pages/NoPage.jsx similarity index 100% rename from frontend/pronunciationAppFront/src/NoPage.jsx rename to frontend/pronunciationAppFront/src/pages/NoPage.jsx diff --git a/frontend/pronunciationAppFront/src/Practice.jsx b/frontend/pronunciationAppFront/src/pages/Practice.jsx similarity index 74% rename from frontend/pronunciationAppFront/src/Practice.jsx rename to frontend/pronunciationAppFront/src/pages/Practice.jsx index c883d79e9..0d0cb4446 100644 --- a/frontend/pronunciationAppFront/src/Practice.jsx +++ b/frontend/pronunciationAppFront/src/pages/Practice.jsx @@ -1,4 +1,4 @@ -import Cards from './Cards' +import Cards from '../components/Cards' export default function Practice(){ diff --git a/frontend/pronunciationAppFront/src/App.css b/frontend/pronunciationAppFront/src/styles/App.css similarity index 100% rename from frontend/pronunciationAppFront/src/App.css rename to frontend/pronunciationAppFront/src/styles/App.css diff --git a/frontend/pronunciationAppFront/src/index.css b/frontend/pronunciationAppFront/src/styles/index.css similarity index 100% rename from frontend/pronunciationAppFront/src/index.css rename to frontend/pronunciationAppFront/src/styles/index.css