fix bus and translate to eus
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-12-31 10:31:49 +03:00
parent fdda45da27
commit a81b1a8579
8 changed files with 72 additions and 69 deletions

View File

@ -8,7 +8,7 @@
**Этап:** Фаза 1 (Frontend) завершена **Этап:** Фаза 1 (Frontend) завершена
**Фаза MVP:** Готов к тестированию базового функционала **Фаза MVP:** Готов к тестированию базового функционала
**Последнее обновление:** 2025-12-29 **Последнее обновление:** 2025-12-31
--- ---
@ -33,6 +33,8 @@
| 2025-12-29 | **Фаза 1:** Frontend — Модалка создания идеи | | 2025-12-29 | **Фаза 1:** Frontend — Модалка создания идеи |
| 2025-12-29 | **Фаза 1:** Frontend — Skeleton loader и empty state | | 2025-12-29 | **Фаза 1:** Frontend — Skeleton loader и empty state |
| 2025-12-29 | **Фаза 1:** Frontend — Удаление идей | | 2025-12-29 | **Фаза 1:** Frontend — Удаление идей |
| 2025-12-31 | Исправлен баг: Select в inline-редактировании закрывался при клике (MenuProps.disablePortal) |
| 2025-12-31 | Локализация интерфейса на русский язык |
--- ---

View File

