From d11ae8d2225ba32218281e943f222325f3a4ac4b Mon Sep 17 00:00:00 2001 From: VanyaMate Date: Mon, 29 Jul 2024 20:05:51 +0300 Subject: [PATCH] add Languages components, add models, actions, effects --- public/locales/en/languages.json | 21 ++ public/locales/ru/languages.json | 21 ++ .../removeLanguage/removeLanguage.action.ts | 13 + .../removeLanguageFolder.action.ts | 13 + .../removeLanguageWord.action.ts | 13 + .../updateLanguage/updateLanguage.action.ts | 16 ++ .../updateLanguageFolder.action.ts | 16 ++ .../updateLanguageWord.action.ts | 16 ++ src/app/model/languages/languages.model.ts | 104 +++++++- .../CreateLanguageFolderFormModalButton.tsx | 48 ++++ .../ui/CreateLanguageFormModalButton.tsx | 45 ++++ .../ui/CreateLanguageWordFormModalButton.tsx | 50 ++++ .../ui/RemoveLanguageButton.tsx | 35 +++ .../ui/RemoveLanguageFolderButton.tsx | 37 +++ .../ui/RemoveLanguageWordButton.tsx | 37 +++ .../UpdateLanguageFolderFormModalButton.tsx | 51 ++++ .../ui/UpdateLanguageFormModalButton.tsx | 49 ++++ .../ui/UpdateLanguageWordFormModalButton.tsx | 51 ++++ src/shared/ui-kit/box/Row/ui/Row.module.scss | 8 + src/shared/ui-kit/box/Row/ui/Row.tsx | 17 +- .../ui/ButtonWithLoading.tsx | 2 +- .../ui/ControlDetails.module.scss | 16 ++ .../ControlDetails/ui/ControlDetails.tsx | 57 +++++ .../ui-kit/details/Details/ui/Details.tsx | 13 +- .../Details/ui/DetailsBody/DetailsBody.tsx | 4 +- src/shared/ui-kit/forms/Form/hooks/useForm.ts | 14 +- .../InputWithError/hooks/useInputWithError.ts | 20 +- .../ui/LanguagesContainer.module.scss | 18 +- .../ui/LanguagesContainer.tsx | 223 +++++------------- .../ui/CreateLanguageFolderForm.module.scss | 5 + .../ui/CreateLanguageFolderForm.tsx | 79 +++++++ .../ui/CreateLanguageForm.module.scss | 5 + .../ui/CreateLanguageForm.tsx | 75 ++++++ .../ui/CreateLanguageWordForm.module.scss | 5 + .../ui/CreateLanguageWordForm.tsx | 96 ++++++++ .../ui/CreateLanguageFolderForm.module.scss | 5 + .../ui/UpdateLanguageFolderForm.tsx | 83 +++++++ .../ui/UpdateLanguageForm.module.scss | 5 + .../ui/UpdateLanguageForm.tsx | 83 +++++++ .../ui/UpdateLanguageWordForm.module.scss | 5 + .../ui/UpdateLanguageWordForm.tsx | 105 +++++++++ .../ui/LanguageFolderItem.module.scss | 7 + .../ui/LanguageFolderItem.tsx | 58 +++++ .../LanguageItem/ui/LanguageItem.module.scss | 13 + .../items/LanguageItem/ui/LanguageItem.tsx | 71 ++++++ .../ui/LanguageWordItem.module.scss | 11 + .../LanguageWordItem/ui/LanguageWordItem.tsx | 87 +++++++ 47 files changed, 1639 insertions(+), 187 deletions(-) create mode 100644 public/locales/en/languages.json create mode 100644 public/locales/ru/languages.json create mode 100644 src/app/action/languages/removeLanguage/removeLanguage.action.ts create mode 100644 src/app/action/languages/removeLanguageFolder/removeLanguageFolder.action.ts create mode 100644 src/app/action/languages/removeLanguageWord/removeLanguageWord.action.ts create mode 100644 src/app/action/languages/updateLanguage/updateLanguage.action.ts create mode 100644 src/app/action/languages/updateLanguageFolder/updateLanguageFolder.action.ts create mode 100644 src/app/action/languages/updateLanguageWord/updateLanguageWord.action.ts create mode 100644 src/features/language/button/CreateLanguageFolderFormModalButton/ui/CreateLanguageFolderFormModalButton.tsx create mode 100644 src/features/language/button/CreateLanguageFormModalButton/ui/CreateLanguageFormModalButton.tsx create mode 100644 src/features/language/button/CreateLanguageWordFormModalButton/ui/CreateLanguageWordFormModalButton.tsx create mode 100644 src/features/language/button/RemoveLanguageButton/ui/RemoveLanguageButton.tsx create mode 100644 src/features/language/button/RemoveLanguageFolderButton/ui/RemoveLanguageFolderButton.tsx create mode 100644 src/features/language/button/RemoveLanguageWordButton/ui/RemoveLanguageWordButton.tsx create mode 100644 src/features/language/button/UpdateLanguageFolderFormModalButton/ui/UpdateLanguageFolderFormModalButton.tsx create mode 100644 src/features/language/button/UpdateLanguageFormModalButton/ui/UpdateLanguageFormModalButton.tsx create mode 100644 src/features/language/button/UpdateLanguageWordFormModalButton/ui/UpdateLanguageWordFormModalButton.tsx create mode 100644 src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.module.scss create mode 100644 src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.tsx create mode 100644 src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss create mode 100644 src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.tsx create mode 100644 src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.module.scss create mode 100644 src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.tsx create mode 100644 src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.module.scss create mode 100644 src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.tsx create mode 100644 src/widgets/language/form/UpdateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss create mode 100644 src/widgets/language/form/UpdateLanguageFolderForm/ui/UpdateLanguageFolderForm.tsx create mode 100644 src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.module.scss create mode 100644 src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.tsx create mode 100644 src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.module.scss create mode 100644 src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.tsx create mode 100644 src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.module.scss create mode 100644 src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.tsx create mode 100644 src/widgets/language/items/LanguageItem/ui/LanguageItem.module.scss create mode 100644 src/widgets/language/items/LanguageItem/ui/LanguageItem.tsx create mode 100644 src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.module.scss create mode 100644 src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.tsx diff --git a/public/locales/en/languages.json b/public/locales/en/languages.json new file mode 100644 index 00000000..37aa228d --- /dev/null +++ b/public/locales/en/languages.json @@ -0,0 +1,21 @@ +{ + "language_page_title": "Languages", + "add_language": "Add language", + "add_folder": "Add folder", + "add_word": "Add word", + "update_language": "Update language", + "update_folder": "Update folder", + "update_word": "Update word", + "remove_language": "Remove language", + "remove_folder": "Remove folder", + "remove_word": "Remove word", + "add_item": "Add", + "check_word": "Check as remembered", + "uncheck_word": "Check as forgotten", + "language_title": "Language title", + "folder_title": "Folder title", + "word_original": "Word in original", + "word_translations": "Translations", + "word_notice": "Notice", + "no_folder_selected": "Folder not selected" +} \ No newline at end of file diff --git a/public/locales/ru/languages.json b/public/locales/ru/languages.json new file mode 100644 index 00000000..e4fec67a --- /dev/null +++ b/public/locales/ru/languages.json @@ -0,0 +1,21 @@ +{ + "language_page_title": "Языки", + "add_language": "Добавить язык", + "add_folder": "Добавить папку", + "add_word": "Добавить слово", + "update_language": "Изменить язык", + "update_folder": "Изменить папку", + "update_word": "Изменить слово", + "remove_language": "Удалить язык", + "remove_folder": "Удалить папку", + "remove_word": "Удалить слово", + "add_item": "Добавить", + "check_word": "Отметить как запомненное", + "uncheck_word": "Отметить как забытое", + "language_title": "Название языка", + "folder_title": "Название папки", + "word_original": "Слово в оригинале", + "word_translations": "Переводы", + "word_notice": "Заметка", + "no_folder_selected": "Папка не выбрана" +} \ No newline at end of file diff --git a/src/app/action/languages/removeLanguage/removeLanguage.action.ts b/src/app/action/languages/removeLanguage/removeLanguage.action.ts new file mode 100644 index 00000000..f89a0016 --- /dev/null +++ b/src/app/action/languages/removeLanguage/removeLanguage.action.ts @@ -0,0 +1,13 @@ +import { request } from '@/app/lib/fetch/request.ts'; +import { + isDomainNotificationLanguageDeletedData, +} from 'product-types/dist/notification/notification-data-types/language/DomainNotificationLanguageDeletedData'; + + +export const removeLanguageAction = function (languageId: string) { + return request( + `v1/language/${ languageId }`, + { method: 'DELETE' }, + isDomainNotificationLanguageDeletedData, + ); +}; \ No newline at end of file diff --git a/src/app/action/languages/removeLanguageFolder/removeLanguageFolder.action.ts b/src/app/action/languages/removeLanguageFolder/removeLanguageFolder.action.ts new file mode 100644 index 00000000..26a56b2e --- /dev/null +++ b/src/app/action/languages/removeLanguageFolder/removeLanguageFolder.action.ts @@ -0,0 +1,13 @@ +import { request } from '@/app/lib/fetch/request.ts'; +import { + isDomainNotificationLanguageFolderDeletedData, +} from 'product-types/dist/notification/notification-data-types/language/DomainNotificationLanguageFolderDeletedData'; + + +export const removeLanguageFolderAction = function (folderId: string) { + return request( + `v1/language/folder/${ folderId }`, + { method: 'DELETE' }, + isDomainNotificationLanguageFolderDeletedData, + ); +}; \ No newline at end of file diff --git a/src/app/action/languages/removeLanguageWord/removeLanguageWord.action.ts b/src/app/action/languages/removeLanguageWord/removeLanguageWord.action.ts new file mode 100644 index 00000000..10caca39 --- /dev/null +++ b/src/app/action/languages/removeLanguageWord/removeLanguageWord.action.ts @@ -0,0 +1,13 @@ +import { request } from '@/app/lib/fetch/request.ts'; +import { + isDomainNotificationLanguageWordDeletedData, +} from 'product-types/dist/notification/notification-data-types/language/DomainNotificationLanguageWordDeletedData'; + + +export const removeLanguageWordAction = function (wordId: string) { + return request( + `v1/language/word/${ wordId }`, + { method: 'DELETE' }, + isDomainNotificationLanguageWordDeletedData, + ); +}; \ No newline at end of file diff --git a/src/app/action/languages/updateLanguage/updateLanguage.action.ts b/src/app/action/languages/updateLanguage/updateLanguage.action.ts new file mode 100644 index 00000000..f2638a40 --- /dev/null +++ b/src/app/action/languages/updateLanguage/updateLanguage.action.ts @@ -0,0 +1,16 @@ +import { + DomainLanguageUpdateData, +} from 'product-types/dist/language/DomainLanguageUpdateData'; +import { request } from '@/app/lib/fetch/request.ts'; +import { + isDomainNotificationLanguageUpdateData, +} from 'product-types/dist/notification/notification-data-types/language/DomainNotificationLanguageUpdateData'; + + +export const updateLanguageAction = function (languageId: string, updateData: DomainLanguageUpdateData) { + return request( + `v1/language/${ languageId }`, + { method: 'PATCH', body: JSON.stringify(updateData) }, + isDomainNotificationLanguageUpdateData, + ); +}; \ No newline at end of file diff --git a/src/app/action/languages/updateLanguageFolder/updateLanguageFolder.action.ts b/src/app/action/languages/updateLanguageFolder/updateLanguageFolder.action.ts new file mode 100644 index 00000000..9af80f90 --- /dev/null +++ b/src/app/action/languages/updateLanguageFolder/updateLanguageFolder.action.ts @@ -0,0 +1,16 @@ +import { + DomainLanguageFolderUpdateData, +} from 'product-types/dist/language/DomainLanguageFolderUpdateData'; +import { request } from '@/app/lib/fetch/request.ts'; +import { + isDomainNotificationLanguageFolderUpdateData, +} from 'product-types/dist/notification/notification-data-types/language/DomainNotificationLanguageFolderUpdateData'; + + +export const updateLanguageFolderAction = function (folderId: string, updateData: DomainLanguageFolderUpdateData) { + return request( + `v1/language/folder/${ folderId }`, + { method: 'PATCH', body: JSON.stringify(updateData) }, + isDomainNotificationLanguageFolderUpdateData, + ); +}; \ No newline at end of file diff --git a/src/app/action/languages/updateLanguageWord/updateLanguageWord.action.ts b/src/app/action/languages/updateLanguageWord/updateLanguageWord.action.ts new file mode 100644 index 00000000..4f706dda --- /dev/null +++ b/src/app/action/languages/updateLanguageWord/updateLanguageWord.action.ts @@ -0,0 +1,16 @@ +import { + DomainLanguageWordUpdateData, +} from 'product-types/dist/language/DomainLanguageWordUpdateData'; +import { request } from '@/app/lib/fetch/request.ts'; +import { + isDomainNotificationLanguageWordUpdateData, +} from 'product-types/dist/notification/notification-data-types/language/DomainNotificationLanguageWordUpdateData'; + + +export const updateLanguageWordAction = function (wordId: string, updateData: DomainLanguageWordUpdateData) { + return request( + `v1/language/word/${ wordId }`, + { method: 'PATCH', body: JSON.stringify(updateData) }, + isDomainNotificationLanguageWordUpdateData, + ); +}; \ No newline at end of file diff --git a/src/app/model/languages/languages.model.ts b/src/app/model/languages/languages.model.ts index 9f56fbf9..2cbf925f 100644 --- a/src/app/model/languages/languages.model.ts +++ b/src/app/model/languages/languages.model.ts @@ -20,11 +20,35 @@ import { import { getMyLanguageFolderWordsAction, } from '@/app/action/languages/getMyLanguageFolderWords/getMyLanguageFolderWords.action.ts'; +import { + removeLanguageWordAction, +} from '@/app/action/languages/removeLanguageWord/removeLanguageWord.action.ts'; +import { + updateLanguageAction, +} from '@/app/action/languages/updateLanguage/updateLanguage.action.ts'; +import { + updateLanguageFolderAction, +} from '@/app/action/languages/updateLanguageFolder/updateLanguageFolder.action.ts'; +import { + updateLanguageWordAction, +} from '@/app/action/languages/updateLanguageWord/updateLanguageWord.action.ts'; +import { + removeLanguageAction, +} from '@/app/action/languages/removeLanguage/removeLanguage.action.ts'; +import { + removeLanguageFolderAction, +} from '@/app/action/languages/removeLanguageFolder/removeLanguageFolder.action.ts'; export const createLanguageEffect = effect(createLanguageAction); export const createLanguageFolderEffect = effect(createLanguageFolderAction); export const createLanguageWordEffect = effect(createLanguageWordAction); +export const updateLanguageEffect = effect(updateLanguageAction); +export const updateLanguageFolderEffect = effect(updateLanguageFolderAction); +export const updateLanguageWordEffect = effect(updateLanguageWordAction); +export const removeLanguageEffect = effect(removeLanguageAction); +export const removeLanguageFolderEffect = effect(removeLanguageFolderAction); +export const removeLanguageWordEffect = effect(removeLanguageWordAction); export const getMyLanguagesEffect = effect(getMyLanguagesAction); export const getMyLanguageFolderWordsEffect = effect(getMyLanguageFolderWordsAction); @@ -60,6 +84,58 @@ export const $languagesList = store>([]) : language, ) : state; + }) + .on(updateLanguageEffect, 'onSuccess', (state, { result }) => { + return state.map((language) => { + if (language.id === result.language.id) { + return { ...language, ...result.language }; + } + + return language; + }); + }) + .on(updateLanguageFolderEffect, 'onSuccess', (state, { result }) => { + return state.map((language) => { + if (language.id === result.language.id) { + const folders = language.folders.map((folder) => { + if (folder.id === result.folder.id) { + return { ...folder, ...result.folder }; + } + + return folder; + }); + + return { ...language, folders }; + } + + return language; + }); + }) + .on(removeLanguageEffect, 'onSuccess', (state, { result }) => { + return state.filter((language) => { + if (language.id === result.language.id) { + return false; + } + + return true; + }); + }) + .on(removeLanguageFolderEffect, 'onSuccess', (state, { result }) => { + return state.map((language) => { + if (language.id === result.language.id) { + const folders = language.folders.filter((folder) => { + if (folder.id === result.folder.id) { + return false; + } + + return true; + }); + + return { ...language, folders }; + } + + return language; + }); }); export const $currentFolderId = store('') @@ -76,4 +152,30 @@ export const $languageFolderWordsList = store>([]) } return state; }, - ); \ No newline at end of file + ) + .on(updateLanguageWordEffect, 'onSuccess', (state, { result }) => { + if ($currentFolderId.get() === result.folder.id) { + return state.map((word) => { + if (word.id === result.word.id) { + return { ...word, ...result.word }; + } + + return word; + }); + } + + return state; + }) + .on(removeLanguageWordEffect, 'onSuccess', (state, { result }) => { + if ($currentFolderId.get() === result.folder.id) { + return state.filter((word) => { + if (word.id === result.word.id) { + return false; + } + + return true; + }); + } + + return state; + }); \ No newline at end of file diff --git a/src/features/language/button/CreateLanguageFolderFormModalButton/ui/CreateLanguageFolderFormModalButton.tsx b/src/features/language/button/CreateLanguageFolderFormModalButton/ui/CreateLanguageFolderFormModalButton.tsx new file mode 100644 index 00000000..75e01d2a --- /dev/null +++ b/src/features/language/button/CreateLanguageFolderFormModalButton/ui/CreateLanguageFolderFormModalButton.tsx @@ -0,0 +1,48 @@ +import { FC, memo } from 'react'; +import { + Button, + ButtonProps, +} from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { + useModalController, +} from '@/shared/ui-kit/modal/Modal/hooks/useModalController.ts'; +import { Modal } from '@/shared/ui-kit/modal/Modal/ui/Modal.tsx'; +import { IoAddCircle } from 'react-icons/io5'; +import { + CreateLanguageFolderForm, +} from '@/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.tsx'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type CreateLanguageFolderFormModalButtonProps = + { + languageId: string; + } + & ButtonProps; + +export const CreateLanguageFolderFormModalButton: FC = memo(function CreateLanguageFolderFormModalButton (props) { + const { languageId, ...other } = props; + const modalController = useModalController(); + const { t } = useTranslation([ 'languages' ]); + + return ( + <> + + modalController.setOpened(false) } + /> + + + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/CreateLanguageFormModalButton/ui/CreateLanguageFormModalButton.tsx b/src/features/language/button/CreateLanguageFormModalButton/ui/CreateLanguageFormModalButton.tsx new file mode 100644 index 00000000..b667f4d9 --- /dev/null +++ b/src/features/language/button/CreateLanguageFormModalButton/ui/CreateLanguageFormModalButton.tsx @@ -0,0 +1,45 @@ +import { FC, memo } from 'react'; +import { + Button, + ButtonProps, +} from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { IoAddCircle } from 'react-icons/io5'; +import { Modal } from '@/shared/ui-kit/modal/Modal/ui/Modal.tsx'; +import { + CreateLanguageForm, +} from '@/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.tsx'; +import { + useModalController, +} from '@/shared/ui-kit/modal/Modal/hooks/useModalController.ts'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type CreateLanguageFormModalButtonProps = + {} + & ButtonProps; + +export const CreateLanguageFormModalButton: FC = memo(function CreateLanguageFormModalButton (props) { + const { ...other } = props; + const modalController = useModalController(); + const { t } = useTranslation([ 'languages' ]); + + return ( + <> + + modalController.setOpened(false) } + /> + + + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/CreateLanguageWordFormModalButton/ui/CreateLanguageWordFormModalButton.tsx b/src/features/language/button/CreateLanguageWordFormModalButton/ui/CreateLanguageWordFormModalButton.tsx new file mode 100644 index 00000000..d6321c61 --- /dev/null +++ b/src/features/language/button/CreateLanguageWordFormModalButton/ui/CreateLanguageWordFormModalButton.tsx @@ -0,0 +1,50 @@ +import { FC, memo } from 'react'; +import { + useModalController, +} from '@/shared/ui-kit/modal/Modal/hooks/useModalController.ts'; +import { Modal } from '@/shared/ui-kit/modal/Modal/ui/Modal.tsx'; +import { + CreateLanguageWordForm, +} from '@/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.tsx'; +import { + Button, + ButtonProps, +} from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { IoAddCircleOutline } from 'react-icons/io5'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type CreateLanguageWordFormModalButtonProps = + { + folderId: string; + } + & ButtonProps; + +export const CreateLanguageWordFormModalButton: FC = memo(function CreateLanguageWordFormModalButton (props) { + const { folderId, ...other } = props; + const modalController = useModalController(); + const { t } = useTranslation([ 'languages' ]); + + return ( + <> + + modalController.setOpened(false) } + /> + + + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/RemoveLanguageButton/ui/RemoveLanguageButton.tsx b/src/features/language/button/RemoveLanguageButton/ui/RemoveLanguageButton.tsx new file mode 100644 index 00000000..d6c2c28f --- /dev/null +++ b/src/features/language/button/RemoveLanguageButton/ui/RemoveLanguageButton.tsx @@ -0,0 +1,35 @@ +import { FC, memo } from 'react'; +import { ButtonProps } from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { IoTrash } from 'react-icons/io5'; +import { removeLanguageEffect } from '@/app/model/languages/languages.model.ts'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type RemoveLanguageButtonProps = + { + languageId: string; + } + & ButtonProps; + +export const RemoveLanguageButton: FC = memo(function RemoveLanguageButton (props) { + const { languageId, ...other } = props; + const { t } = useTranslation([ 'languages' ]); + + return ( + + removeLanguageEffect(languageId) } + quad + styleType={ ButtonStyleType.DANGER } + > + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/RemoveLanguageFolderButton/ui/RemoveLanguageFolderButton.tsx b/src/features/language/button/RemoveLanguageFolderButton/ui/RemoveLanguageFolderButton.tsx new file mode 100644 index 00000000..0bfa37a7 --- /dev/null +++ b/src/features/language/button/RemoveLanguageFolderButton/ui/RemoveLanguageFolderButton.tsx @@ -0,0 +1,37 @@ +import { FC, memo } from 'react'; +import { ButtonProps } from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { IoTrash } from 'react-icons/io5'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { + removeLanguageFolderEffect, +} from '@/app/model/languages/languages.model.ts'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type RemoveLanguageFolderButtonProps = + { + folderId: string; + } + & ButtonProps; + +export const RemoveLanguageFolderButton: FC = memo(function RemoveLanguageFolderButton (props) { + const { folderId, ...other } = props; + const { t } = useTranslation([ 'languages' ]); + + return ( + + removeLanguageFolderEffect(folderId) } + quad + styleType={ ButtonStyleType.DANGER } + > + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/RemoveLanguageWordButton/ui/RemoveLanguageWordButton.tsx b/src/features/language/button/RemoveLanguageWordButton/ui/RemoveLanguageWordButton.tsx new file mode 100644 index 00000000..19aafbf1 --- /dev/null +++ b/src/features/language/button/RemoveLanguageWordButton/ui/RemoveLanguageWordButton.tsx @@ -0,0 +1,37 @@ +import { FC, memo } from 'react'; +import { ButtonProps } from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { IoTrash } from 'react-icons/io5'; +import { + removeLanguageWordEffect, +} from '@/app/model/languages/languages.model.ts'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type RemoveLanguageWordButtonProps = + { + wordId: string; + } + & ButtonProps; + +export const RemoveLanguageWordButton: FC = memo(function RemoveLanguageWordButton (props) { + const { wordId, ...other } = props; + const { t } = useTranslation([ 'languages' ]); + + return ( + + removeLanguageWordEffect(wordId) } + quad + styleType={ ButtonStyleType.DANGER } + > + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/UpdateLanguageFolderFormModalButton/ui/UpdateLanguageFolderFormModalButton.tsx b/src/features/language/button/UpdateLanguageFolderFormModalButton/ui/UpdateLanguageFolderFormModalButton.tsx new file mode 100644 index 00000000..b7412800 --- /dev/null +++ b/src/features/language/button/UpdateLanguageFolderFormModalButton/ui/UpdateLanguageFolderFormModalButton.tsx @@ -0,0 +1,51 @@ +import { FC, memo } from 'react'; +import { + useModalController, +} from '@/shared/ui-kit/modal/Modal/hooks/useModalController.ts'; +import { Modal } from '@/shared/ui-kit/modal/Modal/ui/Modal.tsx'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { + Button, + ButtonProps, +} from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { IoSettings } from 'react-icons/io5'; +import { + DomainLanguageFolder, +} from 'product-types/dist/language/DomainLanguageFolder'; +import { + UpdateLanguageFolderForm, +} from '@/widgets/language/form/UpdateLanguageFolderForm/ui/UpdateLanguageFolderForm.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type UpdateLanguageFolderFormModalButtonProps = + { + folder: DomainLanguageFolder; + } + & ButtonProps; + +export const UpdateLanguageFolderFormModalButton: FC = memo(function UpdateLanguageFolderFormModalButton (props) { + const { folder, ...other } = props; + const modalController = useModalController(); + const { t } = useTranslation([ 'languages' ]); + + return ( + <> + + modalController.setOpened(false) } + /> + + + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/UpdateLanguageFormModalButton/ui/UpdateLanguageFormModalButton.tsx b/src/features/language/button/UpdateLanguageFormModalButton/ui/UpdateLanguageFormModalButton.tsx new file mode 100644 index 00000000..31f93566 --- /dev/null +++ b/src/features/language/button/UpdateLanguageFormModalButton/ui/UpdateLanguageFormModalButton.tsx @@ -0,0 +1,49 @@ +import { FC, memo } from 'react'; +import { + Button, + ButtonProps, +} from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { + useModalController, +} from '@/shared/ui-kit/modal/Modal/hooks/useModalController.ts'; +import { Modal } from '@/shared/ui-kit/modal/Modal/ui/Modal.tsx'; +import { + UpdateLanguageForm, +} from '@/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.tsx'; +import { DomainLanguage } from 'product-types/dist/language/DomainLanguage'; +import { IoSettings } from 'react-icons/io5'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type UpdateLanguageFormModalButtonProps = + { + language: DomainLanguage; + } + & ButtonProps; + +export const UpdateLanguageFormModalButton: FC = memo(function UpdateLanguageFormModalButton (props) { + const { language, ...other } = props; + const modalController = useModalController(); + const { t } = useTranslation([ 'languages' ]); + + return ( + <> + + modalController.setOpened(false) } + /> + + + + + + ); +}); \ No newline at end of file diff --git a/src/features/language/button/UpdateLanguageWordFormModalButton/ui/UpdateLanguageWordFormModalButton.tsx b/src/features/language/button/UpdateLanguageWordFormModalButton/ui/UpdateLanguageWordFormModalButton.tsx new file mode 100644 index 00000000..a80eee37 --- /dev/null +++ b/src/features/language/button/UpdateLanguageWordFormModalButton/ui/UpdateLanguageWordFormModalButton.tsx @@ -0,0 +1,51 @@ +import { FC, memo } from 'react'; +import { + DomainLanguageWord, +} from 'product-types/dist/language/DomainLanguageWord'; +import { + Button, + ButtonProps, +} from '@/shared/ui-kit/buttons/Button/ui/Button.tsx'; +import { + useModalController, +} from '@/shared/ui-kit/modal/Modal/hooks/useModalController.ts'; +import { Modal } from '@/shared/ui-kit/modal/Modal/ui/Modal.tsx'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { IoSettings } from 'react-icons/io5'; +import { + UpdateLanguageWordForm, +} from '@/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type UpdateLanguageWordFormModalButtonProps = + { + word: DomainLanguageWord; + } + & ButtonProps; + +export const UpdateLanguageWordFormModalButton: FC = memo(function UpdateLanguageWordFormModalButton (props) { + const { word, ...other } = props; + const modalController = useModalController(); + const { t } = useTranslation([ 'languages' ]); + + return ( + <> + + modalController.setOpened(false) } + word={ word } + /> + + + + + + ); +}); \ No newline at end of file diff --git a/src/shared/ui-kit/box/Row/ui/Row.module.scss b/src/shared/ui-kit/box/Row/ui/Row.module.scss index 38942547..fc6a35b6 100644 --- a/src/shared/ui-kit/box/Row/ui/Row.module.scss +++ b/src/shared/ui-kit/box/Row/ui/Row.module.scss @@ -2,4 +2,12 @@ display : flex; gap : var(--offset-small); align-items : center; + + .spaceBetween { + justify-content : space-between; + } + + .fullWidth { + width : 100%; + } } \ No newline at end of file diff --git a/src/shared/ui-kit/box/Row/ui/Row.tsx b/src/shared/ui-kit/box/Row/ui/Row.tsx index 2413fa01..869fe15a 100644 --- a/src/shared/ui-kit/box/Row/ui/Row.tsx +++ b/src/shared/ui-kit/box/Row/ui/Row.tsx @@ -4,16 +4,27 @@ import css from './Row.module.scss'; export type RowProps = - {} + { + spaceBetween?: boolean; + fullWidth?: boolean; + } & ComponentPropsWithoutRef<'div'>; export const Row: FC = memo(function Row (props) { - const { className, ...other } = props; + const { + spaceBetween = false, + fullWidth = false, + className, + ...other + } = props; return (
); }); \ No newline at end of file diff --git a/src/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx b/src/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx index 79eeefc7..ff07cf4e 100644 --- a/src/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx +++ b/src/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx @@ -52,8 +52,8 @@ export const ButtonWithLoading: FC = memo(function Butto onClick={ onClickHandler } quad={ quad } > - { pending ? : null } { (quad && pending) ? null : children } + { pending ? : null } ); }); \ No newline at end of file diff --git a/src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.module.scss b/src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.module.scss new file mode 100644 index 00000000..97084bde --- /dev/null +++ b/src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.module.scss @@ -0,0 +1,16 @@ +.title { + display : flex; + justify-content : space-between; + align-items : center; + position : relative; + + .marker { + transition : var(--transition-fast); + } + + &.opened { + .marker { + transform : rotate(90deg); + } + } +} \ No newline at end of file diff --git a/src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.tsx b/src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.tsx new file mode 100644 index 00000000..119cf738 --- /dev/null +++ b/src/shared/ui-kit/details/ControlDetails/ui/ControlDetails.tsx @@ -0,0 +1,57 @@ +import { + FC, + memo, ReactNode, + useLayoutEffect, + useRef, + useState, +} from 'react'; +import { + Details, + DetailsProps, +} from '@/shared/ui-kit/details/Details/ui/Details.tsx'; +import { + DetailsTitle, +} from '@/shared/ui-kit/details/Details/ui/DetailsTitle/DetailsTitle.tsx'; +import { + DetailsBody, +} from '@/shared/ui-kit/details/Details/ui/DetailsBody/DetailsBody.tsx'; +import classNames from 'classnames'; +import css + from '@/widgets/language/items/LanguageItem/ui/LanguageItem.module.scss'; + + +export type ControlDetailsProps = + { + titleChildren: ReactNode; + } + & DetailsProps; + +export const ControlDetails: FC = memo(function ControlDetails (props) { + const { titleChildren, children, open, ...other } = props; + const detailsRef = useRef(null); + const [ opened, setOpened ] = useState(open); + + useLayoutEffect(() => { + if (detailsRef.current) { + detailsRef.current.addEventListener('toggle', () => { + setOpened(detailsRef.current.open); + }); + } + }, []); + + + return ( +
+ + { titleChildren } + + { children } +
+ ); +}); \ No newline at end of file diff --git a/src/shared/ui-kit/details/Details/ui/Details.tsx b/src/shared/ui-kit/details/Details/ui/Details.tsx index e6e9f32a..df496b3e 100644 --- a/src/shared/ui-kit/details/Details/ui/Details.tsx +++ b/src/shared/ui-kit/details/Details/ui/Details.tsx @@ -1,19 +1,24 @@ -import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import { + ComponentPropsWithRef, + FC, forwardRef, + memo, +} from 'react'; import classNames from 'classnames'; import css from './Details.module.scss'; export type DetailsProps = {} - & ComponentPropsWithoutRef<'details'>; + & ComponentPropsWithRef<'details'>; -export const Details: FC = memo(function Details (props) { +export const Details: FC = memo(forwardRef(function Details (props, ref) { const { className, ...other } = props; return (
); -}); \ No newline at end of file +})); \ No newline at end of file diff --git a/src/shared/ui-kit/details/Details/ui/DetailsBody/DetailsBody.tsx b/src/shared/ui-kit/details/Details/ui/DetailsBody/DetailsBody.tsx index a5074642..8376d2db 100644 --- a/src/shared/ui-kit/details/Details/ui/DetailsBody/DetailsBody.tsx +++ b/src/shared/ui-kit/details/Details/ui/DetailsBody/DetailsBody.tsx @@ -5,13 +5,13 @@ import css from './DetailsBody.module.scss'; export type DetailsBodyProps = {} - & ComponentPropsWithoutRef<'summary'>; + & ComponentPropsWithoutRef<'section'>; export const DetailsBody: FC = memo(function DetailsBody (props) { const { className, ...other } = props; return ( -
diff --git a/src/shared/ui-kit/forms/Form/hooks/useForm.ts b/src/shared/ui-kit/forms/Form/hooks/useForm.ts index d79d89df..6468c111 100644 --- a/src/shared/ui-kit/forms/Form/hooks/useForm.ts +++ b/src/shared/ui-kit/forms/Form/hooks/useForm.ts @@ -1,6 +1,12 @@ -import { FormEvent, FormEventHandler, useCallback, useMemo, useState } from 'react'; import { - IUseInputWithError + FormEvent, + FormEventHandler, + useCallback, + useMemo, + useState, +} from 'react'; +import { + IUseInputWithError, } from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; @@ -23,7 +29,9 @@ export const useForm = function (props: UseFormProps< const [ pending, setPending ] = useState(false); const [ error, setError ] = useState(''); const canBeSubmitted = useMemo(() => { - return props.inputs.every((input) => input.isValid && !input.validationAwait); + return props.inputs.every((input) => { + return input.isValid && !input.validationAwait; + }); }, [ props.inputs ]); const onSubmitHandler = useCallback>((event: FormEvent) => { diff --git a/src/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts b/src/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts index 69053198..09d22994 100644 --- a/src/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts +++ b/src/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts @@ -1,7 +1,7 @@ import { ChangeEvent, ChangeEventHandler, MutableRefObject, - useCallback, + useCallback, useLayoutEffect, useMemo, useRef, useState, @@ -31,9 +31,7 @@ export const useInputWithError = function (props: UseInputWithErrorProps): IUseI const [ validationAwait, setValidationAwait ] = useState(false); // Сообщение об ошибке - const [ errorMessage, setErrorMessage ] = useState( - props.validationMethod ? props.validationMethod('') : '', - ); + const [ errorMessage, setErrorMessage ] = useState(''); // Состояние валидности (сокращение для errorMessage.length === 0) const isValid = useMemo(() => errorMessage.length === 0, [ errorMessage ]); @@ -53,8 +51,8 @@ export const useInputWithError = function (props: UseInputWithErrorProps): IUseI clearTimeout(debounceTimer.current); if (props.validationMethod) { - setValidationAwait(true); if (props.debounce) { + setValidationAwait(true); setErrorMessage(''); debounceTimer.current = setTimeout(() => { const validationResult: string = props.validationMethod!(value.current); @@ -70,7 +68,6 @@ export const useInputWithError = function (props: UseInputWithErrorProps): IUseI } else { const validationResult: string = props.validationMethod!(value.current); setErrorMessage(validationResult); - setValidationAwait(false); if (validationResult === '') { resolve(value.current); @@ -97,6 +94,17 @@ export const useInputWithError = function (props: UseInputWithErrorProps): IUseI validateCurrentValue().then(props.onChangeHandler); }, [ props.onChangeHandler, validateCurrentValue ]); + useLayoutEffect(() => { + if (inputRef.current) { + value.current = inputRef.current.value; + + if (props.validationMethod) { + setErrorMessage(props.validationMethod(inputRef.current.value)); + } + } + // eslint-disable-next-line + }, [ props.validationMethod ]); + return { value, isValid, diff --git a/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.module.scss b/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.module.scss index 05cc189f..2b1e4514 100644 --- a/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.module.scss +++ b/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.module.scss @@ -1,5 +1,19 @@ .container { - li { - padding-left : 30px; + display : flex; + flex-direction : column; + gap : var(--offset-medium); + + .content { + display : flex; + align-items : start; + gap : var(--offset-medium); + + .folders { + width : 50%; + } + + .words { + width : 50%; + } } } \ No newline at end of file diff --git a/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.tsx b/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.tsx index 74d1a08d..3cd88e02 100644 --- a/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.tsx +++ b/src/widgets/language/container/LanguagesContainer/ui/LanguagesContainer.tsx @@ -3,33 +3,28 @@ import classNames from 'classnames'; import css from './LanguagesContainer.module.scss'; import { useStore } from '@vanyamate/sec-react'; import { + $currentFolderId, $languageFolderWordsList, $languagesList, - createLanguageEffect, - createLanguageFolderEffect, - createLanguageWordEffect, - getMyLanguageFolderWordsEffect, getMyLanguagesEffect, } from '@/app/model/languages/languages.model.ts'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; import { - InputWithError, -} from '@/shared/ui-kit/inputs/InputWithError/ui/InputWithError.tsx'; + CreateLanguageFormModalButton, +} from '@/features/language/button/CreateLanguageFormModalButton/ui/CreateLanguageFormModalButton.tsx'; +import { Col } from '@/shared/ui-kit/box/Col/ui/Col.tsx'; import { - useInputWithError, -} from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; -import { Form } from '@/shared/ui-kit/forms/Form/ui/Form.tsx'; -import { useForm } from '@/shared/ui-kit/forms/Form/hooks/useForm.ts'; + LanguageItem, +} from '@/widgets/language/items/LanguageItem/ui/LanguageItem.tsx'; import { - ButtonWithLoading, -} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; -import { Details } from '@/shared/ui-kit/details/Details/ui/Details.tsx'; + LanguageWordItem, +} from '@/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.tsx'; +import { useTranslation } from 'react-i18next'; import { - DetailsTitle, -} from '@/shared/ui-kit/details/Details/ui/DetailsTitle/DetailsTitle.tsx'; -import { - DetailsBody, -} from '@/shared/ui-kit/details/Details/ui/DetailsBody/DetailsBody.tsx'; + CreateLanguageWordForm, +} from '@/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.tsx'; +// TODO: TEmp /* eslint-disable */ export type LanguagesContainerProps = @@ -41,160 +36,58 @@ export type LanguagesContainerProps = export const LanguagesContainer: FC = memo(function LanguagesContainer (props) { const { className, userId, ...other } = props; const languages = useStore($languagesList); + const selectedFolderId = useStore($currentFolderId); const words = useStore($languageFolderWordsList); - const createLanguageInput = useInputWithError({ - name: 'title', - }); - const createLanguageForm = useForm<{ title: string }>({ - inputs : [ createLanguageInput ], - onSubmit: async (data) => createLanguageEffect(data).then(), - }); - - const createLanguageFolderInput = useInputWithError({ - name: 'title', - }); - const createLanguageIdFolderInput = useInputWithError({ - name: 'id', - }); - const createLanguageFolderForm = useForm<{ title: string, id: string }>({ - inputs : [ createLanguageFolderInput, createLanguageIdFolderInput ], - onSubmit: async (data) => createLanguageFolderEffect(data.id, { title: data.title }).then(), - }); - - const createLanguageWordInput = useInputWithError({ - name: 'original', - }); - const createLanguageWordTranlation1Input = useInputWithError({ - name: 'translation1', - }); - const createLanguageWordTranlation2Input = useInputWithError({ - name: 'translation2', - }); - const createLanguageWordNoticeInput = useInputWithError({ - name: 'notice', - }); - const createLanguageWordFormIdInput = useInputWithError({ - name: 'id', - }); - const createLanguageWordForm = useForm<{ - original: string, - translation1: string, - translation2: string, - notice: string, - id: string - }>({ - inputs : [ - createLanguageWordInput, - createLanguageWordTranlation1Input, - createLanguageWordTranlation2Input, - createLanguageWordNoticeInput, - createLanguageWordFormIdInput, - ], - onSubmit: async (data) => createLanguageWordEffect(data.id, { - notice : data.notice, - translations: [ data.translation1, data.translation2 ], - original : data.original, - }).then(), - }); + const { t } = useTranslation([ 'languages' ]); useLayoutEffect(() => { + // Tempo getMyLanguagesEffect(); }, []); return ( -
-
-

create language

-
- - - Создать - - -
-
-

create folder

-
- - - - Создать - - -
-
-

create word

-
- - - - - - - Создать - - -
-

languages

-
    - { - languages.map((language) => ( -
  • -

    { language.title }

    -

    { language.id }

    -
      - { language.folders.map((folder) => ( -
    • getMyLanguageFolderWordsEffect(folder.id) }> -

      { folder.title }

      -

      { folder.id }

      -
    • - )) } -
    -
  • - )) - } -
-

words

-
    - { - words.map((word) => ( -
  • -

    { word.original }

    -

    { word.notice }

    -
    - Перевод - - { - word.translations.map((t, index) => ( -
    { t }
    )) - } -
    -
    -
  • - )) - } -
+
+ +

{ t('language_page_title') }

+ +
+ + + { + languages.map((language) => ( + + )) + } + + + { + selectedFolderId === '' + ? t('no_folder_selected') + : ( + <> + + { + words.map((word) => ( + + )) + } + + ) + } + +
); }); \ No newline at end of file diff --git a/src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss b/src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss new file mode 100644 index 00000000..5bdd7c2d --- /dev/null +++ b/src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss @@ -0,0 +1,5 @@ +.container { + display : flex; + flex-direction : column; + gap : var(--offset-medium); +} \ No newline at end of file diff --git a/src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.tsx b/src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.tsx new file mode 100644 index 00000000..562232b1 --- /dev/null +++ b/src/widgets/language/form/CreateLanguageFolderForm/ui/CreateLanguageFolderForm.tsx @@ -0,0 +1,79 @@ +import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import classNames from 'classnames'; +import css from './CreateLanguageFolderForm.module.scss'; +import { + useInputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; +import { useForm } from '@/shared/ui-kit/forms/Form/hooks/useForm.ts'; +import { + createLanguageFolderEffect, +} from '@/app/model/languages/languages.model.ts'; +import { Form } from '@/shared/ui-kit/forms/Form/ui/Form.tsx'; +import { + InputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/ui/InputWithError.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { IoCreate } from 'react-icons/io5'; +import { lengthValidator } from '@/app/validation/string/length.validator.ts'; +import { useTranslation } from 'react-i18next'; + + +export type CreateLanguageFolderFormProps = + { + languageId: string; + onSubmitHandler?: () => void; + onErrorHandler?: () => void; + onFinallyHandler?: () => void; + } + & ComponentPropsWithoutRef<'form'>; + +export const CreateLanguageFolderForm: FC = memo(function CreateLanguageFolderForm (props) { + const { + languageId, + className, + onSubmitHandler, + onErrorHandler, + onFinallyHandler, + ...other + } = props; + const titleInputController = useInputWithError({ + name : 'title', + validationMethod: lengthValidator(1, Infinity), + }); + const formController = useForm<{ title: string }>({ + inputs : [ titleInputController ], + onSubmit: async (data) => createLanguageFolderEffect(languageId, data) + .then(onSubmitHandler) + .catch(onErrorHandler) + .finally(onFinallyHandler), + }); + const { t } = useTranslation([ 'languages' ]); + + return ( +
+ + + + + { t('add_item') } + + + + ); +}); \ No newline at end of file diff --git a/src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.module.scss b/src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.module.scss new file mode 100644 index 00000000..5bdd7c2d --- /dev/null +++ b/src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.module.scss @@ -0,0 +1,5 @@ +.container { + display : flex; + flex-direction : column; + gap : var(--offset-medium); +} \ No newline at end of file diff --git a/src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.tsx b/src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.tsx new file mode 100644 index 00000000..c4bb1a83 --- /dev/null +++ b/src/widgets/language/form/CreateLanguageForm/ui/CreateLanguageForm.tsx @@ -0,0 +1,75 @@ +import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import classNames from 'classnames'; +import css from './CreateLanguageForm.module.scss'; +import { + useInputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; +import { useForm } from '@/shared/ui-kit/forms/Form/hooks/useForm.ts'; +import { createLanguageEffect } from '@/app/model/languages/languages.model.ts'; +import { Form } from '@/shared/ui-kit/forms/Form/ui/Form.tsx'; +import { + InputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/ui/InputWithError.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { IoCreate } from 'react-icons/io5'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { lengthValidator } from '@/app/validation/string/length.validator.ts'; +import { useTranslation } from 'react-i18next'; + + +export type CreateLanguageFormProps = + { + onSubmitHandler?: () => void; + onErrorHandler?: () => void; + onFinallyHandler?: () => void; + } + & ComponentPropsWithoutRef<'form'>; + +export const CreateLanguageForm: FC = memo(function CreateLanguageForm (props) { + const { + className, + onSubmitHandler, + onErrorHandler, + onFinallyHandler, + ...other + } = props; + const titleInputController = useInputWithError({ + name : 'title', + validationMethod: lengthValidator(1, Infinity), + }); + const formController = useForm<{ title: string }>({ + inputs : [ titleInputController ], + onSubmit: async (data) => createLanguageEffect(data) + .then(onSubmitHandler) + .catch(onErrorHandler) + .finally(onFinallyHandler), + }); + const { t } = useTranslation([ 'languages' ]); + + return ( +
+ + + + + { t('add_item') } + + + + ); +}); \ No newline at end of file diff --git a/src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.module.scss b/src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.module.scss new file mode 100644 index 00000000..5bdd7c2d --- /dev/null +++ b/src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.module.scss @@ -0,0 +1,5 @@ +.container { + display : flex; + flex-direction : column; + gap : var(--offset-medium); +} \ No newline at end of file diff --git a/src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.tsx b/src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.tsx new file mode 100644 index 00000000..ab5f14c4 --- /dev/null +++ b/src/widgets/language/form/CreateLanguageWordForm/ui/CreateLanguageWordForm.tsx @@ -0,0 +1,96 @@ +import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import classNames from 'classnames'; +import css from './CreateLanguageWordForm.module.scss'; +import { + useInputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; +import { useForm } from '@/shared/ui-kit/forms/Form/hooks/useForm.ts'; +import { + createLanguageWordEffect, +} from '@/app/model/languages/languages.model.ts'; +import { Form } from '@/shared/ui-kit/forms/Form/ui/Form.tsx'; +import { + InputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/ui/InputWithError.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { IoCreate } from 'react-icons/io5'; +import { lengthValidator } from '@/app/validation/string/length.validator.ts'; +import { useTranslation } from 'react-i18next'; + + +export type CreateLanguageWordFormProps = + { + folderId: string; + onSubmitHandler?: () => void; + onErrorHandler?: () => void; + onFinallyHandler?: () => void; + } + & ComponentPropsWithoutRef<'form'>; + +export const CreateLanguageWordForm: FC = memo(function CreateLanguageWordForm (props) { + const { + folderId, + className, + onSubmitHandler, + onErrorHandler, + onFinallyHandler, + ...other + } = props; + const originalInputController = useInputWithError({ + name : 'original', + validationMethod: lengthValidator(1, Infinity), + }); + const translationsInputController = useInputWithError({ + name : 'translations', + validationMethod: lengthValidator(1, Infinity), + }); + const noticeInputController = useInputWithError({ + name: 'notice', + }); + const formController = useForm<{ + original: string, + translations: string, + notice: string + }>({ + inputs : [ originalInputController, translationsInputController, noticeInputController ], + onSubmit: async (data) => createLanguageWordEffect(folderId, { + original : data.original, + notice : data.notice, + translations: data.translations.split(','), + }) + .then(onSubmitHandler) + .catch(onErrorHandler) + .finally(onFinallyHandler), + }); + const { t } = useTranslation([ 'languages' ]); + + return ( +
+ + + + + + + { t('add_item') } + + + + ); +}); \ No newline at end of file diff --git a/src/widgets/language/form/UpdateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss b/src/widgets/language/form/UpdateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss new file mode 100644 index 00000000..5bdd7c2d --- /dev/null +++ b/src/widgets/language/form/UpdateLanguageFolderForm/ui/CreateLanguageFolderForm.module.scss @@ -0,0 +1,5 @@ +.container { + display : flex; + flex-direction : column; + gap : var(--offset-medium); +} \ No newline at end of file diff --git a/src/widgets/language/form/UpdateLanguageFolderForm/ui/UpdateLanguageFolderForm.tsx b/src/widgets/language/form/UpdateLanguageFolderForm/ui/UpdateLanguageFolderForm.tsx new file mode 100644 index 00000000..5d1af656 --- /dev/null +++ b/src/widgets/language/form/UpdateLanguageFolderForm/ui/UpdateLanguageFolderForm.tsx @@ -0,0 +1,83 @@ +import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import classNames from 'classnames'; +import css from './CreateLanguageFolderForm.module.scss'; +import { + useInputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; +import { useForm } from '@/shared/ui-kit/forms/Form/hooks/useForm.ts'; +import { + updateLanguageFolderEffect, +} from '@/app/model/languages/languages.model.ts'; +import { Form } from '@/shared/ui-kit/forms/Form/ui/Form.tsx'; +import { + InputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/ui/InputWithError.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { IoCreate } from 'react-icons/io5'; +import { lengthValidator } from '@/app/validation/string/length.validator.ts'; +import { + DomainLanguageFolder, +} from 'product-types/dist/language/DomainLanguageFolder'; +import { useTranslation } from 'react-i18next'; + + +export type UpdateLanguageFolderFormProps = + { + folder: DomainLanguageFolder; + onSubmitHandler?: () => void; + onErrorHandler?: () => void; + onFinallyHandler?: () => void; + } + & ComponentPropsWithoutRef<'form'>; + +export const UpdateLanguageFolderForm: FC = memo(function UpdateLanguageFolderForm (props) { + const { + folder, + className, + onSubmitHandler, + onErrorHandler, + onFinallyHandler, + ...other + } = props; + const titleInputController = useInputWithError({ + name : 'title', + validationMethod: lengthValidator(1, Infinity), + }); + const formController = useForm<{ title: string }>({ + inputs : [ titleInputController ], + onSubmit: async (data) => updateLanguageFolderEffect(folder.id, data) + .then(onSubmitHandler) + .catch(onErrorHandler) + .finally(onFinallyHandler), + }); + const { t } = useTranslation([ 'languages' ]); + + return ( +
+ + + + + {t('add_item')} + + + + ); +}); \ No newline at end of file diff --git a/src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.module.scss b/src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.module.scss new file mode 100644 index 00000000..5bdd7c2d --- /dev/null +++ b/src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.module.scss @@ -0,0 +1,5 @@ +.container { + display : flex; + flex-direction : column; + gap : var(--offset-medium); +} \ No newline at end of file diff --git a/src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.tsx b/src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.tsx new file mode 100644 index 00000000..04b78a93 --- /dev/null +++ b/src/widgets/language/form/UpdateLanguageForm/ui/UpdateLanguageForm.tsx @@ -0,0 +1,83 @@ +import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import classNames from 'classnames'; +import css from './UpdateLanguageForm.module.scss'; +import { + useInputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; +import { useForm } from '@/shared/ui-kit/forms/Form/hooks/useForm.ts'; +import { + updateLanguageEffect, +} from '@/app/model/languages/languages.model.ts'; +import { Form } from '@/shared/ui-kit/forms/Form/ui/Form.tsx'; +import { + InputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/ui/InputWithError.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { IoCreate } from 'react-icons/io5'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { lengthValidator } from '@/app/validation/string/length.validator.ts'; +import { DomainLanguage } from 'product-types/dist/language/DomainLanguage'; +import { useTranslation } from 'react-i18next'; + + +export type UpdateLanguageFormProps = + { + language: DomainLanguage; + onSubmitHandler?: () => void; + onErrorHandler?: () => void; + onFinallyHandler?: () => void; + } + & ComponentPropsWithoutRef<'form'>; + +export const UpdateLanguageForm: FC = memo(function UpdateLanguageForm (props) { + const { + language, + className, + onSubmitHandler, + onErrorHandler, + onFinallyHandler, + ...other + } = props; + const titleInputController = useInputWithError({ + name : 'title', + validationMethod: lengthValidator(1, Infinity), + }); + const formController = useForm<{ + title: string + }>({ + inputs : [ titleInputController ], + onSubmit: async (data) => updateLanguageEffect(language.id, data) + .then(onSubmitHandler) + .catch(onErrorHandler) + .finally(onFinallyHandler), + }); + const { t } = useTranslation([ 'languages' ]); + + return ( +
+ + + + + { t('add_item') } + + + + ); +}); \ No newline at end of file diff --git a/src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.module.scss b/src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.module.scss new file mode 100644 index 00000000..5bdd7c2d --- /dev/null +++ b/src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.module.scss @@ -0,0 +1,5 @@ +.container { + display : flex; + flex-direction : column; + gap : var(--offset-medium); +} \ No newline at end of file diff --git a/src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.tsx b/src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.tsx new file mode 100644 index 00000000..e2bf26e9 --- /dev/null +++ b/src/widgets/language/form/UpdateLanguageWordForm/ui/UpdateLanguageWordForm.tsx @@ -0,0 +1,105 @@ +import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import classNames from 'classnames'; +import css from './UpdateLanguageWordForm.module.scss'; +import { + useInputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/hooks/useInputWithError.ts'; +import { useForm } from '@/shared/ui-kit/forms/Form/hooks/useForm.ts'; +import { + updateLanguageWordEffect, +} from '@/app/model/languages/languages.model.ts'; +import { Form } from '@/shared/ui-kit/forms/Form/ui/Form.tsx'; +import { + InputWithError, +} from '@/shared/ui-kit/inputs/InputWithError/ui/InputWithError.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { IoCreate } from 'react-icons/io5'; +import { lengthValidator } from '@/app/validation/string/length.validator.ts'; +import { + DomainLanguageWord, +} from 'product-types/dist/language/DomainLanguageWord'; +import { useTranslation } from 'react-i18next'; + + +export type UpdateLanguageWordFormProps = + { + word: DomainLanguageWord; + onSubmitHandler?: () => void; + onErrorHandler?: () => void; + onFinallyHandler?: () => void; + } + & ComponentPropsWithoutRef<'form'>; + +export const UpdateLanguageWordForm: FC = memo(function UpdateLanguageWordForm (props) { + const { + word, + className, + onSubmitHandler, + onErrorHandler, + onFinallyHandler, + ...other + } = props; + const originalInputController = useInputWithError({ + name : 'original', + validationMethod: lengthValidator(1, Infinity), + }); + const translationsInputController = useInputWithError({ + name : 'translations', + validationMethod: lengthValidator(1, Infinity), + }); + const noticeInputController = useInputWithError({ + name: 'notice', + }); + const formController = useForm<{ + original: string, + translations: string, + notice: string + }>({ + inputs : [ originalInputController, translationsInputController, noticeInputController ], + onSubmit: async (data) => updateLanguageWordEffect(word.id, { + original : data.original, + notice : data.notice, + translations: data.translations.split(','), + }) + .then(onSubmitHandler) + .catch(onErrorHandler) + .finally(onFinallyHandler), + }); + const { t } = useTranslation([ 'languages' ]); + + return ( +
+ + + + + + + { t('add_item') } + + + + ); +}); \ No newline at end of file diff --git a/src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.module.scss b/src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.module.scss new file mode 100644 index 00000000..c518250e --- /dev/null +++ b/src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.module.scss @@ -0,0 +1,7 @@ +.container { + display : flex; + flex-direction : row; + justify-content : space-between; + gap : var(--offset-medium); + align-items : center; +} \ No newline at end of file diff --git a/src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.tsx b/src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.tsx new file mode 100644 index 00000000..bce4a7c0 --- /dev/null +++ b/src/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.tsx @@ -0,0 +1,58 @@ +import { ComponentPropsWithoutRef, FC, memo } from 'react'; +import classNames from 'classnames'; +import css from './LanguageFolderItem.module.scss'; +import { + DomainLanguageFolder, +} from 'product-types/dist/language/DomainLanguageFolder'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { + CreateLanguageWordFormModalButton, +} from '@/features/language/button/CreateLanguageWordFormModalButton/ui/CreateLanguageWordFormModalButton.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { + $currentFolderId, + getMyLanguageFolderWordsEffect, +} from '@/app/model/languages/languages.model.ts'; +import { + UpdateLanguageFolderFormModalButton, +} from '@/features/language/button/UpdateLanguageFolderFormModalButton/ui/UpdateLanguageFolderFormModalButton.tsx'; +import { + RemoveLanguageFolderButton, +} from '@/features/language/button/RemoveLanguageFolderButton/ui/RemoveLanguageFolderButton.tsx'; +import { useStore } from '@vanyamate/sec-react'; + + +export type LanguageFolderItemProps = + { + folder: DomainLanguageFolder; + } + & ComponentPropsWithoutRef<'div'>; + +export const LanguageFolderItem: FC = memo(function LanguageFolderItem (props) { + const { folder, className, ...other } = props; + const selectedFolderId = useStore($currentFolderId); + + return ( +
+ + getMyLanguageFolderWordsEffect(folder.id) } + styleType={ selectedFolderId === folder.id + ? ButtonStyleType.PRIMARY + : ButtonStyleType.GHOST } + > + { folder.title } + + + + + + + +
+ ); +}); \ No newline at end of file diff --git a/src/widgets/language/items/LanguageItem/ui/LanguageItem.module.scss b/src/widgets/language/items/LanguageItem/ui/LanguageItem.module.scss new file mode 100644 index 00000000..f25ad554 --- /dev/null +++ b/src/widgets/language/items/LanguageItem/ui/LanguageItem.module.scss @@ -0,0 +1,13 @@ +.title { + display : flex; + + .marker { + transition : var(--transition-fast); + } + + &.opened { + .marker { + transform : rotate(90deg); + } + } +} \ No newline at end of file diff --git a/src/widgets/language/items/LanguageItem/ui/LanguageItem.tsx b/src/widgets/language/items/LanguageItem/ui/LanguageItem.tsx new file mode 100644 index 00000000..1aab21ed --- /dev/null +++ b/src/widgets/language/items/LanguageItem/ui/LanguageItem.tsx @@ -0,0 +1,71 @@ +import { FC, memo } from 'react'; +import css from './LanguageItem.module.scss'; +import { + DomainLanguageWithFolders, +} from 'product-types/dist/language/DomainLanguageWithFolders'; +import { + DetailsProps, +} from '@/shared/ui-kit/details/Details/ui/Details.tsx'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { + RemoveLanguageButton, +} from '@/features/language/button/RemoveLanguageButton/ui/RemoveLanguageButton.tsx'; +import { + UpdateLanguageFormModalButton, +} from '@/features/language/button/UpdateLanguageFormModalButton/ui/UpdateLanguageFormModalButton.tsx'; +import { IoChevronForward } from 'react-icons/io5'; +import { + CreateLanguageFolderFormModalButton, +} from '@/features/language/button/CreateLanguageFolderFormModalButton/ui/CreateLanguageFolderFormModalButton.tsx'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { + LanguageFolderItem, +} from '@/widgets/language/items/LanguageFolderItem/ui/LanguageFolderItem.tsx'; +import { Col } from '@/shared/ui-kit/box/Col/ui/Col.tsx'; +import { + ControlDetails, +} from '@/shared/ui-kit/details/ControlDetails/ui/ControlDetails.tsx'; + + +export type LanguageItemProps = + { + language: DomainLanguageWithFolders; + } + & DetailsProps; + +export const LanguageItem: FC = memo(function LanguageItem (props) { + const { language, ...other } = props; + + return ( + + + +

{ language.title }

+ +
+ + + + + + } + > + + { + language.folders.map((folder) => ( + + )) + } + +
+ ); +}); \ No newline at end of file diff --git a/src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.module.scss b/src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.module.scss new file mode 100644 index 00000000..4df8039c --- /dev/null +++ b/src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.module.scss @@ -0,0 +1,11 @@ +.container { + .words { + .notice { + color : var(--text-second-color); + } + + .list { + padding-left : var(--offset-large); + } + } +} \ No newline at end of file diff --git a/src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.tsx b/src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.tsx new file mode 100644 index 00000000..d0ed2be9 --- /dev/null +++ b/src/widgets/language/items/LanguageWordItem/ui/LanguageWordItem.tsx @@ -0,0 +1,87 @@ +import { FC, memo } from 'react'; +import { DetailsProps } from '@/shared/ui-kit/details/Details/ui/Details.tsx'; +import { + DomainLanguageWord, +} from 'product-types/dist/language/DomainLanguageWord'; +import { + ControlDetails, +} from '@/shared/ui-kit/details/ControlDetails/ui/ControlDetails.tsx'; +import { Row } from '@/shared/ui-kit/box/Row/ui/Row.tsx'; +import { + ButtonWithLoading, +} from '@/shared/ui-kit/buttons/ButtonWithLoading/ui/ButtonWithLoading.tsx'; +import { IoCheckmark, IoClose } from 'react-icons/io5'; +import { ButtonStyleType } from '@/shared/ui-kit/buttons/Button/types/types.ts'; +import { Col } from '@/shared/ui-kit/box/Col/ui/Col.tsx'; +import { + UpdateLanguageWordFormModalButton, +} from '@/features/language/button/UpdateLanguageWordFormModalButton/ui/UpdateLanguageWordFormModalButton.tsx'; +import { + RemoveLanguageWordButton, +} from '@/features/language/button/RemoveLanguageWordButton/ui/RemoveLanguageWordButton.tsx'; +import classNames from 'classnames'; +import css from './LanguageWordItem.module.scss'; +import { + updateLanguageWordEffect, +} from '@/app/model/languages/languages.model.ts'; +import { PopOver } from '@/shared/ui-kit/modal/PopOver/ui/PopOver.tsx'; +import { useTranslation } from 'react-i18next'; + + +export type LanguageWordItemProps = + { + word: DomainLanguageWord; + } + & DetailsProps; + +export const LanguageWordItem: FC = memo(function LanguageWordItem (props) { + const { word, className, ...other } = props; + const { t } = useTranslation([ 'languages' ]); + + return ( + + + + updateLanguageWordEffect(word.id, { checked: !word.checked }) } + quad + styleType={ word.checked + ? ButtonStyleType.SECOND + : ButtonStyleType.GHOST } + > + { + word.checked ? : + } + + +

{ word.original }

+
+ + + + + + } + > + + { + word.notice ?

{ word.notice }

: null + } +
    + { + word.translations.map((translation, index) => ( +
  • { translation }
  • + )) + } +
+ +
+ ); +}); \ No newline at end of file