add deploy
Some checks reported errors
continuous-integration/drone/push Build was killed

This commit is contained in:
2025-12-31 09:50:51 +03:00
parent 524f3ebf23
commit 85c4a36e17
40 changed files with 855 additions and 199 deletions

View File

@ -69,7 +69,12 @@ export function CreateIdeaModal() {
};
return (
<Dialog open={createModalOpen} onClose={handleClose} maxWidth="sm" fullWidth>
<Dialog
open={createModalOpen}
onClose={handleClose}
maxWidth="sm"
fullWidth
>
<form onSubmit={handleSubmit}>
<DialogTitle>Create New Idea</DialogTitle>
<DialogContent>
@ -163,7 +168,9 @@ export function CreateIdeaModal() {
<TextField
label="Verification Method"
value={formData.verificationMethod}
onChange={(e) => handleChange('verificationMethod', e.target.value)}
onChange={(e) =>
handleChange('verificationMethod', e.target.value)
}
multiline
rows={2}
placeholder="How to verify this is done?"

View File

@ -32,7 +32,7 @@ const priorityOptions: { value: IdeaPriority; label: string }[] = [
export function IdeasFilters() {
const { filters, setFilter, clearFilters } = useIdeasStore();
const { data: modules = [] } = useModulesQuery();
const [searchValue, setSearchValue] = useState(filters.search || '');
const [searchValue, setSearchValue] = useState(filters.search ?? '');
// Debounced search
useEffect(() => {
@ -42,31 +42,40 @@ export function IdeasFilters() {
return () => clearTimeout(timer);
}, [searchValue, setFilter]);
const hasFilters = filters.status || filters.priority || filters.module || filters.search;
const hasFilters = Boolean(
filters.status ?? filters.priority ?? filters.module ?? filters.search,
);
return (
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'center' }}>
<Box
sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'center' }}
>
<TextField
size="small"
placeholder="Search ideas..."
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
sx={{ minWidth: 200 }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search fontSize="small" />
</InputAdornment>
),
slotProps={{
input: {
startAdornment: (
<InputAdornment position="start">
<Search fontSize="small" />
</InputAdornment>
),
},
}}
/>
<FormControl size="small" sx={{ minWidth: 120 }}>
<InputLabel>Status</InputLabel>
<Select
value={filters.status || ''}
<Select<IdeaStatus | ''>
value={filters.status ?? ''}
label="Status"
onChange={(e) => setFilter('status', e.target.value as IdeaStatus || undefined)}
onChange={(e) => {
const val = e.target.value;
setFilter('status', val === '' ? undefined : val);
}}
>
<MenuItem value="">All</MenuItem>
{statusOptions.map((opt) => (
@ -79,10 +88,13 @@ export function IdeasFilters() {
<FormControl size="small" sx={{ minWidth: 120 }}>
<InputLabel>Priority</InputLabel>
<Select
value={filters.priority || ''}
<Select<IdeaPriority | ''>
value={filters.priority ?? ''}
label="Priority"
onChange={(e) => setFilter('priority', e.target.value as IdeaPriority || undefined)}
onChange={(e) => {
const val = e.target.value;
setFilter('priority', val === '' ? undefined : val);
}}
>
<MenuItem value="">All</MenuItem>
{priorityOptions.map((opt) => (
@ -96,7 +108,7 @@ export function IdeasFilters() {
<FormControl size="small" sx={{ minWidth: 120 }}>
<InputLabel>Module</InputLabel>
<Select
value={filters.module || ''}
value={filters.module ?? ''}
label="Module"
onChange={(e) => setFilter('module', e.target.value || undefined)}
>

View File

@ -6,7 +6,7 @@ import {
Box,
ClickAwayListener,
} from '@mui/material';
import type { Idea, IdeaStatus, IdeaPriority } from '../../types/idea';
import type { Idea } from '../../types/idea';
import { useUpdateIdea } from '../../hooks/useIdeas';
interface EditableCellProps {
@ -27,7 +27,7 @@ export function EditableCell({
renderDisplay,
}: EditableCellProps) {
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState(value || '');
const [editValue, setEditValue] = useState(value ?? '');
const inputRef = useRef<HTMLInputElement>(null);
const updateIdea = useUpdateIdea();
@ -40,7 +40,7 @@ export function EditableCell({
const handleDoubleClick = () => {
setIsEditing(true);
setEditValue(value || '');
setEditValue(value ?? '');
};
const handleSave = async () => {
@ -55,10 +55,10 @@ export function EditableCell({
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
handleSave();
void handleSave();
} else if (e.key === 'Escape') {
setIsEditing(false);
setEditValue(value || '');
setEditValue(value ?? '');
}
};
@ -124,20 +124,3 @@ export function EditableCell({
</Box>
);
}
// Status options
export const statusOptions: { value: IdeaStatus; label: string }[] = [
{ value: 'backlog', label: 'Backlog' },
{ value: 'todo', label: 'To Do' },
{ value: 'in_progress', label: 'In Progress' },
{ value: 'done', label: 'Done' },
{ value: 'cancelled', label: 'Cancelled' },
];
// Priority options
export const priorityOptions: { value: IdeaPriority; label: string }[] = [
{ value: 'low', label: 'Low' },
{ value: 'medium', label: 'Medium' },
{ value: 'high', label: 'High' },
{ value: 'critical', label: 'Critical' },
];

View File

@ -28,13 +28,15 @@ const SKELETON_COLUMNS_COUNT = 7;
export function IdeasTable() {
const { data, isLoading, isError } = useIdeasQuery();
const deleteIdea = useDeleteIdea();
const { sorting, setSorting, pagination, setPage, setLimit } = useIdeasStore();
const { sorting, setSorting, pagination, setPage, setLimit } =
useIdeasStore();
const columns = useMemo(
() => createColumns((id) => deleteIdea.mutate(id)),
[deleteIdea]
[deleteIdea],
);
// eslint-disable-next-line react-hooks/incompatible-library
const table = useReactTable({
data: data?.data ?? [],
columns,
@ -51,7 +53,9 @@ export function IdeasTable() {
setPage(newPage + 1);
};
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
const handleChangeRowsPerPage = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
setLimit(parseInt(event.target.value, 10));
};
@ -84,20 +88,22 @@ export function IdeasTable() {
active={sorting.sortBy === header.id}
direction={
sorting.sortBy === header.id
? (sorting.sortOrder.toLowerCase() as 'asc' | 'desc')
? (sorting.sortOrder.toLowerCase() as
| 'asc'
| 'desc')
: 'asc'
}
onClick={() => handleSort(header.id)}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
header.getContext(),
)}
</TableSortLabel>
) : (
flexRender(
header.column.columnDef.header,
header.getContext()
header.getContext(),
)
)}
</TableCell>
@ -109,11 +115,13 @@ export function IdeasTable() {
{isLoading ? (
Array.from({ length: 5 }).map((_, index) => (
<TableRow key={index}>
{Array.from({ length: SKELETON_COLUMNS_COUNT }).map((_, colIndex) => (
<TableCell key={colIndex}>
<Skeleton variant="text" />
</TableCell>
))}
{Array.from({ length: SKELETON_COLUMNS_COUNT }).map(
(_, colIndex) => (
<TableCell key={colIndex}>
<Skeleton variant="text" />
</TableCell>
),
)}
</TableRow>
))
) : table.getRowModel().rows.length === 0 ? (
@ -156,7 +164,7 @@ export function IdeasTable() {
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
cell.getContext(),
)}
</TableCell>
))}

View File

@ -2,11 +2,15 @@ import { createColumnHelper } from '@tanstack/react-table';
import { Chip, Box, IconButton } from '@mui/material';
import { Delete } from '@mui/icons-material';
import type { Idea, IdeaStatus, IdeaPriority } from '../../types/idea';
import { EditableCell, statusOptions, priorityOptions } from './EditableCell';
import { EditableCell } from './EditableCell';
import { statusOptions, priorityOptions } from './constants';
const columnHelper = createColumnHelper<Idea>();
const statusColors: Record<IdeaStatus, 'default' | 'primary' | 'secondary' | 'success' | 'error'> = {
const statusColors: Record<
IdeaStatus,
'default' | 'primary' | 'secondary' | 'success' | 'error'
> = {
backlog: 'default',
todo: 'primary',
in_progress: 'secondary',
@ -14,7 +18,10 @@ const statusColors: Record<IdeaStatus, 'default' | 'primary' | 'secondary' | 'su
cancelled: 'error',
};
const priorityColors: Record<IdeaPriority, 'default' | 'info' | 'warning' | 'error'> = {
const priorityColors: Record<
IdeaPriority,
'default' | 'info' | 'warning' | 'error'
> = {
low: 'default',
medium: 'info',
high: 'warning',
@ -30,7 +37,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
field="title"
value={info.getValue()}
renderDisplay={(value) => (
<Box sx={{ fontWeight: 500 }}>{value || '—'}</Box>
<Box sx={{ fontWeight: 500 }}>{value ?? '—'}</Box>
)}
/>
),
@ -40,7 +47,8 @@ export const createColumns = (onDelete: (id: string) => void) => [
header: 'Status',
cell: (info) => {
const status = info.getValue();
const label = statusOptions.find((s) => s.value === status)?.label || status;
const label =
statusOptions.find((s) => s.value === status)?.label ?? status;
return (
<EditableCell
idea={info.row.original}
@ -60,7 +68,8 @@ export const createColumns = (onDelete: (id: string) => void) => [
header: 'Priority',
cell: (info) => {
const priority = info.getValue();
const label = priorityOptions.find((p) => p.value === priority)?.label || priority;
const label =
priorityOptions.find((p) => p.value === priority)?.label ?? priority;
return (
<EditableCell
idea={info.row.original}
@ -88,7 +97,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
idea={info.row.original}
field="module"
value={info.getValue()}
renderDisplay={(value) => value || '—'}
renderDisplay={(value) => value ?? '—'}
/>
),
size: 120,
@ -100,7 +109,7 @@ export const createColumns = (onDelete: (id: string) => void) => [
idea={info.row.original}
field="targetAudience"
value={info.getValue()}
renderDisplay={(value) => value || '—'}
renderDisplay={(value) => value ?? '—'}
/>
),
size: 150,

View File

@ -0,0 +1,16 @@
import type { IdeaStatus, IdeaPriority } from '../../types/idea';
export const statusOptions: { value: IdeaStatus; label: string }[] = [
{ value: 'backlog', label: 'Backlog' },
{ value: 'todo', label: 'To Do' },
{ value: 'in_progress', label: 'In Progress' },
{ value: 'done', label: 'Done' },
{ value: 'cancelled', label: 'Cancelled' },
];
export const priorityOptions: { value: IdeaPriority; label: string }[] = [
{ value: 'low', label: 'Low' },
{ value: 'medium', label: 'Medium' },
{ value: 'high', label: 'High' },
{ value: 'critical', label: 'Critical' },
];