Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v4

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/lint-frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,33 @@ jobs:
- name: TypeScript type check
working-directory: frontend
run: npm run typecheck

react-doctor:
name: React Doctor
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
working-directory: frontend
run: npm ci

- name: React Doctor score check
working-directory: frontend
run: |
SCORE=$(npx -y react-doctor@0.5.8 --score --yes)
echo "React Doctor score: $SCORE"
if [ "$SCORE" -lt 40 ]; then
echo "::error::React Doctor score $SCORE is below the minimum threshold of 40"
npx -y react-doctor@0.5.8 --verbose --yes
exit 1
fi
52 changes: 52 additions & 0 deletions .github/workflows/react-doctor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# React Doctor — finds security, performance, correctness, accessibility,
# bundle-size, and architecture issues in React codebases.
#
# Docs: https://www.react.doctor/ci
# Source: https://github.com/millionco/react-doctor

name: React Doctor

on:
# Scans the PR's changed files and posts a sticky summary comment listing only the new issues introduced relative to the merge base of the target branch.
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
# Scans `main` on every push to track the health-score trend and catch regressions that slipped past PR review.
push:
branches: ["main"]

permissions:
contents: read
pull-requests: write
issues: write
statuses: write

# Cancels any in-flight scan for the same PR (or branch, on push) the moment a new commit arrives, so reviewers only ever see the latest run.
concurrency:
group: react-doctor-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
react-doctor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: millionco/react-doctor@v2
with:
directory: frontend
version: "0.5.8"
# Advisory by default: React Doctor reports findings on every PR — a
# sticky summary comment, inline review comments, and a commit status
# with the health score — but never fails the check, so it won't red-X
# a teammate's PR on day one. When your team trusts the signal, graduate
# the gate: uncomment the block below and set blocking to "error" (fail
# on new error-severity findings) or "warning" (fail on any finding).
# Full reference: https://www.react.doctor/ci
# blocking: error # Gate level: "none" (advisory, the default) | "warning" | "error"
# scope: full # On PRs, scan the whole project instead of just changed files
# comment: false # Disable the sticky PR summary comment
# review-comments: false # Disable inline review comments on changed lines
# commit-status: false # Disable the commit status (score + counts, links to the run)
# project: "web,admin" # In a monorepo, scan specific workspace project(s)
Comment thread
mfortman11 marked this conversation as resolved.
30 changes: 15 additions & 15 deletions frontend/app/api/mutations/useCancelTaskMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ export interface CancelTaskResponse {
task_id: string;
}

async function cancelTask(
variables: CancelTaskRequest,
): Promise<CancelTaskResponse> {
const response = await fetch(`/api/tasks/${variables.taskId}/cancel`, {
method: "POST",
});

if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || "Failed to cancel task");
}

return response.json();
}

export const useCancelTaskMutation = (
options?: Omit<
UseMutationOptions<CancelTaskResponse, Error, CancelTaskRequest>,
Expand All @@ -23,21 +38,6 @@ export const useCancelTaskMutation = (
) => {
const queryClient = useQueryClient();

async function cancelTask(
variables: CancelTaskRequest,
): Promise<CancelTaskResponse> {
const response = await fetch(`/api/tasks/${variables.taskId}/cancel`, {
method: "POST",
});

if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || "Failed to cancel task");
}

return response.json();
}

const { onSuccess, onError, onSettled, ...restOptions } = options ?? {};

return useMutation({
Expand Down
38 changes: 19 additions & 19 deletions frontend/app/api/mutations/useCreateApiKeyMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,25 @@ export interface CreateApiKeyResponse {
created_at: string;
}

async function createApiKey(
variables: CreateApiKeyRequest,
): Promise<CreateApiKeyResponse> {
const response = await fetch("/api/keys", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(variables),
});

if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || "Failed to create API key");
}

return response.json();
}

