From bc2871a7a02f39af17fd88e54d2d02d5aa5e67cc Mon Sep 17 00:00:00 2001 From: Temirkhan Amanzhanov Date: Sun, 8 Mar 2026 11:48:13 +0100 Subject: [PATCH 1/2] Update ESLint and add Prettier --- .husky/task-runner.json | 12 + AffirmationGenerator.Client/.eslintrc.cjs | 18 -- AffirmationGenerator.Client/.prettierrc.json | 9 + AffirmationGenerator.Client/eslint.config.cjs | 46 ++++ AffirmationGenerator.Client/package.json | 6 + AffirmationGenerator.Client/pnpm-lock.yaml | 51 ++++ AffirmationGenerator.Client/src/App.css | 145 ++++++------ AffirmationGenerator.Client/src/App.tsx | 217 +++++++++--------- .../components/Affirmation/ArrowDownIcon.tsx | 16 +- .../components/Affirmation/ErrorMessage.tsx | 5 +- .../Affirmation/LanguagesDropdown.tsx | 25 +- .../src/components/Affirmation/MainText.tsx | 11 +- .../Affirmation/RemainingItemsText.tsx | 17 +- .../src/components/Footer/Footer.tsx | 15 +- .../src/components/Footer/GithubLink.tsx | 7 +- .../src/components/Footer/LinkedInLink.tsx | 7 +- .../src/components/Footer/Separator.tsx | 6 +- .../src/components/MainCard.tsx | 7 +- AffirmationGenerator.Client/src/index.css | 72 +++--- AffirmationGenerator.Client/src/main.tsx | 14 +- .../models/affirmationLanguagesResponse.ts | 6 +- .../src/models/affirmationResponse.ts | 4 +- .../models/remainingAffirmationsResponse.ts | 6 +- AffirmationGenerator.Client/vite.config.js | 66 +++--- AffirmationGenerator.Client/vite.config.ts | 163 +++++++------ 25 files changed, 525 insertions(+), 426 deletions(-) delete mode 100644 AffirmationGenerator.Client/.eslintrc.cjs create mode 100644 AffirmationGenerator.Client/.prettierrc.json create mode 100644 AffirmationGenerator.Client/eslint.config.cjs diff --git a/.husky/task-runner.json b/.husky/task-runner.json index 31e59fe..1ed7e9d 100644 --- a/.husky/task-runner.json +++ b/.husky/task-runner.json @@ -6,6 +6,18 @@ "command": "dotnet", "args": [ "csharpier", "format", "${staged}" ], "include": [ "**/*.cs" ] + }, + { + "name": "Run eslint", + "cwd": "AffirmationGenerator.Client", + "command": "pnpm", + "args": [ "run", "lint" ], + }, + { + "name": "Run prettier", + "cwd": "AffirmationGenerator.Client", + "command": "pnpm", + "args": [ "run", "format" ], } ] } diff --git a/AffirmationGenerator.Client/.eslintrc.cjs b/AffirmationGenerator.Client/.eslintrc.cjs deleted file mode 100644 index d6c9537..0000000 --- a/AffirmationGenerator.Client/.eslintrc.cjs +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/AffirmationGenerator.Client/.prettierrc.json b/AffirmationGenerator.Client/.prettierrc.json new file mode 100644 index 0000000..e67f105 --- /dev/null +++ b/AffirmationGenerator.Client/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "semi": true, + "tabWidth": 2, + "printWidth": 100, + "singleQuote": false, + "trailingComma": "all", + "jsxSingleQuote": false, + "bracketSpacing": true +} diff --git a/AffirmationGenerator.Client/eslint.config.cjs b/AffirmationGenerator.Client/eslint.config.cjs new file mode 100644 index 0000000..f08170a --- /dev/null +++ b/AffirmationGenerator.Client/eslint.config.cjs @@ -0,0 +1,46 @@ +const { + defineConfig, + globalIgnores, +} = require("eslint/config"); + +const globals = require("globals"); + +const { + fixupConfigRules, +} = require("@eslint/compat"); + +const tsParser = require("@typescript-eslint/parser"); +const reactRefresh = require("eslint-plugin-react-refresh"); +const js = require("@eslint/js"); + +const { + FlatCompat, +} = require("@eslint/eslintrc"); + +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +module.exports = defineConfig([{ + languageOptions: { + globals: { + ...globals.browser, + }, + + parser: tsParser, + }, + + extends: fixupConfigRules(compat.extends( + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + )), + + plugins: { + "react-refresh": reactRefresh, + }, + + rules: {}, +}, globalIgnores(["**/dist", "**/eslint.config.cjs"])]); diff --git a/AffirmationGenerator.Client/package.json b/AffirmationGenerator.Client/package.json index 0fef06d..6d85f4d 100644 --- a/AffirmationGenerator.Client/package.json +++ b/AffirmationGenerator.Client/package.json @@ -8,6 +8,7 @@ "dev": "vite", "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "format": "prettier --write 'src/**/*.{ts,tsx,css}' --config ./.prettierrc.json", "preview": "vite preview" }, "dependencies": { @@ -20,6 +21,9 @@ "tailwindcss": "^4.2.1" }, "devDependencies": { + "@eslint/compat": "^2.0.3", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "^9.39.4", "@types/node": "^25.3.5", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", @@ -29,6 +33,8 @@ "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "prettier": "^3.8.1", "react-doctor": "^0.0.28", "typescript": "^5.9.3", "vite": "^7.3.1" diff --git a/AffirmationGenerator.Client/pnpm-lock.yaml b/AffirmationGenerator.Client/pnpm-lock.yaml index fc6bade..f64c106 100644 --- a/AffirmationGenerator.Client/pnpm-lock.yaml +++ b/AffirmationGenerator.Client/pnpm-lock.yaml @@ -30,6 +30,15 @@ importers: specifier: ^4.2.1 version: 4.2.1 devDependencies: + '@eslint/compat': + specifier: ^2.0.3 + version: 2.0.3(eslint@9.39.4(jiti@2.6.1)) + '@eslint/eslintrc': + specifier: ^3.3.5 + version: 3.3.5 + '@eslint/js': + specifier: ^9.39.4 + version: 9.39.4 '@types/node': specifier: ^25.3.5 version: 25.3.5 @@ -57,6 +66,12 @@ importers: eslint-plugin-react-refresh: specifier: ^0.5.2 version: 0.5.2(eslint@9.39.4(jiti@2.6.1)) + globals: + specifier: ^17.4.0 + version: 17.4.0 + prettier: + specifier: ^3.8.1 + version: 3.8.1 react-doctor: specifier: ^0.0.28 version: 0.0.28(@types/node@25.3.5)(eslint@9.39.4(jiti@2.6.1)) @@ -327,6 +342,15 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/compat@2.0.3': + resolution: {integrity: sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^8.40 || 9 || 10 + peerDependenciesMeta: + eslint: + optional: true + '@eslint/config-array@0.21.2': resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -339,6 +363,10 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@1.1.1': + resolution: {integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/eslintrc@3.3.5': resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1306,6 +1334,10 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@17.4.0: + resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} + engines: {node: '>=18'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -1626,6 +1658,11 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -2078,6 +2115,12 @@ snapshots: '@eslint-community/regexpp@4.12.2': {} + '@eslint/compat@2.0.3(eslint@9.39.4(jiti@2.6.1))': + dependencies: + '@eslint/core': 1.1.1 + optionalDependencies: + eslint: 9.39.4(jiti@2.6.1) + '@eslint/config-array@0.21.2': dependencies: '@eslint/object-schema': 2.1.7 @@ -2094,6 +2137,10 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@1.1.1': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.5': dependencies: ajv: 6.14.0 @@ -2941,6 +2988,8 @@ snapshots: globals@14.0.0: {} + globals@17.4.0: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -3237,6 +3286,8 @@ snapshots: prelude-ls@1.2.1: {} + prettier@3.8.1: {} + prompts@2.4.2: dependencies: kleur: 3.0.3 diff --git a/AffirmationGenerator.Client/src/App.css b/AffirmationGenerator.Client/src/App.css index 9982421..e07ddd2 100644 --- a/AffirmationGenerator.Client/src/App.css +++ b/AffirmationGenerator.Client/src/App.css @@ -1,72 +1,73 @@ -@import "tailwindcss"; - -@plugin "daisyui"; - -@keyframes gradient { - 0% { - background-position: 0 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0 50%; - } -} - -.animated-bg { - background: linear-gradient(-45deg, #fef08a, #86efac, #93c5fd); - background-size: 400% 400%; - animation: gradient 15s ease infinite; -} - -.typing-cursor::after { - content: '|'; - animation: blink 1s step-end infinite; -} - -@keyframes blink { - from, to { - opacity: 1; - } - 50% { - opacity: 0; - } -} - -@keyframes slide-down { - 0% { - opacity: 0; - transform: translateY(-100%); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes pop-shake { - 0% { - transform: scale(1) rotate(0deg); - } - 25% { - transform: scale(1.05) rotate(3deg); - } - 50% { - transform: scale(1.05) rotate(-3deg); - } - 75% { - transform: scale(1.02) rotate(1.5deg); - } - 100% { - transform: scale(1) rotate(0deg); - } -} - -.animate-slide-down { - animation: slide-down 0.5s ease-out forwards; -} - -.animate-pop-shake { - animation: pop-shake 0.5s ease-in-out; -} +@import "tailwindcss"; + +@plugin "daisyui"; + +@keyframes gradient { + 0% { + background-position: 0 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0 50%; + } +} + +.animated-bg { + background: linear-gradient(-45deg, #fef08a, #86efac, #93c5fd); + background-size: 400% 400%; + animation: gradient 15s ease infinite; +} + +.typing-cursor::after { + content: "|"; + animation: blink 1s step-end infinite; +} + +@keyframes blink { + from, + to { + opacity: 1; + } + 50% { + opacity: 0; + } +} + +@keyframes slide-down { + 0% { + opacity: 0; + transform: translateY(-100%); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes pop-shake { + 0% { + transform: scale(1) rotate(0deg); + } + 25% { + transform: scale(1.05) rotate(3deg); + } + 50% { + transform: scale(1.05) rotate(-3deg); + } + 75% { + transform: scale(1.02) rotate(1.5deg); + } + 100% { + transform: scale(1) rotate(0deg); + } +} + +.animate-slide-down { + animation: slide-down 0.5s ease-out forwards; +} + +.animate-pop-shake { + animation: pop-shake 0.5s ease-in-out; +} diff --git a/AffirmationGenerator.Client/src/App.tsx b/AffirmationGenerator.Client/src/App.tsx index 711d614..be27199 100644 --- a/AffirmationGenerator.Client/src/App.tsx +++ b/AffirmationGenerator.Client/src/App.tsx @@ -1,111 +1,106 @@ -import './App.css'; -import LanguagesDropdown from "./components/Affirmation/LanguagesDropdown.tsx"; -import ErrorMessage from "./components/Affirmation/ErrorMessage.tsx"; -import MainText from "./components/Affirmation/MainText.tsx"; -import RemainingItemsText from "./components/Affirmation/RemainingItemsText.tsx"; -import MainCard from "./components/MainCard.tsx"; -import Footer from "./components/Footer/Footer.tsx"; -import {useEffect, useState} from 'react'; -import axios, {HttpStatusCode} from 'axios'; -import {useQuery, useQueryClient} from "@tanstack/react-query"; -import AffirmationResponse from './models/affirmationResponse.ts'; -import RemainingAffirmationsResponse from "./models/remainingAffirmationsResponse.ts"; -import AffirmationLanguagesResponse from "./models/affirmationLanguagesResponse.ts"; - -function App() { - const [remainingAffirmations, setRemainingAffirmations] = useState(0); - const [affirmationText, setAffirmationText] = useState('Select a language for the affirmation'); - const [displayedText, setDisplayedText] = useState(''); - const [errorMessage, setErrorMessage] = useState(''); - const [selectedLanguageCode, setSelectedLanguageCode] = useState(''); - const [isShaking, setIsShaking] = useState(false); - const [isFetching, setIsFetching] = useState(false); - - const queryClient = useQueryClient(); - - const {data: languages = []} = useQuery({ - queryKey: ['languages'], - queryFn: () => axios - .get('/affirmations/languages') - .then(response => response.data.languages) - .then(languages => Object.entries(languages).map(([code, label]) => ({code, label}))) - }); - - const {data: remainingCount} = useQuery({ - queryKey: ['remainingAffirmations'], - queryFn: () => axios - .get('/affirmations/remaining') - .then(response => response.data.remainingCount) - }); - - useEffect(() => { - if (remainingCount === undefined) return; - setRemainingAffirmations(remainingCount); - if (remainingCount !== 0) return; - setMaxAmountOfAffirmationsErrorMessage(); - }, [remainingCount]); - - useEffect(() => { - if (displayedText.length < affirmationText.length) { - const text = affirmationText.slice(0, displayedText.length + 1); - const timeout = setTimeout(() => setDisplayedText(text), 50); - return () => clearTimeout(timeout); - } - }, [displayedText, affirmationText]); - - const isTyping = displayedText.length < affirmationText.length; - const isInteractionDisabled = isFetching || isTyping || remainingAffirmations === 0; - - function setMaxAmountOfAffirmationsErrorMessage() { - setErrorMessage('Achieved maximum amount of affirmations per day. Come back tomorrow for more affirmations! 😁'); - } - - function getAffirmation(targetLanguage: string) { - setErrorMessage(''); - setIsFetching(true); - axios - .get('/affirmations', {params: {targetLanguage: targetLanguage}}) - .then(response => response.data) - .then(data => { - setAffirmationText(data.text); - setDisplayedText(''); - setRemainingAffirmations(data.remainingCount); - queryClient.setQueryData(['remainingAffirmations'], data.remainingCount); - if (data.remainingCount === 0) - setMaxAmountOfAffirmationsErrorMessage(); - setIsShaking(true); - setTimeout(() => setIsShaking(false), 500); - }) - .catch(error => { - if (axios.isAxiosError(error) && error.response?.status === HttpStatusCode.TooManyRequests) - setMaxAmountOfAffirmationsErrorMessage(); - else - setErrorMessage('Unable to generate affirmation right now ☹️'); - }) - .finally(() => setIsFetching(false)); - } - - function handleLanguageChange(targetLanguage: string) { - if (isInteractionDisabled) - return; - setSelectedLanguageCode(targetLanguage); - getAffirmation(targetLanguage); - } - - return ( -
- }> - - - - -
-
- ); -} - -export default App; \ No newline at end of file +import "./App.css"; +import LanguagesDropdown from "./components/Affirmation/LanguagesDropdown.tsx"; +import ErrorMessage from "./components/Affirmation/ErrorMessage.tsx"; +import MainText from "./components/Affirmation/MainText.tsx"; +import RemainingItemsText from "./components/Affirmation/RemainingItemsText.tsx"; +import MainCard from "./components/MainCard.tsx"; +import Footer from "./components/Footer/Footer.tsx"; +import { useEffect, useState } from "react"; +import axios, { HttpStatusCode } from "axios"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import AffirmationResponse from "./models/affirmationResponse.ts"; +import RemainingAffirmationsResponse from "./models/remainingAffirmationsResponse.ts"; +import AffirmationLanguagesResponse from "./models/affirmationLanguagesResponse.ts"; + +function App() { + const [affirmationText, setAffirmationText] = useState("Select a language for the affirmation"); + const [displayedText, setDisplayedText] = useState(""); + const [errorMessage, setErrorMessage] = useState(""); + const [selectedLanguageCode, setSelectedLanguageCode] = useState(""); + const [isShaking, setIsShaking] = useState(false); + const [isFetching, setIsFetching] = useState(false); + + const queryClient = useQueryClient(); + + const { data: languages = [] } = useQuery({ + queryKey: ["languages"], + queryFn: () => + axios + .get("/affirmations/languages") + .then((response) => response.data.languages) + .then((languages) => Object.entries(languages).map(([code, label]) => ({ code, label }))), + }); + + const { data: remainingCount } = useQuery({ + queryKey: ["remainingAffirmations"], + queryFn: () => + axios + .get("/affirmations/remaining") + .then((response) => response.data.remainingCount), + }); + + const remainingAffirmations = remainingCount ?? 0; + + const maxAffirmationsMessage = + "Achieved maximum amount of affirmations per day. Come back tomorrow for more affirmations! 😁"; + + const displayedErrorMessage = + remainingAffirmations === 0 && !errorMessage ? maxAffirmationsMessage : errorMessage; + + useEffect(() => { + if (displayedText.length < affirmationText.length) { + const text = affirmationText.slice(0, displayedText.length + 1); + const timeout = setTimeout(() => setDisplayedText(text), 50); + return () => clearTimeout(timeout); + } + }, [displayedText, affirmationText]); + + const isTyping = displayedText.length < affirmationText.length; + const isInteractionDisabled = isFetching || isTyping || remainingAffirmations === 0; + + function getAffirmation(targetLanguage: string) { + setErrorMessage(""); + setIsFetching(true); + axios + .get("/affirmations", { params: { targetLanguage: targetLanguage } }) + .then((response) => response.data) + .then((data) => { + setAffirmationText(data.text); + setDisplayedText(""); + queryClient.setQueryData(["remainingAffirmations"], data.remainingCount); + if (data.remainingCount === 0) setErrorMessage(maxAffirmationsMessage); + setIsShaking(true); + setTimeout(() => setIsShaking(false), 500); + }) + .catch((error) => { + if (axios.isAxiosError(error) && error.response?.status === HttpStatusCode.TooManyRequests) + setErrorMessage(maxAffirmationsMessage); + else setErrorMessage("Unable to generate affirmation right now ☹️"); + }) + .finally(() => setIsFetching(false)); + } + + function handleLanguageChange(targetLanguage: string) { + if (isInteractionDisabled) return; + setSelectedLanguageCode(targetLanguage); + getAffirmation(targetLanguage); + } + + return ( +
+ }> + + + + +
+
+ ); +} + +export default App; diff --git a/AffirmationGenerator.Client/src/components/Affirmation/ArrowDownIcon.tsx b/AffirmationGenerator.Client/src/components/Affirmation/ArrowDownIcon.tsx index 165f158..ca989fe 100644 --- a/AffirmationGenerator.Client/src/components/Affirmation/ArrowDownIcon.tsx +++ b/AffirmationGenerator.Client/src/components/Affirmation/ArrowDownIcon.tsx @@ -1,11 +1,17 @@ function ArrowDownIcon() { return ( - - + + ); } -export default ArrowDownIcon; \ No newline at end of file +export default ArrowDownIcon; diff --git a/AffirmationGenerator.Client/src/components/Affirmation/ErrorMessage.tsx b/AffirmationGenerator.Client/src/components/Affirmation/ErrorMessage.tsx index 5e39429..c2a8f2a 100644 --- a/AffirmationGenerator.Client/src/components/Affirmation/ErrorMessage.tsx +++ b/AffirmationGenerator.Client/src/components/Affirmation/ErrorMessage.tsx @@ -2,9 +2,8 @@ type ErrorMessageProps = { message: string; }; -function ErrorMessage({message}: ErrorMessageProps) { - if (!message) - return null; +function ErrorMessage({ message }: ErrorMessageProps) { + if (!message) return null; return (
diff --git a/AffirmationGenerator.Client/src/components/Affirmation/LanguagesDropdown.tsx b/AffirmationGenerator.Client/src/components/Affirmation/LanguagesDropdown.tsx index 38effed..24dc216 100644 --- a/AffirmationGenerator.Client/src/components/Affirmation/LanguagesDropdown.tsx +++ b/AffirmationGenerator.Client/src/components/Affirmation/LanguagesDropdown.tsx @@ -1,4 +1,4 @@ -import {useRef} from 'react'; +import { useRef } from "react"; import ArrowDownIcon from "./ArrowDownIcon.tsx"; interface LanguageOption { @@ -13,38 +13,37 @@ type LanguageDropdownProps = { languages: LanguageOption[]; }; -function LanguagesDropdown({value, onChange, disabled, languages}: LanguageDropdownProps) { +function LanguagesDropdown({ value, onChange, disabled, languages }: LanguageDropdownProps) { const detailsRef = useRef(null); function handleSelect(code: string) { - if (disabled) - return; - + if (disabled) return; + onChange(code); - if (detailsRef.current) - detailsRef.current.removeAttribute('open'); + if (detailsRef.current) detailsRef.current.removeAttribute("open"); } - const selectedLabel = languages.find(language => language.code === value)?.label || "Choose language"; + const selectedLabel = + languages.find((language) => language.code === value)?.label || "Choose language"; return (
{selectedLabel} - +
    - {languages.map(affirmationLanguage => ( + {languages.map((affirmationLanguage) => (
  • @@ -56,4 +55,4 @@ function LanguagesDropdown({value, onChange, disabled, languages}: LanguageDropd ); } -export default LanguagesDropdown; \ No newline at end of file +export default LanguagesDropdown; diff --git a/AffirmationGenerator.Client/src/components/Affirmation/MainText.tsx b/AffirmationGenerator.Client/src/components/Affirmation/MainText.tsx index d018d96..f94f093 100644 --- a/AffirmationGenerator.Client/src/components/Affirmation/MainText.tsx +++ b/AffirmationGenerator.Client/src/components/Affirmation/MainText.tsx @@ -3,7 +3,7 @@ type DisplayedTextProps = { isLoading?: boolean; }; -function MainText({text, isLoading}: DisplayedTextProps) { +function MainText({ text, isLoading }: DisplayedTextProps) { function getTextSizeClass() { let textSizeClass = "text-4xl md:text-8xl"; @@ -20,12 +20,15 @@ function MainText({text, isLoading}: DisplayedTextProps) { return textSizeClass; } - return isLoading ? () : ( -

    + return isLoading ? ( + + ) : ( +

    {text}

    ); } - export default MainText; diff --git a/AffirmationGenerator.Client/src/components/Affirmation/RemainingItemsText.tsx b/AffirmationGenerator.Client/src/components/Affirmation/RemainingItemsText.tsx index fe6e1a3..8e90fa6 100644 --- a/AffirmationGenerator.Client/src/components/Affirmation/RemainingItemsText.tsx +++ b/AffirmationGenerator.Client/src/components/Affirmation/RemainingItemsText.tsx @@ -2,19 +2,20 @@ type RemainingAffirmationsTextProps = { count: number; }; -function RemainingItemsText({count}: RemainingAffirmationsTextProps) { +function RemainingItemsText({ count }: RemainingAffirmationsTextProps) { function getCountColor(count: number) { - if (count >= 4) - return 'text-green-600'; - if (count >= 2) - return 'text-yellow-500'; - - return 'text-red-600'; + if (count >= 4) return "text-green-600"; + if (count >= 2) return "text-yellow-500"; + + return "text-red-600"; } return (
    - Remaining affirmations: {count} + Remaining affirmations:{" "} + + {count} +
    ); } diff --git a/AffirmationGenerator.Client/src/components/Footer/Footer.tsx b/AffirmationGenerator.Client/src/components/Footer/Footer.tsx index 7930666..7424e8e 100644 --- a/AffirmationGenerator.Client/src/components/Footer/Footer.tsx +++ b/AffirmationGenerator.Client/src/components/Footer/Footer.tsx @@ -4,20 +4,19 @@ import Separator from "./Separator.tsx"; function Footer() { return ( -