fix: resolve final TypeScript error in features directory

- Update UseTaskEditorReturn interface to properly type projectFeatures
- Change from unknown[] to explicit shape with id, label, type, and color properties
- All TypeScript errors in /src/features now resolved
This commit is contained in:
Rasmus Widing
2025-09-04 10:52:32 +03:00
parent 5b89dc1273
commit 581095d8a3
11 changed files with 38 additions and 20 deletions

View File

@@ -29,7 +29,7 @@ export const projectService = {
const processed = {
...project,
// Ensure pinned is properly handled as boolean
pinned: project.pinned === true || project.pinned === "true",
pinned: project.pinned === true,
progress: project.progress || 0,
updated: project.updated || formatRelativeTime(project.updated_at),
};

View File

@@ -120,7 +120,7 @@ export const TasksTab = ({ projectId }: TasksTabProps) => {
const handleTaskReorder = useCallback(
async (taskId: string, targetIndex: number, status: Task["status"]) => {
// Get all tasks in the target status, sorted by current order
const statusTasks = tasks.filter((task) => task.status === status).sort((a, b) => a.task_order - b.task_order);
const statusTasks = (tasks as Task[]).filter((task) => task.status === status).sort((a, b) => a.task_order - b.task_order);
const movingTaskIndex = statusTasks.findIndex((task) => task.id === taskId);
if (movingTaskIndex === -1 || targetIndex < 0 || targetIndex >= statusTasks.length) return;
@@ -152,12 +152,12 @@ export const TasksTab = ({ projectId }: TasksTabProps) => {
// Move task to different status
const moveTask = useCallback(
async (taskId: string, newStatus: Task["status"]) => {
const movingTask = tasks.find((task) => task.id === taskId);
const movingTask = (tasks as Task[]).find((task) => task.id === taskId);
if (!movingTask || movingTask.status === newStatus) return;
try {
// Calculate position for new status
const tasksInNewStatus = tasks.filter((t) => t.status === newStatus);
const tasksInNewStatus = (tasks as Task[]).filter((t) => t.status === newStatus);
const newOrder = getDefaultTaskOrder(tasksInNewStatus);
// Update via mutation (handles optimistic updates)
@@ -221,7 +221,7 @@ export const TasksTab = ({ projectId }: TasksTabProps) => {
<div className="relative h-[calc(100vh-220px)] overflow-auto">
{viewMode === "table" ? (
<TableView
tasks={tasks}
tasks={tasks as Task[]}
projectId={projectId}
onTaskView={openEditModal}
onTaskComplete={completeTask}
@@ -231,7 +231,7 @@ export const TasksTab = ({ projectId }: TasksTabProps) => {
/>
) : (
<BoardView
tasks={tasks}
tasks={tasks as Task[]}
projectId={projectId}
onTaskMove={moveTask}
onTaskReorder={handleTaskReorder}

View File

@@ -13,4 +13,4 @@ export type { TaskCardProps } from "./TaskCard";
export { TaskCard } from "./TaskCard";
export { TaskCardActions } from "./TaskCardActions";
export { TaskEditModal } from "./TaskEditModal";
export { TaskPriority } from "./TaskPriority";
export { TaskPriority as TaskPriorityComponent } from "./TaskPriority";

View File

@@ -1,6 +1,6 @@
import { useCallback, useState } from "react";
import { useToast } from "../../../../contexts/ToastContext";
import type { Task, UseTaskActionsReturn } from "../types";
import type { Assignee, Task, UseTaskActionsReturn } from "../types";
import { useDeleteTask, useUpdateTask } from "./useTaskQueries";
export const useTaskActions = (projectId: string): UseTaskActionsReturn => {
@@ -17,7 +17,7 @@ export const useTaskActions = (projectId: string): UseTaskActionsReturn => {
(taskId: string, newAssignee: string) => {
updateTaskMutation.mutate({
taskId,
updates: { assignee: newAssignee },
updates: { assignee: newAssignee as Assignee },
});
},
[updateTaskMutation],

View File

@@ -1,7 +1,7 @@
import { useCallback } from "react";
import { useToast } from "../../../../contexts/ToastContext";
import { useProjectFeatures } from "../../hooks/useProjectQueries";
import type { Assignee, CreateTaskRequest, Task, UseTaskEditorReturn } from "../types";
import type { Assignee, CreateTaskRequest, Task, UpdateTaskRequest, UseTaskEditorReturn } from "../types";
import { useCreateTask, useUpdateTask } from "./useTaskQueries";
export const useTaskEditor = (projectId: string): UseTaskEditorReturn => {
@@ -13,7 +13,12 @@ export const useTaskEditor = (projectId: string): UseTaskEditorReturn => {
const updateTaskMutation = useUpdateTask(projectId);
// Transform features data
const projectFeatures = featuresData?.features || [];
const projectFeatures = (featuresData?.features || []) as Array<{
id: string;
label: string;
type?: string;
color?: string;
}>;
const isSaving = createTaskMutation.isPending || updateTaskMutation.isPending;
// Get default order for new tasks based on status

View File

@@ -30,7 +30,12 @@ export interface UseTaskActionsReturn {
*/
export interface UseTaskEditorReturn {
// Data
projectFeatures: unknown[];
projectFeatures: Array<{
id: string;
label: string;
type?: string;
color?: string;
}>;
// Actions
saveTask: (localTask: Partial<Task> | null, editingTask: Task | null, onSuccess?: () => void) => void;

View File

@@ -5,7 +5,8 @@
*/
// Import priority type from priority.ts to avoid duplication
export type { TaskPriority } from "./priority";
import type { TaskPriority } from "./priority";
export type { TaskPriority };
// Database status enum - using database values directly
export type DatabaseTaskStatus = "todo" | "doing" | "review" | "done";

View File

@@ -7,7 +7,14 @@
// Project JSONB field types - replacing any with proper unions
export type ProjectPRD = Record<string, unknown>;
export type ProjectDocs = unknown[]; // Will be refined to ProjectDocument[] when fully migrated
export type ProjectFeatures = unknown[];
export type ProjectFeature = {
id: string;
label: string;
type?: string;
color?: string;
};
export type ProjectFeatures = ProjectFeature[];
export type ProjectData = unknown[];
// Project creation progress tracking

View File

@@ -69,7 +69,7 @@ export function ProjectsView({ className = "", "data-id": dataId }: ProjectsView
// Sort projects - pinned first, then alphabetically
const sortedProjects = useMemo(() => {
return [...projects].sort((a, b) => {
return [...(projects as Project[])].sort((a, b) => {
if (a.pinned && !b.pinned) return -1;
if (!a.pinned && b.pinned) return 1;
return a.title.localeCompare(b.title);
@@ -111,7 +111,7 @@ export function ProjectsView({ className = "", "data-id": dataId }: ProjectsView
// Refetch task counts when projects change
useEffect(() => {
if (projects.length > 0) {
if ((projects as Project[]).length > 0) {
refetchTaskCounts();
}
}, [projects, refetchTaskCounts]);
@@ -119,7 +119,7 @@ export function ProjectsView({ className = "", "data-id": dataId }: ProjectsView
// Handle pin toggle
const handlePinProject = async (e: React.MouseEvent, projectId: string) => {
e.stopPropagation();
const project = projects.find((p) => p.id === projectId);
const project = (projects as Project[]).find((p) => p.id === projectId);
if (!project) return;
updateProjectMutation.mutate({
@@ -146,7 +146,7 @@ export function ProjectsView({ className = "", "data-id": dataId }: ProjectsView
// If we deleted the selected project, select another one
if (selectedProject?.id === projectToDelete.id) {
const remainingProjects = projects.filter((p) => p.id !== projectToDelete.id);
const remainingProjects = (projects as Project[]).filter((p) => p.id !== projectToDelete.id);
if (remainingProjects.length > 0) {
const nextProject = remainingProjects[0];
setSelectedProject(nextProject);

View File

@@ -55,7 +55,7 @@ export class FeatureErrorBoundary extends Component<Props, State> {
const isDevelopment = process.env.NODE_ENV === "development";
return (
<div className={cn("min-h-[400px] flex items-center justify-center p-8", glassmorphism.background.medium)}>
<div className={cn("min-h-[400px] flex items-center justify-center p-8", glassmorphism.background.subtle)}>
<div className="max-w-2xl w-full">
<div className="flex items-start gap-4">
<div

View File

@@ -34,7 +34,7 @@ export function useSmartPolling(baseInterval: number = 10000) {
}, []);
// Calculate smart interval based on visibility and focus
const getSmartInterval = () => {
const getSmartInterval = (): number | false => {
if (!isVisible) {
// Page is hidden - disable polling
return false;