Enhance localization support by integrating i18next for translations across various components and pages. Update UI elements to utilize translated strings for improved user experience in both English and Russian. Additionally, refactor the Toaster component to support a context-based approach for toast notifications.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Box,
|
||||
Heading,
|
||||
@@ -9,9 +10,7 @@ import {
|
||||
Input,
|
||||
HStack,
|
||||
Text,
|
||||
IconButton,
|
||||
Badge,
|
||||
createListCollection,
|
||||
} from '@chakra-ui/react'
|
||||
import { useGetTasksQuery, useDeleteTaskMutation } from '../../__data__/api/api'
|
||||
import { URLs } from '../../__data__/urls'
|
||||
@@ -24,6 +23,7 @@ import { toaster } from '../../components/ui/toaster'
|
||||
|
||||
export const TasksListPage: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation()
|
||||
const { data: tasks, isLoading, error, refetch } = useGetTasksQuery()
|
||||
const [deleteTask, { isLoading: isDeleting }] = useDeleteTaskMutation()
|
||||
|
||||
@@ -36,26 +36,26 @@ export const TasksListPage: React.FC = () => {
|
||||
try {
|
||||
await deleteTask(taskToDelete.id).unwrap()
|
||||
toaster.create({
|
||||
title: 'Успешно',
|
||||
description: 'Задание удалено',
|
||||
title: t('challenge.admin.common.success'),
|
||||
description: t('challenge.admin.tasks.deleted'),
|
||||
type: 'success',
|
||||
})
|
||||
setTaskToDelete(null)
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toaster.create({
|
||||
title: 'Ошибка',
|
||||
description: 'Не удалось удалить задание',
|
||||
title: t('challenge.admin.common.error'),
|
||||
description: t('challenge.admin.tasks.delete.error'),
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingSpinner message="Загрузка заданий..." />
|
||||
return <LoadingSpinner message={t('challenge.admin.tasks.list.loading')} />
|
||||
}
|
||||
|
||||
if (error || !tasks) {
|
||||
return <ErrorAlert message="Не удалось загрузить список заданий" onRetry={refetch} />
|
||||
return <ErrorAlert message={t('challenge.admin.tasks.list.load.error')} onRetry={refetch} />
|
||||
}
|
||||
|
||||
const filteredTasks = tasks.filter((task) =>
|
||||
@@ -73,16 +73,16 @@ export const TasksListPage: React.FC = () => {
|
||||
return (
|
||||
<Box>
|
||||
<Flex justify="space-between" align="center" mb={6}>
|
||||
<Heading>Задания</Heading>
|
||||
<Heading>{t('challenge.admin.tasks.list.title')}</Heading>
|
||||
<Button colorPalette="teal" onClick={() => navigate(URLs.taskNew)}>
|
||||
+ Создать задание
|
||||
{t('challenge.admin.tasks.list.create.button')}
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{tasks.length > 0 && (
|
||||
<Box mb={4}>
|
||||
<Input
|
||||
placeholder="Поиск по названию..."
|
||||
placeholder={t('challenge.admin.tasks.list.search.placeholder')}
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
maxW="400px"
|
||||
@@ -92,26 +92,26 @@ export const TasksListPage: React.FC = () => {
|
||||
|
||||
{filteredTasks.length === 0 && tasks.length === 0 ? (
|
||||
<EmptyState
|
||||
title="Нет заданий"
|
||||
description="Создайте первое задание для начала работы"
|
||||
actionLabel="Создать задание"
|
||||
title={t('challenge.admin.tasks.list.empty.title')}
|
||||
description={t('challenge.admin.tasks.list.empty.description')}
|
||||
actionLabel={t('challenge.admin.tasks.list.empty.action')}
|
||||
onAction={() => navigate(URLs.taskNew)}
|
||||
/>
|
||||
) : filteredTasks.length === 0 ? (
|
||||
<EmptyState
|
||||
title="Ничего не найдено"
|
||||
description={`По запросу "${searchQuery}" ничего не найдено`}
|
||||
title={t('challenge.admin.common.not.found')}
|
||||
description={t('challenge.admin.tasks.list.search.empty', { query: searchQuery })}
|
||||
/>
|
||||
) : (
|
||||
<Box bg="white" borderRadius="lg" boxShadow="sm" borderWidth="1px" borderColor="gray.200" overflowX="auto">
|
||||
<Table.Root size="sm">
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.ColumnHeader>Название</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>Создатель</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>Дата создания</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>Скрытые инструкции</Table.ColumnHeader>
|
||||
<Table.ColumnHeader textAlign="right">Действия</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>{t('challenge.admin.tasks.list.table.title')}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>{t('challenge.admin.tasks.list.table.creator')}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>{t('challenge.admin.tasks.list.table.created')}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>{t('challenge.admin.tasks.list.table.hidden.instructions')}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader textAlign="right">{t('challenge.admin.tasks.list.table.actions')}</Table.ColumnHeader>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
@@ -131,7 +131,7 @@ export const TasksListPage: React.FC = () => {
|
||||
<Table.Cell>
|
||||
{task.hiddenInstructions ? (
|
||||
<Badge colorPalette="purple" variant="subtle">
|
||||
🔒 Есть
|
||||
{t('challenge.admin.tasks.list.badge.has.instructions')}
|
||||
</Badge>
|
||||
) : (
|
||||
<Text fontSize="sm" color="gray.400">
|
||||
@@ -146,7 +146,7 @@ export const TasksListPage: React.FC = () => {
|
||||
variant="ghost"
|
||||
onClick={() => navigate(URLs.taskEdit(task.id))}
|
||||
>
|
||||
Редактировать
|
||||
{t('challenge.admin.tasks.list.button.edit')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -154,7 +154,7 @@ export const TasksListPage: React.FC = () => {
|
||||
colorPalette="red"
|
||||
onClick={() => setTaskToDelete(task)}
|
||||
>
|
||||
Удалить
|
||||
{t('challenge.admin.tasks.list.button.delete')}
|
||||
</Button>
|
||||
</HStack>
|
||||
</Table.Cell>
|
||||
@@ -169,9 +169,9 @@ export const TasksListPage: React.FC = () => {
|
||||
isOpen={!!taskToDelete}
|
||||
onClose={() => setTaskToDelete(null)}
|
||||
onConfirm={handleDeleteTask}
|
||||
title="Удалить задание"
|
||||
message={`Вы уверены, что хотите удалить задание "${taskToDelete?.title}"? Это действие нельзя отменить.`}
|
||||
confirmLabel="Удалить"
|
||||
title={t('challenge.admin.tasks.delete.confirm.title')}
|
||||
message={t('challenge.admin.tasks.delete.confirm.message', { title: taskToDelete?.title })}
|
||||
confirmLabel={t('challenge.admin.tasks.delete.confirm.button')}
|
||||
isLoading={isDeleting}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user