Batch C: Fix medium-priority 'any' in UI components (vibe-kanban) (#1312)

* ##  Batch C Complete

All medium-priority TypeScript `any` types have been fixed across 7 files:

1. **[markdown-renderer.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/ui/markdown-renderer.tsx)** - Replaced 11 `any` with `React.ComponentProps<'tag'>`
2. **[ExecutorConfigForm.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/ExecutorConfigForm.tsx)** - Used `unknown` for form data, `IChangeEvent<unknown>` for handlers
3. **[FormTemplate.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/rjsf/templates/FormTemplate.tsx)** - Replaced with `React.PropsWithChildren`
4. **[ArrayFieldTemplate.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/rjsf/templates/ArrayFieldTemplate.tsx)** - Created minimal `ArrayItemWithButtons` type for RJSF v6 beta
5. **[DiffsPanel.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/panels/DiffsPanel.tsx)** - Used `Diff[]` and typed `t` function properly
6. **[DiffCard.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/DiffCard.tsx)** - Used existing `ReviewComment` type from context
7. **[useGitOperations.ts](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/hooks/useGitOperations.ts)** - Properly narrowed `unknown` errors with type guards

`pnpm run check` passes 

* Cleanup script changes for task attempt 5fe86537-3bc1-4d78-96c0-95347fc7c05f

* Excellent! The build succeeds. Now let's create a summary:

##  Batch C Complete + RJSF Upgrade

Successfully fixed all medium-priority TypeScript `any` types **and** upgraded RJSF to stable 6.1.1:

### Type Fixes (7 files)
1. **[markdown-renderer.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/ui/markdown-renderer.tsx)** - Replaced 11 `any` with `React.ComponentProps<'tag'>`
2. **[ExecutorConfigForm.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/ExecutorConfigForm.tsx)** - Used `unknown` and `IChangeEvent<unknown>`
3. **[FormTemplate.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/rjsf/templates/FormTemplate.tsx)** - Used `React.PropsWithChildren`
4. **[ArrayFieldTemplate.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/rjsf/templates/ArrayFieldTemplate.tsx)** - Migrated to stable RJSF v6 API with `ArrayFieldItemTemplate`
5. **[DiffsPanel.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/panels/DiffsPanel.tsx)** - Used `Diff[]` and proper `t` function type
6. **[DiffCard.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/components/DiffCard.tsx)** - Used `ReviewComment` type from context
7. **[useGitOperations.ts](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/5fe8-batch-c-fix-medi/frontend/src/hooks/useGitOperations.ts)** - Properly narrowed `unknown` with type guards

### Dependency Upgrades
- `@rjsf/core`: 6.0.0-beta.11 → **6.1.1** (stable)
- `@rjsf/utils`: 6.0.0-beta.11 → **6.1.1** (stable)
- `@rjsf/validator-ajv8`: 6.0.0-beta.11 → **6.1.1** (stable)
- `@rjsf/shadcn`: 6.0.0-beta.10 → **6.1.1** (stable)

### RJSF Migration
Updated to v6 stable API:
- Removed workaround type (no longer needed)
- Added `ArrayFieldItemTemplate` for proper item rendering
- Updated to use `onRemoveItem` callback instead of deprecated `onDropIndexClick`

All checks pass 

* Cleanup script changes for task attempt 5fe86537-3bc1-4d78-96c0-95347fc7c05f
This commit is contained in:
Louis Knight-Webb
2025-11-17 22:35:20 +00:00
committed by GitHub
parent 870a166c38
commit 41376eba94
11 changed files with 175 additions and 119 deletions

View File

@@ -22,7 +22,11 @@ import {
import '@/styles/diff-style-overrides.css';
import { attemptsApi } from '@/lib/api';
import type { TaskAttempt } from 'shared/types';
import { useReview, type ReviewDraft } from '@/contexts/ReviewProvider';
import {
useReview,
type ReviewDraft,
type ReviewComment,
} from '@/contexts/ReviewProvider';
import { CommentWidgetLine } from '@/components/diff/CommentWidgetLine';
import { ReviewCommentRenderer } from '@/components/diff/ReviewCommentRenderer';
import {
@@ -149,9 +153,8 @@ export default function DiffCard({
// Transform comments to git-diff-view extendData format
const extendData = useMemo(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const oldFileData: Record<string, { data: any }> = {};
const newFileData: Record<string, { data: any }> = {};
const oldFileData: Record<string, { data: ReviewComment }> = {};
const newFileData: Record<string, { data: ReviewComment }> = {};
commentsForFile.forEach((comment) => {
const lineKey = String(comment.lineNumber);
@@ -181,7 +184,11 @@ export default function DiffCard({
setDraft(widgetKey, draft);
};
const renderWidgetLine = (props: any) => {
const renderWidgetLine = (props: {
side: SplitSide;
lineNumber: number;
onClose: () => void;
}) => {
const widgetKey = `${filePath}-${props.side}-${props.lineNumber}`;
const draft = drafts[widgetKey];
if (!draft) return null;
@@ -197,7 +204,7 @@ export default function DiffCard({
);
};
const renderExtendLine = (lineData: any) => {
const renderExtendLine = (lineData: { data: ReviewComment }) => {
return (
<ReviewCommentRenderer comment={lineData.data} projectId={projectId} />
);

View File

@@ -1,5 +1,6 @@
import { useMemo, useEffect, useState } from 'react';
import Form from '@rjsf/core';
import type { IChangeEvent } from '@rjsf/core';
import { RJSFValidationError } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';
@@ -22,10 +23,10 @@ type ExecutorType =
interface ExecutorConfigFormProps {
executor: ExecutorType;
value: any;
onSubmit?: (formData: any) => void;
onChange?: (formData: any) => void;
onSave?: (formData: any) => Promise<void>;
value: unknown;
onSubmit?: (formData: unknown) => void;
onChange?: (formData: unknown) => void;
onSave?: (formData: unknown) => Promise<void>;
disabled?: boolean;
isSaving?: boolean;
isDirty?: boolean;
@@ -43,7 +44,7 @@ export function ExecutorConfigForm({
isSaving = false,
isDirty = false,
}: ExecutorConfigFormProps) {
const [formData, setFormData] = useState(value || {});
const [formData, setFormData] = useState<unknown>(value || {});
const [validationErrors, setValidationErrors] = useState<
RJSFValidationError[]
>([]);
@@ -57,14 +58,16 @@ export function ExecutorConfigForm({
setValidationErrors([]);
}, [value, executor]);
const handleChange = ({ formData: newFormData }: any) => {
const handleChange = (event: IChangeEvent<unknown>) => {
const newFormData = event.formData;
setFormData(newFormData);
if (onChange) {
onChange(newFormData);
}
};
const handleSubmit = async ({ formData: submitData }: any) => {
const handleSubmit = async (event: IChangeEvent<unknown>) => {
const submitData = event.formData;
setValidationErrors([]);
if (onSave) {
await onSave(submitData);

View File

@@ -14,7 +14,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import type { TaskAttempt } from 'shared/types';
import type { TaskAttempt, Diff } from 'shared/types';
import GitOperations, {
type GitOperationsInputs,
} from '@/components/tasks/Toolbar/GitOperations.tsx';
@@ -125,7 +125,7 @@ export function DiffsPanel({ selectedAttempt, gitOps }: DiffsPanelProps) {
}
interface DiffsPanelContentProps {
diffs: any[];
diffs: Diff[];
fileCount: number;
added: number;
deleted: number;
@@ -136,7 +136,7 @@ interface DiffsPanelContentProps {
selectedAttempt: TaskAttempt | null;
gitOps?: GitOperationsInputs;
loading: boolean;
t: (key: string, params?: any) => string;
t: (key: string, params?: Record<string, unknown>) => string;
}
function DiffsPanelContent({

View File

@@ -1,6 +1,6 @@
import {
import type {
ArrayFieldTemplateProps,
ArrayFieldTemplateItemType,
ArrayFieldItemTemplateProps,
} from '@rjsf/utils';
import { Button } from '@/components/ui/button';
import { Plus, X } from 'lucide-react';
@@ -14,17 +14,7 @@ export const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
return (
<div className="space-y-4">
<div>
{items.length > 0 &&
items.map((element: ArrayFieldTemplateItemType) => (
<ArrayItem
key={element.key}
element={element}
disabled={disabled}
readonly={readonly}
/>
))}
</div>
<div>{items}</div>
{canAdd && (
<Button
@@ -43,30 +33,21 @@ export const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
);
};
interface ArrayItemProps {
element: ArrayFieldTemplateItemType;
disabled?: boolean;
readonly?: boolean;
}
const ArrayItem = ({ element, disabled, readonly }: ArrayItemProps) => {
const { children } = element;
const elementAny = element as any; // Type assertion needed for RJSF v6 beta properties
export const ArrayFieldItemTemplate = (props: ArrayFieldItemTemplateProps) => {
const { children, buttonsProps, disabled, readonly } = props;
return (
<div className="flex items-center gap-2">
<div className="flex-1">{children}</div>
{/* Remove button */}
{elementAny.buttonsProps?.hasRemove && (
{buttonsProps.hasRemove && (
<Button
type="button"
variant="ghost"
size="sm"
onClick={elementAny.buttonsProps.onDropIndexClick(
elementAny.buttonsProps.index
)}
disabled={disabled || readonly || elementAny.buttonsProps.disabled}
onClick={buttonsProps.onRemoveItem}
disabled={disabled || readonly || buttonsProps.disabled}
className="h-8 w-8 p-0 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-all duration-200 shrink-0"
title="Remove item"
>

View File

@@ -1,5 +1,3 @@
export const FormTemplate = (props: any) => {
const { children } = props;
export const FormTemplate = ({ children }: React.PropsWithChildren) => {
return <div className="w-full">{children}</div>;
};

View File

@@ -1,4 +1,7 @@
export { ArrayFieldTemplate } from './ArrayFieldTemplate';
export {
ArrayFieldTemplate,
ArrayFieldItemTemplate,
} from './ArrayFieldTemplate';
export { FieldTemplate } from './FieldTemplate';
export { ObjectFieldTemplate } from './ObjectFieldTemplate';
export { FormTemplate } from './FormTemplate';

View File

@@ -7,6 +7,7 @@ import {
} from './widgets';
import {
ArrayFieldTemplate,
ArrayFieldItemTemplate,
FieldTemplate,
ObjectFieldTemplate,
FormTemplate,
@@ -22,6 +23,7 @@ export const customWidgets: RegistryWidgetsType = {
export const customTemplates = {
ArrayFieldTemplate,
ArrayFieldItemTemplate,
FieldTemplate,
ObjectFieldTemplate,
FormTemplate,

View File

@@ -85,7 +85,11 @@ function LinkOverride({
);
}
function InlineCodeOverride({ children, className, ...props }: any) {
function InlineCodeOverride({
children,
className,
...props
}: React.ComponentProps<'code'>) {
// Only highlight inline code, not fenced code blocks
const hasLanguage =
typeof className === 'string' && /\blanguage-/.test(className);
@@ -123,28 +127,28 @@ function MarkdownRenderer({
a: { component: LinkOverride },
code: { component: InlineCodeOverride },
strong: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'strong'>) => (
<span {...props} className="">
{children}
</span>
),
},
em: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'em'>) => (
<em {...props} className="italic">
{children}
</em>
),
},
p: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'p'>) => (
<p {...props} className="leading-tight my-2">
{children}
</p>
),
},
h1: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'h1'>) => (
<h1
{...props}
className="text-lg font-medium leading-tight mt-4 mb-2"
@@ -154,7 +158,7 @@ function MarkdownRenderer({
),
},
h2: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'h2'>) => (
<h2
{...props}
className="text-base font-medium leading-tight mt-4 mb-2"
@@ -164,14 +168,14 @@ function MarkdownRenderer({
),
},
h3: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'h3'>) => (
<h3 {...props} className="text-sm leading-tight mt-3 mb-2">
{children}
</h3>
),
},
ul: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'ul'>) => (
<ul
{...props}
className="list-disc list-outside ps-6 my-3 space-y-1.5"
@@ -181,7 +185,7 @@ function MarkdownRenderer({
),
},
ol: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'ol'>) => (
<ol
{...props}
className="list-decimal list-outside ps-6 my-3 space-y-1.5"
@@ -191,14 +195,14 @@ function MarkdownRenderer({
),
},
li: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'li'>) => (
<li {...props} className="leading-tight">
{children}
</li>
),
},
pre: {
component: ({ children, ...props }: any) => (
component: ({ children, ...props }: React.ComponentProps<'pre'>) => (
<pre
{...props}
className="overflow-x-auto whitespace-pre-wrap break-words font-mono text-sm bg-muted/50 rounded-sm p-2 my-2"

View File

@@ -32,16 +32,24 @@ export function useGitOperations(
const merge = useMerge(
attemptId,
() => setError(null),
(err: any) => {
setError(err?.message || 'Failed to merge');
(err: unknown) => {
const message =
err && typeof err === 'object' && 'message' in err
? String(err.message)
: 'Failed to merge';
setError(message);
}
);
const push = usePush(
attemptId,
() => setError(null),
(err: any) => {
setError(err?.message || 'Failed to push');
(err: unknown) => {
const message =
err && typeof err === 'object' && 'message' in err
? String(err.message)
: 'Failed to push';
setError(message);
}
);
@@ -49,8 +57,12 @@ export function useGitOperations(
attemptId,
projectId,
() => setError(null),
(err: any) => {
setError(err?.message || 'Failed to change target branch');
(err: unknown) => {
const message =
err && typeof err === 'object' && 'message' in err
? String(err.message)
: 'Failed to change target branch';
setError(message);
}
);