fix lint
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2026-01-15 02:36:24 +03:00
parent dea0676169
commit 2e46cc41a1
42 changed files with 940 additions and 301 deletions

View File

@ -85,7 +85,15 @@ export function ColorPickerCell({ idea }: ColorPickerCellProps) {
} as React.HTMLAttributes<HTMLDivElement>,
}}
>
<Box sx={{ p: 1, display: 'flex', gap: 0.5, flexWrap: 'wrap', maxWidth: 180 }}>
<Box
sx={{
p: 1,
display: 'flex',
gap: 0.5,
flexWrap: 'wrap',
maxWidth: 180,
}}
>
{COLORS.map((color) => (
<IconButton
key={color}

View File

@ -80,7 +80,12 @@ export function DraggableRow({ row }: DraggableRowProps) {
return (
<DragHandleContext.Provider value={{ attributes, listeners, isDragging }}>
<TableRow ref={setNodeRef} hover sx={style} data-testid={`idea-row-${row.original.id}`}>
<TableRow
ref={setNodeRef}
hover
sx={style}
data-testid={`idea-row-${row.original.id}`}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}

View File

@ -1,4 +1,4 @@
import { useMemo, useState, Fragment } from 'react';
import { useMemo, useState, Fragment, useCallback } from 'react';
import {
useReactTable,
getCoreRowModel,
@ -79,34 +79,45 @@ export function IdeasTable() {
// AI-оценка
const [estimatingId, setEstimatingId] = useState<string | null>(null);
const [estimateModalOpen, setEstimateModalOpen] = useState(false);
const [estimateResult, setEstimateResult] = useState<EstimateResult | null>(null);
const [estimateResult, setEstimateResult] = useState<EstimateResult | null>(
null,
);
// ТЗ (спецификация)
const [specificationModalOpen, setSpecificationModalOpen] = useState(false);
const [specificationIdea, setSpecificationIdea] = useState<Idea | null>(null);
const [generatedSpecification, setGeneratedSpecification] = useState<string | null>(null);
const [generatingSpecificationId, setGeneratingSpecificationId] = useState<string | null>(null);
const [generatedSpecification, setGeneratedSpecification] = useState<
string | null
>(null);
const [generatingSpecificationId, setGeneratingSpecificationId] = useState<
string | null
>(null);
// История ТЗ
const specificationHistory = useSpecificationHistory(specificationIdea?.id ?? null);
const specificationHistory = useSpecificationHistory(
specificationIdea?.id ?? null,
);
const handleToggleComments = (id: string) => {
setExpandedId((prev) => (prev === id ? null : id));
};
const handleEstimate = (id: string) => {
setEstimatingId(id);
setEstimateModalOpen(true);
setEstimateResult(null);
estimateIdea.mutate(id, {
onSuccess: (result) => {
setEstimateResult(result);
setEstimatingId(null);
},
onError: () => {
setEstimatingId(null);
},
});
};
const handleEstimate = useCallback(
(id: string) => {
setEstimatingId(id);
setEstimateModalOpen(true);
setEstimateResult(null);
estimateIdea.mutate(id, {
onSuccess: (result) => {
setEstimateResult(result);
setEstimatingId(null);
},
onError: () => {
setEstimatingId(null);
},
});
},
[estimateIdea],
);
const handleCloseEstimateModal = () => {
setEstimateModalOpen(false);
@ -121,37 +132,40 @@ export function IdeasTable() {
ideaId: idea.id,
ideaTitle: idea.title,
totalHours: idea.estimatedHours,
complexity: idea.complexity!,
complexity: idea.complexity ?? 'medium',
breakdown: idea.estimateDetails.breakdown,
recommendations: idea.estimateDetails.recommendations,
estimatedAt: idea.estimatedAt!,
estimatedAt: idea.estimatedAt ?? new Date().toISOString(),
});
setEstimateModalOpen(true);
};
const handleSpecification = (idea: Idea) => {
setSpecificationIdea(idea);
setSpecificationModalOpen(true);
const handleSpecification = useCallback(
(idea: Idea) => {
setSpecificationIdea(idea);
setSpecificationModalOpen(true);
// Если ТЗ уже есть — показываем его
if (idea.specification) {
setGeneratedSpecification(idea.specification);
return;
}
// Если ТЗ уже есть — показываем его
if (idea.specification) {
setGeneratedSpecification(idea.specification);
return;
}
// Иначе генерируем
setGeneratedSpecification(null);
setGeneratingSpecificationId(idea.id);
generateSpecification.mutate(idea.id, {
onSuccess: (result) => {
setGeneratedSpecification(result.specification);
setGeneratingSpecificationId(null);
},
onError: () => {
setGeneratingSpecificationId(null);
},
});
};
// Иначе генерируем
setGeneratedSpecification(null);
setGeneratingSpecificationId(idea.id);
generateSpecification.mutate(idea.id, {
onSuccess: (result) => {
setGeneratedSpecification(result.specification);
setGeneratingSpecificationId(null);
},
onError: () => {
setGeneratingSpecificationId(null);
},
});
},
[generateSpecification],
);
const handleCloseSpecificationModal = () => {
setSpecificationModalOpen(false);
@ -162,7 +176,7 @@ export function IdeasTable() {
const handleSaveSpecification = (specification: string) => {
if (!specificationIdea) return;
updateIdea.mutate(
{ id: specificationIdea.id, data: { specification } },
{ id: specificationIdea.id, dto: { specification } },
{
onSuccess: () => {
setGeneratedSpecification(specification);
@ -209,7 +223,14 @@ export function IdeasTable() {
estimatingId,
generatingSpecificationId,
}),
[deleteIdea, expandedId, estimatingId, generatingSpecificationId],
[
deleteIdea,
expandedId,
estimatingId,
generatingSpecificationId,
handleEstimate,
handleSpecification,
],
);
// eslint-disable-next-line react-hooks/incompatible-library
@ -291,7 +312,10 @@ export function IdeasTable() {
: null;
return (
<Paper sx={{ width: '100%', overflow: 'hidden' }} data-testid="ideas-table-container">
<Paper
sx={{ width: '100%', overflow: 'hidden' }}
data-testid="ideas-table-container"
>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
@ -386,9 +410,17 @@ export function IdeasTable() {
<TableRow>
<TableCell
colSpan={SKELETON_COLUMNS_COUNT}
sx={{ p: 0, borderBottom: expandedId === row.original.id ? 1 : 0, borderColor: 'divider' }}
sx={{
p: 0,
borderBottom:
expandedId === row.original.id ? 1 : 0,
borderColor: 'divider',
}}
>
<Collapse in={expandedId === row.original.id} unmountOnExit>
<Collapse
in={expandedId === row.original.id}
unmountOnExit
>
<CommentsPanel ideaId={row.original.id} />
</Collapse>
</TableCell>

View File

@ -1,7 +1,26 @@
import { createColumnHelper } from '@tanstack/react-table';
import { Chip, Box, IconButton, Tooltip, CircularProgress, Typography } from '@mui/material';
import { Delete, Comment, ExpandLess, AutoAwesome, AccessTime, Description } from '@mui/icons-material';
import type { Idea, IdeaStatus, IdeaPriority, IdeaComplexity } from '../../types/idea';
import {
Chip,
Box,
IconButton,
Tooltip,
CircularProgress,
Typography,
} from '@mui/material';
import {
Delete,
Comment,
ExpandLess,
AutoAwesome,
AccessTime,
Description,
} from '@mui/icons-material';
import type {
Idea,
IdeaStatus,
IdeaPriority,
IdeaComplexity,
} from '../../types/idea';
import { EditableCell } from './EditableCell';
import { ColorPickerCell } from './ColorPickerCell';
import { statusOptions, priorityOptions } from './constants';
@ -51,10 +70,10 @@ const complexityColors: Record<
function formatHoursShort(hours: number): string {
if (hours < 8) {
return `${hours}ч`;
return `${String(hours)}ч`;
}
const days = Math.floor(hours / 8);
return `${days}д`;
return `${String(days)}д`;
}
interface ColumnsConfig {
@ -68,7 +87,16 @@ interface ColumnsConfig {
generatingSpecificationId: string | null;
}
export const createColumns = ({ onDelete, onToggleComments, onEstimate, onViewEstimate, onSpecification, expandedId, estimatingId, generatingSpecificationId }: ColumnsConfig) => [
export const createColumns = ({
onDelete,
onToggleComments,
onEstimate,
onViewEstimate,
onSpecification,
expandedId,
estimatingId,
generatingSpecificationId,
}: ColumnsConfig) => [
columnHelper.display({
id: 'drag',
header: '',
@ -246,7 +274,9 @@ export const createColumns = ({ onDelete, onToggleComments, onEstimate, onViewEs
const hasSpecification = !!idea.specification;
return (
<Box sx={{ display: 'flex', gap: 0.5 }}>
<Tooltip title={hasSpecification ? 'Просмотр ТЗ' : 'Сгенерировать ТЗ'}>
<Tooltip
title={hasSpecification ? 'Просмотр ТЗ' : 'Сгенерировать ТЗ'}
>
<span>
<IconButton
size="small"
@ -254,7 +284,10 @@ export const createColumns = ({ onDelete, onToggleComments, onEstimate, onViewEs
disabled={isGeneratingSpec}
color={hasSpecification ? 'primary' : 'default'}
data-testid="specification-button"
sx={{ opacity: hasSpecification ? 0.9 : 0.5, '&:hover': { opacity: 1 } }}
sx={{
opacity: hasSpecification ? 0.9 : 0.5,
'&:hover': { opacity: 1 },
}}
>
{isGeneratingSpec ? (
<CircularProgress size={18} />
@ -290,7 +323,11 @@ export const createColumns = ({ onDelete, onToggleComments, onEstimate, onViewEs
data-testid="toggle-comments-button"
sx={{ opacity: isExpanded ? 1 : 0.5, '&:hover': { opacity: 1 } }}
>
{isExpanded ? <ExpandLess fontSize="small" /> : <Comment fontSize="small" />}
{isExpanded ? (
<ExpandLess fontSize="small" />
) : (
<Comment fontSize="small" />
)}
</IconButton>
</Tooltip>
<IconButton