From 2bd4772ff4f850cb1ea0653e07b07e14a1d148a0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 2 Jan 2026 23:16:17 +0000 Subject: [PATCH 01/10] Apply Prettier formatting --- .vscode/settings.json | 2 +- .../migrations/meta/0000_snapshot.json | 178 +++++------------- .../workers/migrations/meta/_journal.json | 2 +- packages/workers/scripts/generate-openapi.mjs | 15 +- 4 files changed, 59 insertions(+), 138 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index eccb27945..9c0220518 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -39,5 +39,5 @@ "tailwindCSS.experimental.configFile": "packages/web/src/global.css", "files.associations": { "*.css": "tailwindcss" - }, + } } diff --git a/packages/workers/migrations/meta/0000_snapshot.json b/packages/workers/migrations/meta/0000_snapshot.json index 738a907e6..41c3935c8 100644 --- a/packages/workers/migrations/meta/0000_snapshot.json +++ b/packages/workers/migrations/meta/0000_snapshot.json @@ -107,12 +107,8 @@ "name": "account_userId_user_id_fk", "tableFrom": "account", "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -190,12 +186,8 @@ "name": "invitation_inviterId_user_id_fk", "tableFrom": "invitation", "tableTo": "user", - "columnsFrom": [ - "inviterId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["inviterId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -203,12 +195,8 @@ "name": "invitation_organizationId_organization_id_fk", "tableFrom": "invitation", "tableTo": "organization", - "columnsFrom": [ - "organizationId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["organizationId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -284,12 +272,8 @@ "name": "mediaFiles_uploadedBy_user_id_fk", "tableFrom": "mediaFiles", "tableTo": "user", - "columnsFrom": [ - "uploadedBy" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["uploadedBy"], + "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } @@ -345,12 +329,8 @@ "name": "member_userId_user_id_fk", "tableFrom": "member", "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -358,12 +338,8 @@ "name": "member_organizationId_organization_id_fk", "tableFrom": "member", "tableTo": "organization", - "columnsFrom": [ - "organizationId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["organizationId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -422,9 +398,7 @@ "indexes": { "organization_slug_unique": { "name": "organization_slug_unique", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -528,9 +502,7 @@ "indexes": { "project_invitations_token_unique": { "name": "project_invitations_token_unique", - "columns": [ - "token" - ], + "columns": ["token"], "isUnique": true } }, @@ -539,12 +511,8 @@ "name": "project_invitations_orgId_organization_id_fk", "tableFrom": "project_invitations", "tableTo": "organization", - "columnsFrom": [ - "orgId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["orgId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -552,12 +520,8 @@ "name": "project_invitations_projectId_projects_id_fk", "tableFrom": "project_invitations", "tableTo": "projects", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["projectId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -565,12 +529,8 @@ "name": "project_invitations_invitedBy_user_id_fk", "tableFrom": "project_invitations", "tableTo": "user", - "columnsFrom": [ - "invitedBy" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["invitedBy"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -626,12 +586,8 @@ "name": "project_members_projectId_projects_id_fk", "tableFrom": "project_members", "tableTo": "projects", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["projectId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -639,12 +595,8 @@ "name": "project_members_userId_user_id_fk", "tableFrom": "project_members", "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -714,12 +666,8 @@ "name": "projects_orgId_organization_id_fk", "tableFrom": "projects", "tableTo": "organization", - "columnsFrom": [ - "orgId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["orgId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -727,12 +675,8 @@ "name": "projects_createdBy_user_id_fk", "tableFrom": "projects", "tableTo": "user", - "columnsFrom": [ - "createdBy" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["createdBy"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -820,9 +764,7 @@ "indexes": { "session_token_unique": { "name": "session_token_unique", - "columns": [ - "token" - ], + "columns": ["token"], "isUnique": true } }, @@ -831,12 +773,8 @@ "name": "session_userId_user_id_fk", "tableFrom": "session", "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -844,12 +782,8 @@ "name": "session_impersonatedBy_user_id_fk", "tableFrom": "session", "tableTo": "user", - "columnsFrom": [ - "impersonatedBy" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["impersonatedBy"], + "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, @@ -857,12 +791,8 @@ "name": "session_activeOrganizationId_organization_id_fk", "tableFrom": "session", "tableTo": "organization", - "columnsFrom": [ - "activeOrganizationId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["activeOrganizationId"], + "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } @@ -960,23 +890,17 @@ "indexes": { "subscriptions_userId_unique": { "name": "subscriptions_userId_unique", - "columns": [ - "userId" - ], + "columns": ["userId"], "isUnique": true }, "subscriptions_stripeCustomerId_unique": { "name": "subscriptions_stripeCustomerId_unique", - "columns": [ - "stripeCustomerId" - ], + "columns": ["stripeCustomerId"], "isUnique": true }, "subscriptions_stripeSubscriptionId_unique": { "name": "subscriptions_stripeSubscriptionId_unique", - "columns": [ - "stripeSubscriptionId" - ], + "columns": ["stripeSubscriptionId"], "isUnique": true } }, @@ -985,12 +909,8 @@ "name": "subscriptions_userId_user_id_fk", "tableFrom": "subscriptions", "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1053,12 +973,8 @@ "name": "twoFactor_userId_user_id_fk", "tableFrom": "twoFactor", "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1198,16 +1114,12 @@ "indexes": { "user_email_unique": { "name": "user_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true }, "user_username_unique": { "name": "user_username_unique", - "columns": [ - "username" - ], + "columns": ["username"], "isUnique": true } }, @@ -1281,4 +1193,4 @@ "internal": { "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/workers/migrations/meta/_journal.json b/packages/workers/migrations/meta/_journal.json index 957112561..abac566a1 100644 --- a/packages/workers/migrations/meta/_journal.json +++ b/packages/workers/migrations/meta/_journal.json @@ -10,4 +10,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/packages/workers/scripts/generate-openapi.mjs b/packages/workers/scripts/generate-openapi.mjs index 4511a4606..dc35b84d8 100644 --- a/packages/workers/scripts/generate-openapi.mjs +++ b/packages/workers/scripts/generate-openapi.mjs @@ -252,9 +252,18 @@ async function generate() { // Organization routes (new org-scoped architecture) { file: 'src/routes/orgs/index.js', basePath: '/api/orgs' }, { file: 'src/routes/orgs/projects.js', basePath: '/api/orgs/{orgId}/projects' }, - { file: 'src/routes/orgs/members.js', basePath: '/api/orgs/{orgId}/projects/{projectId}/members' }, - { file: 'src/routes/orgs/invitations.js', basePath: '/api/orgs/{orgId}/projects/{projectId}/invitations' }, - { file: 'src/routes/orgs/pdfs.js', basePath: '/api/orgs/{orgId}/projects/{projectId}/studies/{studyId}/pdfs' }, + { + file: 'src/routes/orgs/members.js', + basePath: '/api/orgs/{orgId}/projects/{projectId}/members', + }, + { + file: 'src/routes/orgs/invitations.js', + basePath: '/api/orgs/{orgId}/projects/{projectId}/invitations', + }, + { + file: 'src/routes/orgs/pdfs.js', + basePath: '/api/orgs/{orgId}/projects/{projectId}/studies/{studyId}/pdfs', + }, // Legacy routes (deprecated, kept for backward compatibility detection) { file: 'src/routes/projects.js', basePath: '/api/projects' }, { file: 'src/routes/members.js', basePath: '/api/projects/{projectId}/members' }, From ab26b7ff6efd84a91f4c0860bbb15be2b63bdee2 Mon Sep 17 00:00:00 2001 From: Jacob Maynard Date: Fri, 2 Jan 2026 17:49:15 -0600 Subject: [PATCH 02/10] make robins text collaborative like amstar --- .../components/checklist/ChecklistWithPdf.jsx | 2 + .../checklist/ChecklistYjsWrapper.jsx | 4 + .../components/checklist/GenericChecklist.jsx | 2 + .../ROBINSIChecklist/DomainSection.jsx | 4 + .../ROBINSIChecklist/PlanningSection.jsx | 30 +- .../ROBINSIChecklist/ROBINSIChecklist.jsx | 7 + .../checklist/ROBINSIChecklist/SectionA.jsx | 30 +- .../checklist/ROBINSIChecklist/SectionB.jsx | 30 +- .../checklist/ROBINSIChecklist/SectionC.jsx | 30 +- .../checklist/ROBINSIChecklist/SectionD.jsx | 30 +- .../ROBINSIChecklist/SignallingQuestion.jsx | 26 +- .../src/primitives/useProject/checklists.js | 538 ------------------ .../useProject/checklists/common.js | 112 ++++ .../useProject/checklists/handlers/amstar2.js | 146 +++++ .../useProject/checklists/handlers/base.js | 70 +++ .../checklists/handlers/robins-i.js | 349 ++++++++++++ .../primitives/useProject/checklists/index.js | 234 ++++++++ .../web/src/primitives/useProject/index.js | 4 +- .../web/src/primitives/useProject/sync.js | 20 +- 19 files changed, 1033 insertions(+), 635 deletions(-) delete mode 100644 packages/web/src/primitives/useProject/checklists.js create mode 100644 packages/web/src/primitives/useProject/checklists/common.js create mode 100644 packages/web/src/primitives/useProject/checklists/handlers/amstar2.js create mode 100644 packages/web/src/primitives/useProject/checklists/handlers/base.js create mode 100644 packages/web/src/primitives/useProject/checklists/handlers/robins-i.js create mode 100644 packages/web/src/primitives/useProject/checklists/index.js diff --git a/packages/web/src/components/checklist/ChecklistWithPdf.jsx b/packages/web/src/components/checklist/ChecklistWithPdf.jsx index e1b24873d..323f4f3c7 100644 --- a/packages/web/src/components/checklist/ChecklistWithPdf.jsx +++ b/packages/web/src/components/checklist/ChecklistWithPdf.jsx @@ -24,6 +24,7 @@ export default function ChecklistWithPdf(props) { // props.selectedPdfId - currently selected PDF ID // props.onPdfSelect - handler for PDF selection change // props.getQuestionNote - function to get Y.Text for a question note + // props.getRobinsText - function to get Y.Text for a ROBINS-I free-text field // props.pdfUrl - optional PDF URL (for server-hosted PDFs) return ( @@ -44,6 +45,7 @@ export default function ChecklistWithPdf(props) { onUpdate={props.onUpdate} readOnly={props.readOnly} getQuestionNote={props.getQuestionNote} + getRobinsText={props.getRobinsText} /> {/* Second panel: PDF Viewer */} diff --git a/packages/web/src/components/checklist/ChecklistYjsWrapper.jsx b/packages/web/src/components/checklist/ChecklistYjsWrapper.jsx index 49d8535a2..a123baca1 100644 --- a/packages/web/src/components/checklist/ChecklistYjsWrapper.jsx +++ b/packages/web/src/components/checklist/ChecklistYjsWrapper.jsx @@ -40,6 +40,7 @@ export default function ChecklistYjsWrapper() { getChecklistData, addPdfToStudy, getQuestionNote, + getRobinsText, } = useProject(params.projectId); // Set active project for action store @@ -460,6 +461,9 @@ export default function ChecklistYjsWrapper() { getQuestionNote={questionKey => getQuestionNote(params.studyId, params.checklistId, questionKey) } + getRobinsText={(sectionKey, fieldKey, questionKey) => + getRobinsText(params.studyId, params.checklistId, sectionKey, fieldKey, questionKey) + } /> diff --git a/packages/web/src/components/checklist/GenericChecklist.jsx b/packages/web/src/components/checklist/GenericChecklist.jsx index 4eadaf958..e2d527474 100644 --- a/packages/web/src/components/checklist/GenericChecklist.jsx +++ b/packages/web/src/components/checklist/GenericChecklist.jsx @@ -25,6 +25,7 @@ import { ROBINSIChecklist } from '@/components/checklist/ROBINSIChecklist/index. * @param {Function} props.onUpdate - Callback for checklist updates * @param {boolean} [props.readOnly] - Whether the checklist is read-only * @param {Function} [props.getQuestionNote] - Function to get Y.Text for a question note + * @param {Function} [props.getRobinsText] - Function to get Y.Text for a ROBINS-I free-text field */ export default function GenericChecklist(props) { // Determine the checklist type from props or state @@ -55,6 +56,7 @@ export default function GenericChecklist(props) { showComments={true} showLegend={true} readOnly={props.readOnly} + getRobinsText={props.getRobinsText} /> diff --git a/packages/web/src/components/checklist/ROBINSIChecklist/DomainSection.jsx b/packages/web/src/components/checklist/ROBINSIChecklist/DomainSection.jsx index 72f32ce37..f367ac63c 100644 --- a/packages/web/src/components/checklist/ROBINSIChecklist/DomainSection.jsx +++ b/packages/web/src/components/checklist/ROBINSIChecklist/DomainSection.jsx @@ -13,6 +13,7 @@ import { DomainJudgement, JudgementBadge } from './DomainJudgement.jsx'; * @param {boolean} [props.showComments] - Whether to show comment fields * @param {boolean} [props.collapsed] - Whether the domain is collapsed * @param {Function} [props.onToggleCollapse] - Callback to toggle collapse + * @param {Function} [props.getRobinsText] - Function to get Y.Text for a ROBINS-I free-text field */ export function DomainSection(props) { const domain = () => ROBINS_I_CHECKLIST[props.domainKey]; @@ -113,6 +114,9 @@ export function DomainSection(props) { onUpdate={newAnswer => handleQuestionUpdate(qKey, newAnswer)} disabled={props.disabled} showComment={props.showComments} + domainKey={props.domainKey} + questionKey={qKey} + getRobinsText={props.getRobinsText} /> )} diff --git a/packages/web/src/components/checklist/ROBINSIChecklist/PlanningSection.jsx b/packages/web/src/components/checklist/ROBINSIChecklist/PlanningSection.jsx index 87efcd02a..9d8c5dfa7 100644 --- a/packages/web/src/components/checklist/ROBINSIChecklist/PlanningSection.jsx +++ b/packages/web/src/components/checklist/ROBINSIChecklist/PlanningSection.jsx @@ -1,4 +1,5 @@ import { PLANNING_SECTION } from './checklist-map.js'; +import NoteEditor from '@/components/checklist/common/NoteEditor.jsx'; /** * Planning Section: List confounding factors at planning stage @@ -6,18 +7,15 @@ import { PLANNING_SECTION } from './checklist-map.js'; * @param {Object} props.planningState - Current planning state { confoundingFactors } * @param {Function} props.onUpdate - Callback when planning state changes * @param {boolean} [props.disabled] - Whether the section is disabled + * @param {Function} [props.getRobinsText] - Function to get Y.Text for a ROBINS-I free-text field */ export function PlanningSection(props) { const p1Field = PLANNING_SECTION.p1; - function handleFieldChange(value) { - props.onUpdate({ - ...props.planningState, - [p1Field.stateKey]: value, - }); - } - - const value = () => props.planningState?.[p1Field.stateKey] || ''; + const yText = () => { + if (!props.getRobinsText) return null; + return props.getRobinsText('planning', 'confoundingFactors'); + }; return (
@@ -33,14 +31,14 @@ export function PlanningSection(props) { {p1Field.label}. {p1Field.text} -