export const useCreateApiKeyMutation = (
options?: Omit<
UseMutationOptions<CreateApiKeyResponse, Error, CreateApiKeyRequest>,
Expand All @@ -24,25 +43,6 @@ export const useCreateApiKeyMutation = (
) => {
const queryClient = useQueryClient();

async function createApiKey(
variables: CreateApiKeyRequest,
): Promise<CreateApiKeyResponse> {
const response = await fetch("/api/keys", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(variables),
});

if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || "Failed to create API key");
}

return response.json();
}

return useMutation({
mutationFn: createApiKey,
onSuccess: (...args) => {
Expand Down
38 changes: 19 additions & 19 deletions frontend/app/api/mutations/useOnboardingMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ interface OnboardingResponse {
task_id?: string;
}

async function submitOnboarding(
variables: OnboardingVariables,
): Promise<OnboardingResponse> {
const response = await fetch("/api/onboarding", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(variables),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to complete onboarding");
}

return response.json();
}

export const useOnboardingMutation = (
options?: Omit<
UseMutationOptions<OnboardingResponse, Error, OnboardingVariables>,
Expand All @@ -40,25 +59,6 @@ export const useOnboardingMutation = (

const updateOnboardingMutation = useUpdateOnboardingStateMutation();

async function submitOnboarding(
variables: OnboardingVariables,
): Promise<OnboardingResponse> {
const response = await fetch("/api/onboarding", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(variables),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to complete onboarding");
}

return response.json();
}

return useMutation({
mutationFn: submitOnboarding,
onSuccess: (data) => {
Expand Down
47 changes: 26 additions & 21 deletions frontend/app/api/mutations/useOnboardingRollbackMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@ interface RollbackParams {
embedding_only?: boolean;
}

async function rollbackOnboarding(
params: RollbackParams | void,
): Promise<OnboardingRollbackResponse> {
const requestBody = params || { embedding_only: false };

const response = await fetch("/api/onboarding/rollback", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});

if (!response.ok) {
const text = await response.text();
let message = "Failed to rollback onboarding";
try {
const error = JSON.parse(text);
if (error.error) message = error.error;
} catch {}
throw new Error(message);
}

return response.json();
}

export const useOnboardingRollbackMutation = (
options?: Omit<
UseMutationOptions<
Expand All @@ -26,27 +52,6 @@ export const useOnboardingRollbackMutation = (
) => {
const queryClient = useQueryClient();

async function rollbackOnboarding(
params: RollbackParams | void,
): Promise<OnboardingRollbackResponse> {
const requestBody = params || { embedding_only: false };

const response = await fetch("/api/onboarding/rollback", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to rollback onboarding");
}

return response.json();
}

return useMutation({
mutationFn: rollbackOnboarding,
onSettled: () => {
Expand Down
52 changes: 26 additions & 26 deletions frontend/app/api/mutations/useRetryTaskMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,32 @@ export interface RetryTaskResponse {
error?: string;
}

async function retryTask(
variables: RetryTaskRequest,
): Promise<RetryTaskResponse> {
const body = JSON.stringify(
variables.filePaths != null ? { file_paths: variables.filePaths } : {},
);

const response = await fetch(`/api/tasks/${variables.taskId}/retry`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});

const payload = (await response
.json()
.catch(() => ({}))) as RetryTaskResponse;

if (!response.ok) {
throw new Error(
payload.message || payload.error || "Failed to retry task files",
);
}

return payload;
}

export const useRetryTaskMutation = (
options?: Omit<
UseMutationOptions<RetryTaskResponse, Error, RetryTaskRequest>,
Expand All @@ -40,32 +66,6 @@ export const useRetryTaskMutation = (
) => {
const queryClient = useQueryClient();

async function retryTask(
variables: RetryTaskRequest,
): Promise<RetryTaskResponse> {
const body = JSON.stringify(
variables.filePaths != null ? { file_paths: variables.filePaths } : {},
);

const response = await fetch(`/api/tasks/${variables.taskId}/retry`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});

const payload = (await response
.json()
.catch(() => ({}))) as RetryTaskResponse;

if (!response.ok) {
throw new Error(
payload.message || payload.error || "Failed to retry task files",
);
}

return payload;
}

const { onSuccess, onError, onSettled, ...restOptions } = options ?? {};

return useMutation({
Expand Down
Loading
Loading