@ -23,7 +23,7 @@ function App() {
Team Planner Team Planner
</Typography> </Typography>
<Typography variant="body1" color="text.secondary"> <Typography variant="body1" color="text.secondary">
Backlog management for your team Управление бэклогом идей команды
</Typography> </Typography>
</Box> </Box>
<Button <Button
@ -31,7 +31,7 @@ function App() {
startIcon={<Add />} startIcon={<Add />}
onClick={() => setCreateModalOpen(true)} onClick={() => setCreateModalOpen(true)}
> >
New Idea Новая идея
</Button> </Button>
</Box> </Box>

View File

@ -18,17 +18,17 @@ import { useCreateIdea } from '../../hooks/useIdeas';
import type { CreateIdeaDto, IdeaStatus, IdeaPriority } from '../../types/idea'; import type { CreateIdeaDto, IdeaStatus, IdeaPriority } from '../../types/idea';
const statusOptions: { value: IdeaStatus; label: string }[] = [ const statusOptions: { value: IdeaStatus; label: string }[] = [
{ value: 'backlog', label: 'Backlog' }, { value: 'backlog', label: 'Бэклог' },
{ value: 'todo', label: 'To Do' }, { value: 'todo', label: 'К выполнению' },
{ value: 'in_progress', label: 'In Progress' }, { value: 'in_progress', label: 'В работе' },
{ value: 'done', label: 'Done' }, { value: 'done', label: 'Готово' },
]; ];
const priorityOptions: { value: IdeaPriority; label: string }[] = [ const priorityOptions: { value: IdeaPriority; label: string }[] = [
{ value: 'low', label: 'Low' }, { value: 'low', label: 'Низкий' },
{ value: 'medium', label: 'Medium' }, { value: 'medium', label: 'Средний' },
{ value: 'high', label: 'High' }, { value: 'high', label: 'Высокий' },
{ value: 'critical', label: 'Critical' }, { value: 'critical', label: 'Критичный' },
]; ];
const initialFormData: CreateIdeaDto = { const initialFormData: CreateIdeaDto = {
@ -76,17 +76,17 @@ export function CreateIdeaModal() {
fullWidth fullWidth
> >
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<DialogTitle>Create New Idea</DialogTitle> <DialogTitle>Новая идея</DialogTitle>
<DialogContent> <DialogContent>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mt: 1 }}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mt: 1 }}>
{createIdea.isError && ( {createIdea.isError && (
<Alert severity="error"> <Alert severity="error">
Failed to create idea. Please try again. Не удалось создать идею. Попробуйте снова.
</Alert> </Alert>
)} )}
<TextField <TextField
label="Title" label="Название"
value={formData.title} value={formData.title}
onChange={(e) => handleChange('title', e.target.value)} onChange={(e) => handleChange('title', e.target.value)}
required required
@ -94,7 +94,7 @@ export function CreateIdeaModal() {
/> />
<TextField <TextField
label="Description" label="Описание"
value={formData.description} value={formData.description}
onChange={(e) => handleChange('description', e.target.value)} onChange={(e) => handleChange('description', e.target.value)}
multiline multiline
@ -103,10 +103,10 @@ export function CreateIdeaModal() {
<Box sx={{ display: 'flex', gap: 2 }}> <Box sx={{ display: 'flex', gap: 2 }}>
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel>Status</InputLabel> <InputLabel>Статус</InputLabel>
<Select <Select
value={formData.status} value={formData.status}
label="Status" label="Статус"
onChange={(e) => handleChange('status', e.target.value)} onChange={(e) => handleChange('status', e.target.value)}
> >
{statusOptions.map((opt) => ( {statusOptions.map((opt) => (
@ -118,10 +118,10 @@ export function CreateIdeaModal() {
</FormControl> </FormControl>
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel>Priority</InputLabel> <InputLabel>Приоритет</InputLabel>
<Select <Select
value={formData.priority} value={formData.priority}
label="Priority" label="Приоритет"
onChange={(e) => handleChange('priority', e.target.value)} onChange={(e) => handleChange('priority', e.target.value)}
> >
{priorityOptions.map((opt) => ( {priorityOptions.map((opt) => (
@ -134,57 +134,57 @@ export function CreateIdeaModal() {
</Box> </Box>
<TextField <TextField
label="Module" label="Модуль"
value={formData.module} value={formData.module}
onChange={(e) => handleChange('module', e.target.value)} onChange={(e) => handleChange('module', e.target.value)}
placeholder="e.g., Auth, Dashboard, API" placeholder="например: Авторизация, Дашборд, API"
/> />
<TextField <TextField
label="Target Audience" label="Целевая аудитория"
value={formData.targetAudience} value={formData.targetAudience}
onChange={(e) => handleChange('targetAudience', e.target.value)} onChange={(e) => handleChange('targetAudience', e.target.value)}
placeholder="Who is this for?" placeholder="Для кого это?"
/> />
<TextField <TextField
label="Pain Point" label="Боль"
value={formData.pain} value={formData.pain}
onChange={(e) => handleChange('pain', e.target.value)} onChange={(e) => handleChange('pain', e.target.value)}
multiline multiline
rows={2} rows={2}
placeholder="What problem does this solve?" placeholder="Какую проблему это решает?"
/> />
<TextField <TextField
label="AI Role" label="Роль AI"
value={formData.aiRole} value={formData.aiRole}
onChange={(e) => handleChange('aiRole', e.target.value)} onChange={(e) => handleChange('aiRole', e.target.value)}
multiline multiline
rows={2} rows={2}
placeholder="How can AI help with this?" placeholder="Как AI может помочь с этим?"
/> />
<TextField <TextField
label="Verification Method" label="Способ проверки"
value={formData.verificationMethod} value={formData.verificationMethod}
onChange={(e) => onChange={(e) =>
handleChange('verificationMethod', e.target.value) handleChange('verificationMethod', e.target.value)
} }
multiline multiline
rows={2} rows={2}
placeholder="How to verify this is done?" placeholder="Как проверить, что это готово?"
/> />
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={handleClose}>Cancel</Button> <Button onClick={handleClose}>Отмена</Button>
<Button <Button
type="submit" type="submit"
variant="contained" variant="contained"
disabled={!formData.title || createIdea.isPending} disabled={!formData.title || createIdea.isPending}
> >
{createIdea.isPending ? 'Creating...' : 'Create'} {createIdea.isPending ? 'Создание...' : 'Создать'}
</Button> </Button>
</DialogActions> </DialogActions>
</form> </form>

View File

@ -15,18 +15,18 @@ import { useModulesQuery } from '../../hooks/useIdeas';
import type { IdeaStatus, IdeaPriority } from '../../types/idea'; import type { IdeaStatus, IdeaPriority } from '../../types/idea';
const statusOptions: { value: IdeaStatus; label: string }[] = [ const statusOptions: { value: IdeaStatus; label: string }[] = [
{ value: 'backlog', label: 'Backlog' }, { value: 'backlog', label: 'Бэклог' },
{ value: 'todo', label: 'To Do' }, { value: 'todo', label: 'К выполнению' },
{ value: 'in_progress', label: 'In Progress' }, { value: 'in_progress', label: 'В работе' },
{ value: 'done', label: 'Done' }, { value: 'done', label: 'Готово' },
{ value: 'cancelled', label: 'Cancelled' }, { value: 'cancelled', label: 'Отменено' },
]; ];
const priorityOptions: { value: IdeaPriority; label: string }[] = [ const priorityOptions: { value: IdeaPriority; label: string }[] = [
{ value: 'low', label: 'Low' }, { value: 'low', label: 'Низкий' },
{ value: 'medium', label: 'Medium' }, { value: 'medium', label: 'Средний' },
{ value: 'high', label: 'High' }, { value: 'high', label: 'Высокий' },
{ value: 'critical', label: 'Critical' }, { value: 'critical', label: 'Критичный' },
]; ];
export function IdeasFilters() { export function IdeasFilters() {
@ -52,7 +52,7 @@ export function IdeasFilters() {
> >
<TextField <TextField
size="small" size="small"
placeholder="Search ideas..." placeholder="Поиск идей..."
value={searchValue} value={searchValue}
onChange={(e) => setSearchValue(e.target.value)} onChange={(e) => setSearchValue(e.target.value)}
sx={{ minWidth: 200 }} sx={{ minWidth: 200 }}
@ -68,16 +68,16 @@ export function IdeasFilters() {
/> />
<FormControl size="small" sx={{ minWidth: 120 }}> <FormControl size="small" sx={{ minWidth: 120 }}>
<InputLabel>Status</InputLabel> <InputLabel>Статус</InputLabel>
<Select<IdeaStatus | ''> <Select<IdeaStatus | ''>
value={filters.status ?? ''} value={filters.status ?? ''}
label="Status" label="Статус"
onChange={(e) => { onChange={(e) => {
const val = e.target.value; const val = e.target.value;
setFilter('status', val === '' ? undefined : val); setFilter('status', val === '' ? undefined : val);
}} }}
> >
<MenuItem value="">All</MenuItem> <MenuItem value="">Все</MenuItem>
{statusOptions.map((opt) => ( {statusOptions.map((opt) => (
<MenuItem key={opt.value} value={opt.value}> <MenuItem key={opt.value} value={opt.value}>
{opt.label} {opt.label}
@ -87,16 +87,16 @@ export function IdeasFilters() {
</FormControl> </FormControl>
<FormControl size="small" sx={{ minWidth: 120 }}> <FormControl size="small" sx={{ minWidth: 120 }}>
<InputLabel>Priority</InputLabel> <InputLabel>Приоритет</InputLabel>
<Select<IdeaPriority | ''> <Select<IdeaPriority | ''>
value={filters.priority ?? ''} value={filters.priority ?? ''}
label="Priority" label="Приоритет"
onChange={(e) => { onChange={(e) => {
const val = e.target.value; const val = e.target.value;
setFilter('priority', val === '' ? undefined : val); setFilter('priority', val === '' ? undefined : val);
}} }}
> >
<MenuItem value="">All</MenuItem> <MenuItem value="">Все</MenuItem>
{priorityOptions.map((opt) => ( {priorityOptions.map((opt) => (
<MenuItem key={opt.value} value={opt.value}> <MenuItem key={opt.value} value={opt.value}>
{opt.label} {opt.label}
@ -106,13 +106,13 @@ export function IdeasFilters() {
</FormControl> </FormControl>
<FormControl size="small" sx={{ minWidth: 120 }}> <FormControl size="small" sx={{ minWidth: 120 }}>
<InputLabel>Module</InputLabel> <InputLabel>Модуль</InputLabel>
<Select <Select
value={filters.module ?? ''} value={filters.module ?? ''}
label="Module" label="Модуль"
onChange={(e) => setFilter('module', e.target.value || undefined)} onChange={(e) => setFilter('module', e.target.value || undefined)}
> >
<MenuItem value="">All</MenuItem> <MenuItem value="">Все</MenuItem>
{modules.map((module) => ( {modules.map((module) => (
<MenuItem key={module} value={module}> <MenuItem key={module} value={module}>
{module} {module}
@ -130,7 +130,7 @@ export function IdeasFilters() {
setSearchValue(''); setSearchValue('');
}} }}
> >
Clear Сбросить
</Button> </Button>
)} )}
</Box> </Box>

View File

@ -82,6 +82,7 @@ export function EditableCell({
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
autoFocus autoFocus
sx={{ minWidth: 100 }} sx={{ minWidth: 100 }}
MenuProps={{ disablePortal: true }}
> >
{options.map((opt) => ( {options.map((opt) => (
<MenuItem key={opt.value} value={opt.value}> <MenuItem key={opt.value} value={opt.value}>

View File

@ -62,7 +62,7 @@ export function IdeasTable() {
if (isError) { if (isError) {
return ( return (
<Box sx={{ p: 4, textAlign: 'center' }}> <Box sx={{ p: 4, textAlign: 'center' }}>
<Typography color="error">Failed to load ideas</Typography> <Typography color="error">Не удалось загрузить идеи</Typography>
</Box> </Box>
); );
} }
@ -137,9 +137,9 @@ export function IdeasTable() {
}} }}
> >
<Inbox sx={{ fontSize: 48, mb: 2, opacity: 0.5 }} /> <Inbox sx={{ fontSize: 48, mb: 2, opacity: 0.5 }} />
<Typography variant="h6">No ideas yet</Typography> <Typography variant="h6">Идей пока нет</Typography>
<Typography variant="body2"> <Typography variant="body2">
Create your first idea to get started Создайте первую идею, чтобы начать
</Typography> </Typography>
</Box> </Box>
</TableCell> </TableCell>

View File

@ -30,7 +30,7 @@ const priorityColors: Record<
export const createColumns = (onDelete: (id: string) => void) => [ export const createColumns = (onDelete: (id: string) => void) => [
columnHelper.accessor('title', { columnHelper.accessor('title', {
header: 'Title', header: 'Название',
cell: (info) => ( cell: (info) => (
<EditableCell <EditableCell
idea={info.row.original} idea={info.row.original}
@ -44,7 +44,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
size: 250, size: 250,
}), }),
columnHelper.accessor('status', { columnHelper.accessor('status', {
header: 'Status', header: 'Статус',
cell: (info) => { cell: (info) => {
const status = info.getValue(); const status = info.getValue();
const label = const label =
@ -65,7 +65,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
size: 140, size: 140,
}), }),
columnHelper.accessor('priority', { columnHelper.accessor('priority', {
header: 'Priority', header: 'Приоритет',
cell: (info) => { cell: (info) => {
const priority = info.getValue(); const priority = info.getValue();
const label = const label =
@ -91,7 +91,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
size: 120, size: 120,
}), }),
columnHelper.accessor('module', { columnHelper.accessor('module', {
header: 'Module', header: 'Модуль',
cell: (info) => ( cell: (info) => (
<EditableCell <EditableCell
idea={info.row.original} idea={info.row.original}
@ -103,7 +103,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
size: 120, size: 120,
}), }),
columnHelper.accessor('targetAudience', { columnHelper.accessor('targetAudience', {
header: 'Target Audience', header: 'Целевая аудитория',
cell: (info) => ( cell: (info) => (
<EditableCell <EditableCell
idea={info.row.original} idea={info.row.original}
@ -115,7 +115,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
size: 150, size: 150,
}), }),
columnHelper.accessor('description', { columnHelper.accessor('description', {
header: 'Description', header: 'Описание',
cell: (info) => { cell: (info) => {
const value = info.getValue(); const value = info.getValue();
return ( return (

View File

@ -1,16 +1,16 @@
import type { IdeaStatus, IdeaPriority } from '../../types/idea'; import type { IdeaStatus, IdeaPriority } from '../../types/idea';
export const statusOptions: { value: IdeaStatus; label: string }[] = [ export const statusOptions: { value: IdeaStatus; label: string }[] = [
{ value: 'backlog', label: 'Backlog' }, { value: 'backlog', label: 'Бэклог' },
{ value: 'todo', label: 'To Do' }, { value: 'todo', label: 'К выполнению' },
{ value: 'in_progress', label: 'In Progress' }, { value: 'in_progress', label: 'В работе' },
{ value: 'done', label: 'Done' }, { value: 'done', label: 'Готово' },
{ value: 'cancelled', label: 'Cancelled' }, { value: 'cancelled', label: 'Отменено' },
]; ];
export const priorityOptions: { value: IdeaPriority; label: string }[] = [ export const priorityOptions: { value: IdeaPriority; label: string }[] = [
{ value: 'low', label: 'Low' }, { value: 'low', label: 'Низкий' },
{ value: 'medium', label: 'Medium' }, { value: 'medium', label: 'Средний' },
{ value: 'high', label: 'High' }, { value: 'high', label: 'Высокий' },
{ value: 'critical', label: 'Critical' }, { value: 'critical', label: 'Критичный' },
]; ];