diff --git a/bin/debug/fetch-runnable.ts b/bin/debug/fetch-runnable.ts index 550c83e3f..e29a41673 100644 --- a/bin/debug/fetch-runnable.ts +++ b/bin/debug/fetch-runnable.ts @@ -5,5 +5,6 @@ const req = new DebugRequest('studies/ready') req.parse() .perform() .then((json) => { + // eslint-disable-next-line no-console console.dir(json) }) diff --git a/bin/debug/keys.ts b/bin/debug/keys.ts index c0a0fe2b6..64fa6b1b2 100755 --- a/bin/debug/keys.ts +++ b/bin/debug/keys.ts @@ -4,12 +4,13 @@ const req = new DebugRequest() req.program.requiredOption('-j, --jobId ', 'jobId to get keys for') req.parse() -const { status, jobId } = req.program.opts() +const { jobId } = req.program.opts() req.path = `job/${jobId}/keys` req.method = 'GET' //req.body = { status } req.perform().then((json) => { + // eslint-disable-next-line no-console console.dir(json) }) diff --git a/bin/debug/request.ts b/bin/debug/request.ts index d442bffb5..6161b268c 100644 --- a/bin/debug/request.ts +++ b/bin/debug/request.ts @@ -42,6 +42,7 @@ export class DebugRequest { async perform() { const url = `${this.baseURL}/api/${this.path}` + // eslint-disable-next-line no-console console.log(`Sending request to ${url}`) const response = await fetch(url, { method: this.method, diff --git a/bin/debug/set-status.ts b/bin/debug/set-status.ts index d250d0f4e..e2f83546c 100644 --- a/bin/debug/set-status.ts +++ b/bin/debug/set-status.ts @@ -11,5 +11,6 @@ req.method = 'PUT' req.body = { status } req.perform().then((json) => { + // eslint-disable-next-line no-console console.dir(json) }) diff --git a/bin/debug/status.ts b/bin/debug/status.ts index 726520713..9bed39990 100644 --- a/bin/debug/status.ts +++ b/bin/debug/status.ts @@ -10,5 +10,6 @@ req.path = `job/${jobId}/status` req.method = 'GET' req.perform().then((json) => { + // eslint-disable-next-line no-console console.dir(json) }) diff --git a/bin/debug/upload-results.ts b/bin/debug/upload-results.ts index 560ba5ca0..c6feee494 100755 --- a/bin/debug/upload-results.ts +++ b/bin/debug/upload-results.ts @@ -42,6 +42,7 @@ class FileSender extends DebugRequest { const { resultFile, logFile, jobId } = this.program.opts() if (!resultFile && !logFile) { + // eslint-disable-next-line no-console console.log(this.program.opts()) console.warn('must supply either logs or results') process.exit(1) @@ -70,6 +71,7 @@ class FileSender extends DebugRequest { const sender = new FileSender() sender.perform().then(() => { + // eslint-disable-next-line no-console console.info( 'Results must be decrypted using ONLY tests/support/private_key.pem. normal reviewer keys will not work. use:\ncat tests/support/private_key.pem | pbcopy', ) diff --git a/bin/delete-clerk-test-users.ts b/bin/delete-clerk-test-users.ts index 98434df5d..71a83bccf 100644 --- a/bin/delete-clerk-test-users.ts +++ b/bin/delete-clerk-test-users.ts @@ -3,8 +3,6 @@ import 'dotenv/config' import { deleteClerkTestUsers } from '../tests/clerk' import dayjs from 'dayjs' -console.log() - const cutoff = dayjs(process.argv[2]).toDate() deleteClerkTestUsers(cutoff).catch((err: unknown) => { diff --git a/eslint.config.mjs b/eslint.config.mjs index 8846a94c9..fc5e1aef2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -11,7 +11,10 @@ const compat = new FlatCompat({ /** @type {import('eslint').Linter.Config[]} */ const eslintConfig = [ { - ignores: ['.*', 'CHANGELOG.md', 'src/styles/generated/'], + ignores: ['node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts'], + }, + { + ignores: ['.*', 'CHANGELOG.md', 'src/styles/generated/', 'test-results/**', 'tests/coverage/**'], }, ...compat.extends('next/core-web-vitals'), ...compat.extends('next/typescript'), diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs index 4ca19a597..5b02766bb 100644 --- a/lint-staged.config.mjs +++ b/lint-staged.config.mjs @@ -1,4 +1,6 @@ -export default { +const config = { '*.{js,jsx,ts,tsx,md,html,css}': ['prettier --write', 'eslint --fix'], '*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit', } + +export default config diff --git a/package-lock.json b/package-lock.json index 58b7af711..bfed0a495 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ }, "devDependencies": { "@clerk/testing": "^1.12.3", + "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.34.0", "@next/eslint-plugin-next": "^15.5.2", "@octokit/rest": "^22.0.0", diff --git a/package.json b/package.json index 0bac8a64d..f11b265dc 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "db:migrate": "./bin/migrate-dev-db", "start": "next start", "deploy": "tsx bin/deploy.ts", - "lint": "next lint && prettier --check --log-level warn .", + "lint": "eslint . && prettier --check --log-level warn .", "validate-actions": "tsx tests/validate-actions.ts", - "lint:fix": "next lint --fix && prettier --write --log-level warn .", + "lint:fix": "eslint --fix . && prettier --write --log-level warn .", "pre:push": "run-p lint:fix typecheck validate-actions", "typecheck": "tsc --noEmit --skipLibCheck", "update-db-types": "kysely-codegen --out-file src/database/types.ts", @@ -89,6 +89,7 @@ }, "devDependencies": { "@clerk/testing": "^1.12.3", + "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.34.0", "@next/eslint-plugin-next": "^15.5.2", "@octokit/rest": "^22.0.0", diff --git a/sentry.client.config.ts b/sentry.client.config.ts index b19a97f74..964d5f828 100644 --- a/sentry.client.config.ts +++ b/sentry.client.config.ts @@ -14,8 +14,10 @@ Sentry.init({ captureConsoleIntegration({ levels: ['warn', 'error'], }), + // eslint-disable-next-line import/namespace ...(typeof Sentry.replayIntegration === 'function' ? [ + // eslint-disable-next-line import/namespace Sentry.replayIntegration({ maskAllText: false, minReplayDuration: 5000, diff --git a/src/app/[orgSlug]/dashboard/page.tsx b/src/app/[orgSlug]/dashboard/page.tsx index 9b71ced19..ac8dd87f1 100644 --- a/src/app/[orgSlug]/dashboard/page.tsx +++ b/src/app/[orgSlug]/dashboard/page.tsx @@ -4,10 +4,10 @@ import { ResearcherStudiesTable } from '@/components/dashboard/researcher-table' import { ReviewerStudiesTable } from '@/components/dashboard/reviewer-table' import { errorToString, isActionError } from '@/lib/errors' import { titleize } from '@/lib/string' -import { isEnclaveOrg } from '@/lib/types' import { getOrgFromSlugAction } from '@/server/actions/org.actions' import { fetchStudiesForOrgAction } from '@/server/actions/study.actions' import { Stack, Text, Title } from '@mantine/core' +import { isEnclaveOrg } from '@/lib/types' export default async function OrgDashboardPage(props: { params: Promise<{ orgSlug: string }> }) { const { orgSlug } = await props.params diff --git a/src/components/dashboard/researcher-user-studies-table.tsx b/src/components/dashboard/researcher-user-studies-table.tsx index 3f75384cd..3a059c623 100644 --- a/src/components/dashboard/researcher-user-studies-table.tsx +++ b/src/components/dashboard/researcher-user-studies-table.tsx @@ -6,12 +6,14 @@ import { Refresher } from '@/components/refresher' import { DisplayStudyStatus } from '@/components/study/display-study-status' import { StudyJobStatus } from '@/database/types' import { useSession } from '@/hooks/session' +import { getLabOrg } from '@/lib/types' import { getStudyStage } from '@/lib/util' import { fetchStudiesForCurrentResearcherUserAction } from '@/server/actions/study.actions' import { Divider, Flex, Group, + Paper, Stack, Table, TableTbody, @@ -24,102 +26,99 @@ import { } from '@mantine/core' import { PlusIcon } from '@phosphor-icons/react/dist/ssr' import dayjs from 'dayjs' -import { useMemo } from 'react' const FINAL_STATUS: StudyJobStatus[] = ['CODE-REJECTED', 'JOB-ERRORED', 'FILES-APPROVED', 'FILES-REJECTED'] export const ResearcherUserStudiesTable = () => { const { session } = useSession() - - const userId = session?.user.id - const { data: studies, refetch, isFetching, } = useQuery({ - queryKey: ['user-researcher-studies', userId], + queryKey: ['user-researcher-studies'], queryFn: () => fetchStudiesForCurrentResearcherUserAction(), }) - const labOrgs = useMemo(() => { - if (!session) return [] - return Object.values(session.orgs).filter((org) => org.type === 'lab') - }, [session]) + // check if user can create a study + const labOrg = session ? getLabOrg(session) : null - if (!labOrgs.length) { + // temp message + if (!labOrg) { return You are not a member of any lab organizations. } - const primaryLabOrg = labOrgs[0] - const needsRefreshed = studies?.some((study) => study.jobStatusChanges.some((change) => !FINAL_STATUS.includes(change.status)), ) if (!studies || studies.length === 0) { return ( - - You haven't started a study yet - } - href={`/${primaryLabOrg.slug}/study/request`} - data-testid="new-study" - > - Propose New Study - - - ) - } - - return ( - - - Proposed Studies - + + + You haven't started a study yet } + href={`/${labOrg.slug}/study/request`} data-testid="new-study" - href={`/${primaryLabOrg.slug}/study/request`} > Propose New Study - - - - - - - - Study Name - Submitted On - Submitted To - Stage - Status - Study Details - - - - {studies.map((study) => ( - - {study.title} - {dayjs(study.createdAt).format('MMM DD, YYYY')} - {study.orgName} - {getStudyStage(study.status, 'researcher')} - - - - - View - + + + ) + } + + return ( + + + + Proposed Studies + + } + data-testid="new-study" + href={`/${labOrg.slug}/study/request`} + > + Propose New Study + + + + + +
+ + + Study Name + Submitted On + Submitted To + Stage + Status + Study Details - ))} - -
-
+ + + {studies.map((study) => ( + + {study.title} + {dayjs(study.createdAt).format('MMM DD, YYYY')} + {study.orgName} + {getStudyStage(study.status, 'researcher')} + + + + + View + + + ))} + + + + ) } diff --git a/tests/configure-test-env.ts b/tests/configure-test-env.ts index 09c5defc5..333b0acf3 100644 --- a/tests/configure-test-env.ts +++ b/tests/configure-test-env.ts @@ -25,6 +25,7 @@ async function setupUsers() { // Update publicKey in settings for enclave org if (org.type === 'enclave') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const settings = org.settings as any if (!settings.publicKey || settings.publicKey.length < 1000) { await db diff --git a/tests/no-select-all.mjs b/tests/no-select-all.mjs index aabb981e7..110848983 100644 --- a/tests/no-select-all.mjs +++ b/tests/no-select-all.mjs @@ -1,5 +1,5 @@ // custom-rules/no-select-all-without-args.js -export default { +const noSelectAllWithoutArgs = { meta: { type: 'problem', docs: { @@ -36,3 +36,5 @@ export default { } }, } + +export default noSelectAllWithoutArgs diff --git a/tests/providers.tsx b/tests/providers.tsx index 3cff92649..757531aaf 100644 --- a/tests/providers.tsx +++ b/tests/providers.tsx @@ -1,6 +1,7 @@ import { MantineProvider } from '@mantine/core' import { theme } from '@/theme' import { ModalsProvider } from '@mantine/modals' +// eslint-disable-next-line no-restricted-imports import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { FC, ReactNode } from 'react' import { ClerkProvider } from '@clerk/nextjs' diff --git a/tests/unit.helpers.tsx b/tests/unit.helpers.tsx index 6f1c5b0bd..d3be8f498 100644 --- a/tests/unit.helpers.tsx +++ b/tests/unit.helpers.tsx @@ -8,6 +8,7 @@ import jwt from 'jsonwebtoken' import { headers } from 'next/headers.js' import { useParams } from 'next/navigation' import { render } from '@testing-library/react' +// eslint-disable-next-line no-restricted-imports import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { MantineProvider } from '@mantine/core' import { ModalsProvider } from '@mantine/modals' diff --git a/tests/validate-actions.ts b/tests/validate-actions.ts index e72c1cbda..71185635f 100644 --- a/tests/validate-actions.ts +++ b/tests/validate-actions.ts @@ -167,6 +167,7 @@ function analyzeDirectory(directoryPath: string): void { overallSuccess = false errorLogs.push(...logs) } else if (VERBOSE) { + // eslint-disable-next-line no-console logs.forEach((log) => console.log(log)) } } @@ -176,6 +177,7 @@ function analyzeDirectory(directoryPath: string): void { errorLogs.forEach((log) => console.error(log)) process.exit(1) } else { + // eslint-disable-next-line no-console if (VERBOSE) console.log('All files passed analysis.') } } diff --git a/tests/vitest.setup.ts b/tests/vitest.setup.ts index b9ab1b9d8..fae4289d6 100644 --- a/tests/vitest.setup.ts +++ b/tests/vitest.setup.ts @@ -1,5 +1,5 @@ import 'dotenv/config' // read .env file before other imports, to match Next.js default -import { beforeAll, beforeEach, afterEach, afterAll, vi, Mock } from 'vitest' +import { beforeAll, beforeEach, afterEach, afterAll, vi, Mock, expect } from 'vitest' import { testTransaction } from 'pg-transactional-tests' import { createTempDir } from '@/tests/unit.helpers' import fs from 'fs' @@ -8,9 +8,9 @@ import { cleanup } from '@testing-library/react' import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers' import * as matchers from '@testing-library/jest-dom/matchers' -import { expect } from 'vitest' declare module 'vitest' { + // eslint-disable-next-line @typescript-eslint/no-explicit-any interface Assertion extends jest.Matchers, TestingLibraryMatchers {} } @@ -46,6 +46,7 @@ beforeAll(async () => { useSearchParams: () => { const router = useRouter() const path = router.query + // eslint-disable-next-line @typescript-eslint/no-explicit-any return new URLSearchParams(path as any) }, }