From bed199fe380fd749eeca6b8a869da9d0c61c7f6d Mon Sep 17 00:00:00 2001 From: andrxpie Date: Tue, 19 May 2026 20:52:19 +0300 Subject: [PATCH 01/66] Update npm dependencies to latest stable versions & fix related issues --- package.json | 6 +-- src/assets/sass/_utils.functions.scss | 15 +++++--- src/assets/sass/mixins/_utils.mixins.scss | 18 ++++----- src/assets/sass/mixins/_vendor.mixins.scss | 10 +++-- .../TimelineSwiper.component.tsx | 5 ++- src/index.d.ts | 4 ++ tsconfig.json | 37 ++++++++++--------- 7 files changed, 53 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 8c9b403..dec8434 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "styled-components": "^5.3.6", "svg-react-loader": "^0.4.6", "svg-url-loader": "^8.0.0", - "swiper": "^8.4.6", + "swiper": "^12.1.4", "tinymce": "^6.4.0", "typescript": "^4.9.4", "yet-another-react-lightbox": "^2.2.7" @@ -123,8 +123,8 @@ "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", "lint-staged": "^13.1.0", - "sass": "^1.57.1", - "sass-loader": "^13.2.0", + "sass": "^1.99.0", + "sass-loader": "^16.0.8", "ts-loader": "^9.4.2", "url-loader": "^4.1.1", "webpack": "^5.75.0", diff --git a/src/assets/sass/_utils.functions.scss b/src/assets/sass/_utils.functions.scss index cde99e0..db115d5 100644 --- a/src/assets/sass/_utils.functions.scss +++ b/src/assets/sass/_utils.functions.scss @@ -1,4 +1,7 @@ @use "sass:math"; +@use "sass:string"; +@use "sass:list"; +@use "sass:map"; $body-font-size: 16px; @@ -13,27 +16,27 @@ $body-font-size: 16px; } @function str-replace($string, $search, $replace: '') { - $index: str-index($string, $search); + $index: string.index($string, $search); @if $index { - @return str-slice($string, 1, $index - 1) + $replace + - str-replace(str-slice($string, $index + str-length($search)), $search, $replace); + @return string.slice($string, 1, $index - 1) + $replace + + str-replace(string.slice($string, $index + string.length($search)), $search, $replace); } @return $string; } @function str-replace-all($strings, $search, $replace: '') { @each $string in $strings { - $strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace)); + $strings: list.set-nth($strings, list.index($strings, $string), str-replace($string, $search, $replace)); } @return $strings; } @function map-to-rem($map) { - $new-map: ( ); + $new-map: (); @each $key, $value in $map { - $new-map: map-merge($new-map, ($key: str-replace(#{pxToRem($value)}, 'px'))); + $new-map: map.merge($new-map, ($key: str-replace(#{pxToRem($value)}, 'px'))); } @return $new-map; } \ No newline at end of file diff --git a/src/assets/sass/mixins/_utils.mixins.scss b/src/assets/sass/mixins/_utils.mixins.scss index 8032dee..98a6651 100644 --- a/src/assets/sass/mixins/_utils.mixins.scss +++ b/src/assets/sass/mixins/_utils.mixins.scss @@ -1,3 +1,4 @@ +@use 'sass:math'; @use '../variables/variables.fonts' as ft; @use '../variables/variables.colors' as c; @use '../mixins/vendor.mixins' as vnd; @@ -19,24 +20,23 @@ gap: f.pxToRem($gap); } - @mixin set-positioning($top, $right, $bottom, $left) { - @if ($top == 'auto' or unit($top) == '%') { + @if ($top == 'auto' or math.unit($top) == '%') { top: $top; } @else { top: f.pxToRem($top); } - @if ($right == 'auto' or unit($right) == '%') { + @if ($right == 'auto' or math.unit($right) == '%') { right: $right; } @else { right: f.pxToRem($right); } - @if ($bottom == 'auto' or unit($bottom) == '%') { + @if ($bottom == 'auto' or math.unit($bottom) == '%') { bottom: $bottom; } @else { bottom: f.pxToRem($bottom); } - @if ($left == 'auto' or unit($left) == '%') { + @if ($left == 'auto' or math.unit($left) == '%') { left: $left; } @else { left: f.pxToRem($left); @@ -49,24 +49,24 @@ } @mixin sized($width: auto, $height: auto) { - @if ($width == 'auto' or $width == 'inherit' or unit($width) == '%') { + @if ($width == 'auto' or $width == 'inherit' or math.unit($width) == '%') { width: $width ; } @else { width: f.pxToRem($width) ; } - @if ($height == 'auto' or $height == 'inherit' or unit($height) == '%') { + @if ($height == 'auto' or $height == 'inherit' or math.unit($height) == '%') { height: $height ; } @else { height: f.pxToRem($height); } } @mixin sizedImportant($width: auto, $height: auto) { - @if ($width == 'auto' or $width == 'inherit' or unit($width) == '%') { + @if ($width == 'auto' or $width == 'inherit' or math.unit($width) == '%') { width: $width !important ; } @else { width: f.pxToRem($width) !important ; } - @if ($height == 'auto' or $height == 'inherit' or unit($height) == '%') { + @if ($height == 'auto' or $height == 'inherit' or math.unit($height) == '%') { height: $height !important ; } @else { height: f.pxToRem($height) !important; diff --git a/src/assets/sass/mixins/_vendor.mixins.scss b/src/assets/sass/mixins/_vendor.mixins.scss index 7d76c4c..e9c76dd 100644 --- a/src/assets/sass/mixins/_vendor.mixins.scss +++ b/src/assets/sass/mixins/_vendor.mixins.scss @@ -1,3 +1,5 @@ +@use "sass:list"; +@use "sass:string"; @use "@assets/sass/_utils.functions.scss" as f; /// Vendor prefixes. @@ -32,7 +34,7 @@ $vendor-properties: ( 'transform', 'transition', 'transition-delay' -); + ); /// Values that should be vendorized. /// @var {list} @@ -49,16 +51,16 @@ $vendor-values: ( @mixin vendored($property, $value) { // Determine if property should expand. - $expandProperty: index($vendor-properties, $property); + $expandProperty: list.index($vendor-properties, $property); // Determine if value should expand (and if so, add '-prefix-' placeholder). $expandValue: false; @each $x in $value { @each $y in $vendor-values { - @if $y == str-slice($x, 1, str-length($y)) { + @if $y == string.slice($x, 1, string.length($y)) { - $value: set-nth($value, index($value, $x), '-prefix-' + $x); + $value: list.set-nth($value, list.index($value, $x), '-prefix-' + $x); $expandValue: true; } } diff --git a/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx b/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx index cad9ed7..72015cf 100644 --- a/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx +++ b/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx @@ -1,4 +1,4 @@ -import 'swiper/scss'; +import 'swiper/css'; import './TimelineSwiper.styles.scss'; import { observer } from 'mobx-react-lite'; @@ -6,7 +6,8 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import useMobx from '@stores/root-store'; import TimelineSwiperEdgeBtn from '@streetcode/TimelineBlock/TimelineSwiper/TimelineSwiperEdgeBtn/TimelineSwiperEdgeBtn.component'; -import SwiperCore, { Pagination } from 'swiper/core'; +import SwiperCore from 'swiper/core'; +import { Pagination } from 'swiper/modules'; import { Swiper, SwiperProps, SwiperRef, SwiperSlide } from 'swiper/react'; type SwiperWithoutChildren = Omit; diff --git a/src/index.d.ts b/src/index.d.ts index 88c7b40..8951edd 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -6,3 +6,7 @@ declare module '*.svg' { } declare module '*.css'; declare module '*.jpeg'; +declare module '*.scss' { + const classes: { [key: string]: string }; + export default classes; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index b0b4a82..9562b30 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,16 @@ { "compilerOptions": { + "target": "ES2022", + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], "jsx": "react-jsx", - "target": "es2017", - "module": "esnext", - "baseUrl": ".", + "module": "nodenext", + "moduleResolution": "nodenext", "paths": { - "*": ["node_modules/*"], + " * ": ["./node_modules/*"], "@/*": ["./src/*"], "@app/*": ["./src/app/*"], "@assets/*": ["./src/assets/*"], @@ -21,30 +26,26 @@ "@components/*": ["./src/app/common/components/*"], "@hooks/*": ["./src/app/common/hooks/*"], "@constants/*": ["./src/app/common/constants/*"], - "@utils/*": ["./src/app/common/utils/*"], + "@utils/*": ["./src/app/common/utils/*"] }, - "outDir": "dist", + "resolveJsonModule": true, "allowJs": true, - "skipLibCheck": true, + "strict": true, + "noFallthroughCasesInSwitch": true, "esModuleInterop": true, - "sourceMap": true, "allowSyntheticDefaultImports": true, - "strict": true, + "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": false, + "isolatedModules": true, "noEmit": false, - "lib": [ - "ES2021.String", - "DOM" - ] + "outDir": "dist", + "sourceMap": true }, "files": [ "./src/index.d.ts" ], "include": [ - "src", ".eslintrc.js" + "src", + ".eslintrc.js" ] } \ No newline at end of file From f44a959a3a1a5e31361a5d1d596b1643c66bc69c Mon Sep 17 00:00:00 2001 From: andrxpie Date: Tue, 19 May 2026 23:00:01 +0300 Subject: [PATCH 02/66] chore(client): update npm dependencies and resolve critical vulnerabilities --- package.json | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index dec8434..1858178 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "@tinymce/tinymce-react": "^4.3.0", + "@tinymce/tinymce-react": "^6.3.0", "@types/googlemaps": "^3.43.3", "@types/jest": "^27.5.2", "@types/maplibre-gl": "^1.14.0", @@ -69,10 +69,9 @@ "react-toastify": "^9.1.1", "slick-carousel": "^1.8.1", "styled-components": "^5.3.6", - "svg-react-loader": "^0.4.6", "svg-url-loader": "^8.0.0", "swiper": "^12.1.4", - "tinymce": "^6.4.0", + "tinymce": "^8.5.0", "typescript": "^4.9.4", "yet-another-react-lightbox": "^2.2.7" }, @@ -122,14 +121,26 @@ "eslint-config-airbnb": "^19.0.4", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", - "lint-staged": "^13.1.0", + "lint-staged": "^17.0.5", "sass": "^1.99.0", "sass-loader": "^16.0.8", "ts-loader": "^9.4.2", "url-loader": "^4.1.1", "webpack": "^5.75.0", "webpack-cli": "^5.0.1", - "webpack-dev-server": "^4.11.1" + "webpack-dev-server": "^5.2.4" + }, + "overrides": { + "tangram": { + "js-yaml": "^3.14.2", + "jszip": "^3.10.1" + }, + "nth-check": "^2.1.1", + "postcss": "^8.4.38", + "react-scripts": { + "webpack-dev-server": "^5.2.4", + "serialize-javascript": "^6.0.2" + } }, "description": " \"SoftServe ", "main": ".eslintrc.js", From a6d47e44cc8c78dc8d7113a6bffe248e28ea4bb7 Mon Sep 17 00:00:00 2001 From: andrxpie Date: Thu, 21 May 2026 14:47:35 +0300 Subject: [PATCH 03/66] Sort imports --- .../TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx b/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx index 72015cf..3413480 100644 --- a/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx +++ b/src/features/StreetcodePage/TimelineBlock/TimelineSwiper/TimelineSwiper.component.tsx @@ -1,4 +1,3 @@ -import 'swiper/css'; import './TimelineSwiper.styles.scss'; import { observer } from 'mobx-react-lite'; @@ -10,6 +9,8 @@ import SwiperCore from 'swiper/core'; import { Pagination } from 'swiper/modules'; import { Swiper, SwiperProps, SwiperRef, SwiperSlide } from 'swiper/react'; +import 'swiper/css'; + type SwiperWithoutChildren = Omit; SwiperCore.use([Pagination]); From 3dd58c495223298d3ce7943e47c2cecfa7468dcf Mon Sep 17 00:00:00 2001 From: andrxpie Date: Thu, 21 May 2026 17:57:04 +0300 Subject: [PATCH 04/66] feat(AdminPage): update AdminBar component and add DictionaryMainPage component --- .eslintrc.js | 2 +- src/features/AdminPage/AdminBar.component.tsx | 7 +++++-- src/features/AdminPage/AdminPage.component.tsx | 6 ++++-- .../DictionaryPage/DictionaryMainPage.component.tsx | 13 +++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx diff --git a/.eslintrc.js b/.eslintrc.js index 2a411f9..6e31065 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,6 +21,7 @@ module.exports = { 'plugin:react-hooks/recommended', ], rules: { + '@typescript-eslint/quotes': 'off', 'jsx-a11y/click-events-have-key-events': 'off', 'jsx-a11y/no-noninteractive-element-interactions': 'off', 'jsx-a11y/no-static-element-interactions': 'off', @@ -30,7 +31,6 @@ module.exports = { 'import/no-cycle': 'off', 'react/jsx-indent': 'off', 'no-nested-ternary': 'off', - '@typescript-eslint/indent': 'off', 'consistent-return': 'off', 'react/react-in-jsx-scope': 'off', 'react/require-default-props': 'off', diff --git a/src/features/AdminPage/AdminBar.component.tsx b/src/features/AdminPage/AdminBar.component.tsx index 6c90552..b2da0b5 100644 --- a/src/features/AdminPage/AdminBar.component.tsx +++ b/src/features/AdminPage/AdminBar.component.tsx @@ -1,13 +1,16 @@ -import FRONTEND_ROUTES from '@/app/common/constants/frontend-routes.constants'; import './AdminBar.styles.scss'; + import { Link } from 'react-router-dom'; +import FRONTEND_ROUTES from '@/app/common/constants/frontend-routes.constants'; + const AdminBar = () => (
Для фанів Партнери Команда -
+ Словник + ); export default AdminBar; diff --git a/src/features/AdminPage/AdminPage.component.tsx b/src/features/AdminPage/AdminPage.component.tsx index 56846b5..39db326 100644 --- a/src/features/AdminPage/AdminPage.component.tsx +++ b/src/features/AdminPage/AdminPage.component.tsx @@ -1,9 +1,11 @@ import './AdminPage.styles.scss'; + +import StreetcodeCatalogComponent from '@features/StreetcodeCatalogPage/StreetcodeCatalog.component'; + import AdminBar from './AdminBar.component'; -import StreetcodeCatalogComponent from '../StreetcodeCatalogPage/StreetcodeCatalog.component'; const AdminPage = () => ( -
+
diff --git a/src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx b/src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx new file mode 100644 index 0000000..0b9b2bd --- /dev/null +++ b/src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +interface Props { + className?: string; +} + +export const ComponentName: React.FC = ({ className }) => ( +
+
Lol
+
+); + +export default ComponentName; From a2f60a886dc524b1b6ac5a1dc0bf8bb021c8a638 Mon Sep 17 00:00:00 2001 From: andrxpie Date: Sun, 24 May 2026 00:02:54 +0300 Subject: [PATCH 05/66] Implement DictionaryMainPage and DictionaryModal components. --- .../api/streetcode/text-content/terms.api.ts | 4 +- src/app/router/Routes.tsx | 26 +- src/app/stores/term-store.ts | 124 +++--- .../DictionaryMainPage.component.tsx | 131 ++++++- .../DictionaryMainPage.styles.scss | 42 +++ .../DictionaryModal.component.tsx | 103 +++++ .../DictionaryModal.styles.scss | 0 .../PartnersPage/Partners.component.tsx | 357 +++++++++--------- src/models/streetcode/text-contents.model.ts | 14 +- 9 files changed, 535 insertions(+), 266 deletions(-) create mode 100644 src/features/AdminPage/DictionaryPage/DictionaryMainPage.styles.scss create mode 100644 src/features/AdminPage/DictionaryPage/DictionaryModal/DictionaryModal.component.tsx create mode 100644 src/features/AdminPage/DictionaryPage/DictionaryModal/DictionaryModal.styles.scss diff --git a/src/app/api/streetcode/text-content/terms.api.ts b/src/app/api/streetcode/text-content/terms.api.ts index 3500e67..ba87b1e 100644 --- a/src/app/api/streetcode/text-content/terms.api.ts +++ b/src/app/api/streetcode/text-content/terms.api.ts @@ -1,13 +1,13 @@ import Agent from '@api/agent.api'; import { API_ROUTES } from '@constants/api-routes.constants'; -import { Term } from '@models/streetcode/text-contents.model'; +import { Term, TermCreate } from '@models/streetcode/text-contents.model'; const TermsApi = { getById: (id: number) => Agent.get(`${API_ROUTES.TERMS.GET}/${id}`), getAll: () => Agent.get(`${API_ROUTES.TERMS.GET_ALL}`), - create: (term: Term) => Agent.post(`${API_ROUTES.TERMS.CREATE}`, term), + create: (term: TermCreate) => Agent.post(`${API_ROUTES.TERMS.CREATE}`, term), update: (term: Term) => Agent.put(`${API_ROUTES.TERMS.UPDATE}`, term), diff --git a/src/app/router/Routes.tsx b/src/app/router/Routes.tsx index 709868d..50c21ba 100644 --- a/src/app/router/Routes.tsx +++ b/src/app/router/Routes.tsx @@ -4,17 +4,16 @@ import ForFansMainPage from '@features/AdminPage/ForFansPage/ForFansMainPage.com import App from '@layout/app/App.component'; import StreetcodeContent from '@streetcode/Streetcode.component'; +import ContactUs from '@/features/AdditionalPages/ContactUsPage/ContanctUs.component'; +import NewsPage from '@/features/AdditionalPages/NewsPage/News.component'; import NotFound from '@/features/AdditionalPages/NotFoundPage/NotFound.component'; import PartnersPage from '@/features/AdditionalPages/PartnersPage/Partners.component'; +import SupportPage from '@/features/AdditionalPages/SupportUsPage/SupportUs.component'; import AdminPage from '@/features/AdminPage/AdminPage.component'; +import DictionaryMainPage from '@/features/AdminPage/DictionaryPage/DictionaryMainPage.component'; import Partners from '@/features/AdminPage/PartnersPage/Partners.component'; import TeamPage from '@/features/AdminPage/TeamPage/TeamPage.component'; import StreetcodeCatalog from '@/features/StreetcodeCatalogPage/StreetcodeCatalog.component'; -import NewsPage from '@/features/AdditionalPages/NewsPage/News.component'; -import ContactUs from '@/features/AdditionalPages/ContactUsPage/ContanctUs.component'; -import SupportPage from '@/features/AdditionalPages/SupportUsPage/SupportUs.component'; - - const router = createBrowserRouter(createRoutesFromElements( }> @@ -25,7 +24,7 @@ const router = createBrowserRouter(createRoutesFromElements( } - /> + /> } @@ -34,17 +33,22 @@ const router = createBrowserRouter(createRoutesFromElements( path={FRONTEND_ROUTES.ADMIN.PARTNERS} element={} /> - } /> + } + /> - )} + element={} + /> + } /> } /> } /> } /> - } /> + } /> } /> } /> , diff --git a/src/app/stores/term-store.ts b/src/app/stores/term-store.ts index 1e6c25a..b7ff3ee 100644 --- a/src/app/stores/term-store.ts +++ b/src/app/stores/term-store.ts @@ -1,71 +1,75 @@ -import { makeAutoObservable, runInAction } from 'mobx'; -import termsApi from '@api/streetcode/text-content/terms.api'; -import { Term } from '@models/streetcode/text-contents.model'; +import { makeAutoObservable, runInAction } from "mobx"; +import termsApi from "@api/streetcode/text-content/terms.api"; +import { Term, TermCreate } from "@models/streetcode/text-contents.model"; export default class TermStore { - public TermMap = new Map(); + public TermMap = new Map(); - public constructor() { - makeAutoObservable(this); - } + public constructor() { + makeAutoObservable(this); + } - public setInternalMap = (terms: Term[]) => { - terms.forEach(this.setItem); - }; + public setInternalMap = (terms: Term[]) => { + terms.forEach(this.setItem); + }; - private setItem = (term: Term) => { - this.TermMap.set(term.id, term); - }; + private setItem = (term: Term) => { + this.TermMap.set(term.id, term); + }; - get getTermArray() { - return Array.from(this.TermMap.values()); - } + get getTermArray() { + return Array.from(this.TermMap.values()); + } - public fetchTerms = async () => { - try { - const terms = await termsApi.getAll(); - this.setInternalMap(terms); - } catch (error: unknown) {} - }; + public fetchTerms = async () => { + try { + const terms = await termsApi.getAll(); + this.setInternalMap(terms); + } catch (error: unknown) { + return null; + } + }; - public createTerm = async (term: Term) => { - try { - let newData = null as unknown as Term; - await termsApi.create(term).then( - (response) => { - this.setItem(response); - newData = response; - }, - ); - return newData; - } catch (error: unknown) { - return null; - } - }; + public createTerm = async (term: TermCreate) => { + try { + let newData = null as unknown as Term; + await termsApi.create(term).then((response) => { + this.setItem(response); + newData = response; + }); + return newData; + } catch (error: unknown) { + return null; + } + }; - public updateTerm = async (id: number, term: Term) => { - try { - if (id !== 0) { - await termsApi.update(term); - runInAction(() => { - const updatedTerm = { - ...this.TermMap.get(term.id), - ...term, - }; - this.setItem(updatedTerm as Term); - }); - } - } catch (error: unknown) {} - }; + public updateTerm = async (id: number, term: Term) => { + try { + if (id !== 0) { + await termsApi.update(term); + runInAction(() => { + const updatedTerm = { + ...this.TermMap.get(term.id), + ...term, + }; + this.setItem(updatedTerm as Term); + }); + } + } catch (error: unknown) { + return null; + } + }; - public deleteTerm = async (termId: number) => { - try { - if (termId !== 0) { - await termsApi.delete(termId); - runInAction(() => { - this.TermMap.delete(termId); - }); - } - } catch (error: unknown) {} - }; + public deleteTerm = async (termId: number) => { + try { + if (termId !== 0) { + await termsApi.delete(termId); + runInAction(() => { + this.TermMap.delete(termId); + }); + } + } catch (error: unknown) { + return null; + } + }; } diff --git a/src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx b/src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx index 0b9b2bd..1211d2d 100644 --- a/src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx +++ b/src/features/AdminPage/DictionaryPage/DictionaryMainPage.component.tsx @@ -1,13 +1,124 @@ -import React from "react"; +import "./DictionaryMainPage.styles.scss"; -interface Props { - className?: string; -} +import { observer } from "mobx-react-lite"; +import { useEffect, useState } from "react"; +import { + DeleteOutlined, + EditOutlined, +} from "@ant-design/icons/lib/icons"; +import AdminBar from "@features/AdminPage/AdminBar.component"; +import DictionaryModal from "@features/AdminPage/DictionaryPage/DictionaryModal/DictionaryModal.component"; +import useMobx, { useModalContext } from "@stores/root-store"; -export const ComponentName: React.FC = ({ className }) => ( -
-
Lol
-
-); +import Button from "antd/es/button"; +import Table, { ColumnsType } from "antd/es/table"; -export default ComponentName; +import TermApi from "@/app/api/streetcode/text-content/terms.api"; +import { Term } from "@/models/streetcode/text-contents.model"; +/* eslint-disable indent */ +export const DictionaryMainPage: React.FC = observer(() => { + const { termsStore } = useMobx(); + const { modalStore } = useModalContext(); + const [modalAddOpened, setModalAddOpened] = useState(false); + + const updatedTerms = () => { + Promise.all([termsStore?.fetchTerms()]) + .then(() => termsStore.setInternalMap(termsStore.getTermArray)); + }; + + useEffect(() => { + updatedTerms(); + }, []); + + const columns: ColumnsType = [ + { + title: "Назва", + dataIndex: "title", + key: "title", + width: "30%", + render(value, record) { + return ( +
+

{value}

+
+ ); + }, + }, + { + title: "Опис", + dataIndex: "description", + key: "description", + render: (description) => ( +

{description}

+ ), + }, + { + title: "Дії", + dataIndex: "action", + key: "action", + width: "10%", + render: (value, term, index) => ( +
+ { + modalStore.setConfirmationModal( + "confirmation", + () => { + TermApi.delete(term.id) + .then(() => { + termsStore.TermMap.delete(term.id); + }) + .catch((e) => {}); + modalStore.setConfirmationModal("confirmation"); + }, + "Ви впевнені, що хочете видалити цей термін?", + ); + }} + /> + {/* { + setTermToEdit(term); + setModalEditOpened(true); + }} + /> */} +
+ ), + }, + ]; + + return ( +
+ +
+
+ +
+ + + + + ); +}); + +export default DictionaryMainPage; diff --git a/src/features/AdminPage/DictionaryPage/DictionaryMainPage.styles.scss b/src/features/AdminPage/DictionaryPage/DictionaryMainPage.styles.scss new file mode 100644 index 0000000..d420813 --- /dev/null +++ b/src/features/AdminPage/DictionaryPage/DictionaryMainPage.styles.scss @@ -0,0 +1,42 @@ +@use "@sass/mixins/_utils.mixins.scss" as mut; +@use "@sass/variables/_variables.fonts.scss" as ft; +@use "@sass/variables/_variables.colors.scss" as c; +@use "@sass/_utils.functions.scss" as f; + +.dictionary-page { + padding-top: f.pxToRem(100px); + display: flex; + gap: f.pxToRem(10px); + .dictionary-page-add-button { + min-height: f.pxToRem(50px); + min-width: f.pxToRem(120px); + } + .container-justify-end { + display: flex; + justify-content: end; + } + .dictionary-page-container { + @include mut.sized(80%); + + .partners-table-logo { + height: f.pxToRem(40px); + margin: 0; + padding: 0; + } + .partner-page-actions { + @include mut.flexed(row, start, space-between); + } + .partner-links { + @include mut.flexed(row, start, space-between, wrap, 4px); + } + .partners-table { + margin-top: f.pxToRem(20px); + } + .partner-table-item-name { + @include mut.flexed(row, start, flex-start, wrap, 4px); + } + .site-link { + word-break: break-all; + } + } +} diff --git a/src/features/AdminPage/DictionaryPage/DictionaryModal/DictionaryModal.component.tsx b/src/features/AdminPage/DictionaryPage/DictionaryModal/DictionaryModal.component.tsx new file mode 100644 index 0000000..daf5bbc --- /dev/null +++ b/src/features/AdminPage/DictionaryPage/DictionaryModal/DictionaryModal.component.tsx @@ -0,0 +1,103 @@ +import "./DictionaryModal.styles.scss"; +import "@features/AdminPage/AdminModal.styles.scss"; + +import CancelBtn from "@images/utils/Cancel_btn.svg"; + +import { observer } from "mobx-react-lite"; +import React from "react"; + +import Button from "antd/es/button"; +import Form from "antd/es/form"; +import Input from "antd/es/input/Input"; +import TextArea from "antd/es/input/TextArea"; +import Modal from "antd/es/modal"; + +// eslint-disable-next-line import/extensions +import useMobx from "@/app/stores/root-store"; +import { Term, TermCreate } from "@/models/streetcode/text-contents.model"; + +interface Props { + termItem?: Term; + open: boolean; + setIsModalOpen: React.Dispatch>; + afterSubmit?: (partner: Term) => void; +} + +export const DictionaryModal: React.FC = observer( + ({ termItem, open, setIsModalOpen, afterSubmit }) => { + const [form] = Form.useForm(); + + const { termsStore } = useMobx(); + + const closeAndCleanData = () => { + form.resetFields(); + setIsModalOpen(false); + }; + + const onSuccesfulSubmitTerm = async (formValues: TermCreate) => { + const term: TermCreate = { + title: formValues.title, + description: formValues.description?.trim() || undefined, + }; + + try { + const t = await termsStore.createTerm(term); + if (afterSubmit && t) { + afterSubmit(t); + } + } catch (e) { + console.error(e); + } finally { + closeAndCleanData(); + } + }; + + return ( + } + > +
+
+
+

+ {termItem ? "Редагувати " : "Додати "} + термін +

+
+ + + + + + +