Use this skill whenever the user asks you to write, edit, review, refactor, debug, or design TypeScript or TSX code. It is especially relevant for application code, backend routes, React/UI work, schemas, runtime boundaries, persistence, async workflows, API contracts, tests, lint/typecheck fixes, and code review. Apply it even when the user does not explicitly mention "TypeScript" if the files or project are TypeScript-based.
89
85%
Does it follow best practices?
Impact
95%
1.26xAverage score across 5 eval scenarios
Passed
No known issues
Support keeps getting reports that the bulk-invite panel shows stale counts and confusing success or error messages after a failed request. The component grew quickly during a launch and now mixes rendering, request state, recipient rules, and form handling in one place.
Refactor this feature so the user experience is reliable and the code is easier to reason about. Keep the public component props compatible with the existing call site.
Produce a TypeScript React project in the current directory. Include updated source files and focused tests for the invite panel behavior.
The following files are provided as inputs. Extract them before beginning.
=============== FILE: AGENTS.md ===============
src/features/<feature>/.=============== FILE: package.json =============== { "type": "module", "scripts": { "test": "vitest run", "typecheck": "tsc --noEmit" }, "dependencies": { "@testing-library/jest-dom": "^6.4.8", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", "jsdom": "^24.1.1", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", "typescript": "^5.5.3", "vite": "^5.3.4", "vitest": "^2.0.4" } }
=============== FILE: tsconfig.json =============== { "compilerOptions": { "jsx": "react-jsx", "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "exactOptionalPropertyTypes": true, "skipLibCheck": true }, "include": ["src//*.ts", "src//*.tsx"] }
=============== FILE: src/test/setup.ts =============== import "@testing-library/jest-dom/vitest";
=============== FILE: src/features/invites/BulkInvitePanel.tsx =============== import { useEffect, useState } from "react";
type Member = { id: string; email: string; role: "owner" | "admin" | "member"; invitedAt: string | null; };
type Props = { teamName: string; members: Member[]; sendInvites: (emails: string[]) => Promise<{ sent: string[]; skipped: string[] }>; };
export default function BulkInvitePanel({ teamName, members, sendInvites }: Props) { const [localMembers, setLocalMembers] = useState(members); const [rawEmails, setRawEmails] = useState(""); const [selectedCount, setSelectedCount] = useState(0); const [isLoading, setIsLoading] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const [isError, setIsError] = useState(false); const [message, setMessage] = useState<string | null>(null);
useEffect(() => { setLocalMembers(members); setSelectedCount(rawEmails.split(",").filter(Boolean).length); }, [members, rawEmails]);
async function handleClick() { setIsLoading(true); setIsSuccess(false); setIsError(false); setMessage(null); const emails = rawEmails .split(",") .map((email) => email.trim().toLowerCase()) .filter((email) => email && !localMembers.some((member) => member.email === email || member.invitedAt));
try {
const result = await sendInvites(emails);
setIsSuccess(true);
setMessage("Sent " + result.sent.length + " invites");
setSelectedCount(0);
setRawEmails("");
} catch (error) {
setIsError(true);
setMessage((error as Error).message);
} finally {
setIsLoading(false);
}}
return ( <section> <h2>Invite people to {teamName}</h2> <div contentEditable onInput={(event) => setRawEmails(event.currentTarget.textContent || "")} /> <div onClick={handleClick}>Send {selectedCount} invites</div> {isLoading && <p>Sending...</p>} {isSuccess && <p>{message}</p>} {isError && <p>{message}</p>} </section> ); }