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
1 change: 1 addition & 0 deletions bin/debug/fetch-runnable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ const req = new DebugRequest('studies/ready')
req.parse()
.perform()
.then((json) => {
// eslint-disable-next-line no-console
console.dir(json)
})
3 changes: 2 additions & 1 deletion bin/debug/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ const req = new DebugRequest()
req.program.requiredOption('-j, --jobId <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)
})
1 change: 1 addition & 0 deletions bin/debug/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions bin/debug/set-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ req.method = 'PUT'
req.body = { status }

req.perform().then((json) => {
// eslint-disable-next-line no-console
console.dir(json)
})
1 change: 1 addition & 0 deletions bin/debug/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
2 changes: 2 additions & 0 deletions bin/debug/upload-results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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',
)
Expand Down
2 changes: 0 additions & 2 deletions bin/delete-clerk-test-users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
5 changes: 4 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
4 changes: 3 additions & 1 deletion lint-staged.config.mjs
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions sentry.client.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/app/[orgSlug]/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
133 changes: 66 additions & 67 deletions src/components/dashboard/researcher-user-studies-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 <Title order={5}>You are not a member of any lab organizations.</Title>
}

const primaryLabOrg = labOrgs[0]

const needsRefreshed = studies?.some((study) =>
study.jobStatusChanges.some((change) => !FINAL_STATUS.includes(change.status)),
)

if (!studies || studies.length === 0) {
return (
<Stack align="center" gap="md" p="xxl">
<Text>You haven&apos;t started a study yet</Text>
<ButtonLink
leftSection={<PlusIcon />}
href={`/${primaryLabOrg.slug}/study/request`}
data-testid="new-study"
>
Propose New Study
</ButtonLink>
</Stack>
)
}

return (
<Stack pt="xl">
<Group justify="space-between">
<Title order={3}>Proposed Studies</Title>
<Flex justify="flex-end">
<Paper shadow="xs" p="xxl">
<Stack align="center" gap="md">
<Text>You haven&apos;t started a study yet</Text>
<ButtonLink
leftSection={<PlusIcon />}
href={`/${labOrg.slug}/study/request`}
data-testid="new-study"
href={`/${primaryLabOrg.slug}/study/request`}
>
Propose New Study
</ButtonLink>
</Flex>
</Group>
<Divider c="charcoal.1" />
<Refresher isEnabled={Boolean(needsRefreshed)} refresh={refetch} isPending={isFetching} />
<Table layout="fixed" verticalSpacing="md" striped="even" highlightOnHover stickyHeader>
<TableThead>
<TableTr>
<TableTh fw={600}>Study Name</TableTh>
<TableTh fw={600}>Submitted On</TableTh>
<TableTh fw={600}>Submitted To</TableTh>
<TableTh fw={600}>Stage</TableTh>
<TableTh fw={600}>Status</TableTh>
<TableTh fw={600}>Study Details</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{studies.map((study) => (
<TableTr fz={14} key={study.id} bg={study.status === 'APPROVED' ? '#EAD4FC80' : undefined}>
<TableTd>{study.title}</TableTd>
<TableTd>{dayjs(study.createdAt).format('MMM DD, YYYY')}</TableTd>
<TableTd>{study.orgName}</TableTd>
<TableTd>{getStudyStage(study.status, 'researcher')}</TableTd>
<TableTd>
<DisplayStudyStatus
studyStatus={study.status}
audience="researcher"
jobStatusChanges={study.jobStatusChanges}
/>
</TableTd>
<TableTd>
<Link href={`/${study.orgSlug}/study/${study.id}/review`}>View</Link>
</TableTd>
</Stack>
</Paper>
)
}

return (
<Paper shadow="xs" p="xxl">
<Stack>
<Group justify="space-between">
<Title order={3}>Proposed Studies</Title>
<Flex justify="flex-end">
<ButtonLink
leftSection={<PlusIcon />}
data-testid="new-study"
href={`/${labOrg.slug}/study/request`}
>
Propose New Study
</ButtonLink>
</Flex>
</Group>
<Divider c="charcoal.1" />
<Refresher isEnabled={Boolean(needsRefreshed)} refresh={refetch} isPending={isFetching} />
<Table layout="fixed" verticalSpacing="md" striped="even" highlightOnHover stickyHeader>
<TableThead>
<TableTr>
<TableTh fw={600}>Study Name</TableTh>
<TableTh fw={600}>Submitted On</TableTh>
<TableTh fw={600}>Submitted To</TableTh>
<TableTh fw={600}>Stage</TableTh>
<TableTh fw={600}>Status</TableTh>
<TableTh fw={600}>Study Details</TableTh>
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
</TableThead>
<TableTbody>
{studies.map((study) => (
<TableTr fz={14} key={study.id} bg={study.status === 'APPROVED' ? '#EAD4FC80' : undefined}>
<TableTd>{study.title}</TableTd>
<TableTd>{dayjs(study.createdAt).format('MMM DD, YYYY')}</TableTd>
<TableTd>{study.orgName}</TableTd>
<TableTd>{getStudyStage(study.status, 'researcher')}</TableTd>
<TableTd>
<DisplayStudyStatus
studyStatus={study.status}
audience="researcher"
jobStatusChanges={study.jobStatusChanges}
/>
</TableTd>
<TableTd>
<Link href={`/${labOrg.slug}/study/${study.id}/review`}>View</Link>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
</Paper>
)
}
1 change: 1 addition & 0 deletions tests/configure-test-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion tests/no-select-all.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// custom-rules/no-select-all-without-args.js
export default {
const noSelectAllWithoutArgs = {
meta: {
type: 'problem',
docs: {
Expand Down Expand Up @@ -36,3 +36,5 @@ export default {
}
},
}

export default noSelectAllWithoutArgs
1 change: 1 addition & 0 deletions tests/providers.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
1 change: 1 addition & 0 deletions tests/unit.helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions tests/validate-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
Expand All @@ -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.')
}
}
Expand Down
Loading
Loading