From 239519244cb4bc504b28aa764d0842d6ebd18176 Mon Sep 17 00:00:00 2001 From: Jacob Maynard Date: Sat, 17 Jan 2026 13:47:49 -0600 Subject: [PATCH 01/26] add more profile options, begin ui migration to a more shadcn like approach --- eslint.config.js | 15 + packages/web/package.json | 4 + .../web/src/components/admin/GrantDialog.jsx | 119 +++-- .../web/src/components/admin/OrgDetail.jsx | 98 ++-- .../src/components/admin/ProjectDetail.jsx | 119 +++-- .../components/admin/StorageManagement.jsx | 77 ++-- .../components/admin/SubscriptionDialog.jsx | 293 ++++++------ .../web/src/components/admin/UserDetail.jsx | 172 ++++--- .../src/components/auth/CompleteProfile.jsx | 436 ++++++++++++++---- .../web/src/components/auth/RoleSelector.jsx | 12 + .../src/components/billing/PricingTable.jsx | 202 ++++---- .../checklist/ChecklistYjsWrapper.jsx | 59 ++- .../checklist/LocalAppraisalsPanel.jsx | 63 ++- .../ROB2Checklist/ScoringSummary.jsx | 110 ++--- .../ROBINSIChecklist/ScoringSummary.jsx | 106 ++--- .../dashboard/LocalAppraisalsSection.jsx | 63 ++- .../components/dashboard/ProjectsSection.jsx | 73 ++- .../web/src/components/pdf/PdfListItem.jsx | 71 ++- .../src/components/profile/ProfilePage.jsx | 206 ++++++++- .../src/components/project/ProjectsPanel.jsx | 75 ++- .../all-studies-tab/AssignReviewersModal.jsx | 152 +++--- .../all-studies-tab/EditPdfMetadataModal.jsx | 222 +++++---- .../completed-tab/PreviousReviewersView.jsx | 125 +++-- .../google-drive/GoogleDrivePickerModal.jsx | 80 ++-- .../project/overview-tab/OverviewTab.jsx | 73 ++- .../ChecklistReconciliation.jsx | 62 ++- .../rob2-reconcile/ROB2Reconciliation.jsx | 61 ++- .../RobinsIReconciliation.jsx | 62 ++- .../settings/pages/LinkedAccountsSection.jsx | 96 ++-- .../settings/pages/MergeAccountsDialog.jsx | 38 +- .../settings/pages/SessionManagement.jsx | 40 +- .../web/src/components/sidebar/Sidebar.jsx | 103 +++-- .../web/src/components/ui/alert-dialog.tsx | 295 ++++++++++++ packages/web/src/components/ui/avatar.tsx | 87 ++++ packages/web/src/components/ui/button.tsx | 68 +++ packages/web/src/components/ui/checkbox.tsx | 107 +++++ packages/web/src/components/ui/cn.ts | 15 + .../web/src/components/ui/collapsible.tsx | 92 ++++ packages/web/src/components/ui/dialog.tsx | 214 +++++++++ packages/web/src/components/ui/index.ts | 36 ++ packages/web/src/components/ui/menu.tsx | 193 ++++++++ packages/web/src/components/ui/popover.tsx | 193 ++++++++ packages/web/src/components/ui/select.tsx | 264 +++++++++++ packages/web/src/components/ui/spinner.tsx | 120 +++++ packages/web/src/components/ui/steps.tsx | 229 +++++++++ packages/web/src/components/ui/switch.tsx | 113 +++++ packages/web/src/components/ui/tabs.tsx | 130 ++++++ packages/web/src/components/ui/toast.tsx | 209 +++++++++ packages/web/src/components/ui/tooltip.tsx | 116 +++++ packages/web/src/components/ui/z-index.ts | 47 ++ packages/web/src/global.css | 5 +- packages/web/src/styles/ark-ui.css | 131 ++++++ packages/web/tsconfig.json | 38 ++ ...ql => 0000_ambitious_mikhail_rasputin.sql} | 9 + .../migrations/meta/0000_snapshot.json | 259 +++++++++-- .../workers/migrations/meta/_journal.json | 6 +- packages/workers/src/auth/config.ts | 36 ++ packages/workers/src/db/schema.ts | 11 + pnpm-lock.yaml | 19 + 59 files changed, 5315 insertions(+), 1214 deletions(-) create mode 100644 packages/web/src/components/ui/alert-dialog.tsx create mode 100644 packages/web/src/components/ui/avatar.tsx create mode 100644 packages/web/src/components/ui/button.tsx create mode 100644 packages/web/src/components/ui/checkbox.tsx create mode 100644 packages/web/src/components/ui/cn.ts create mode 100644 packages/web/src/components/ui/collapsible.tsx create mode 100644 packages/web/src/components/ui/dialog.tsx create mode 100644 packages/web/src/components/ui/index.ts create mode 100644 packages/web/src/components/ui/menu.tsx create mode 100644 packages/web/src/components/ui/popover.tsx create mode 100644 packages/web/src/components/ui/select.tsx create mode 100644 packages/web/src/components/ui/spinner.tsx create mode 100644 packages/web/src/components/ui/steps.tsx create mode 100644 packages/web/src/components/ui/switch.tsx create mode 100644 packages/web/src/components/ui/tabs.tsx create mode 100644 packages/web/src/components/ui/toast.tsx create mode 100644 packages/web/src/components/ui/tooltip.tsx create mode 100644 packages/web/src/components/ui/z-index.ts create mode 100644 packages/web/src/styles/ark-ui.css create mode 100644 packages/web/tsconfig.json rename packages/workers/migrations/{0000_amusing_black_panther.sql => 0000_ambitious_mikhail_rasputin.sql} (97%) diff --git a/eslint.config.js b/eslint.config.js index cbd0fffee..38629c55e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -258,6 +258,21 @@ export default [ 'no-restricted-imports': 'off', }, }, + { + // Web UI components can import from @ark-ui/solid directly (shadcn-style primitives) + files: ['packages/web/src/components/ui/**/*.{js,jsx,ts,tsx}'], + rules: { + 'no-restricted-imports': 'off', + 'corates/corates-ui-imports': 'off', + }, + }, + { + // Web lib/ui can import from @ark-ui/solid directly + files: ['packages/web/src/lib/ui/**/*.{js,jsx,ts,tsx}'], + rules: { + 'no-restricted-imports': 'off', + }, + }, { // Test file configuration files: [ diff --git a/packages/web/package.json b/packages/web/package.json index 5c6d6804e..4ea1e8ce5 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -17,6 +17,7 @@ "analyze": "vite build --mode analyze && source-map-explorer dist/assets/index-*.js --no-border-checks" }, "dependencies": { + "@ark-ui/solid": "^5.30.0", "@corates/shared": "workspace:*", "@corates/ui": "workspace:*", "@embedpdf/core": "^2.2.0", @@ -53,6 +54,8 @@ "@tanstack/solid-table": "^8.21.3", "better-auth": "^1.4.12", "chart.js": "^4.5.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "d3": "^7.9.0", "dexie": "^4.2.1", "preact": "^10.28.2", @@ -78,6 +81,7 @@ "source-map-explorer": "^2.5.3", "tailwindcss": "^4.1.18", "terser": "^5.44.1", + "typescript": "^5.9.3", "vite": "^7.3.1", "vite-plugin-solid": "^2.11.10", "vitest": "^4.0.17" diff --git a/packages/web/src/components/admin/GrantDialog.jsx b/packages/web/src/components/admin/GrantDialog.jsx index 402c6182a..fb6bd0cf6 100644 --- a/packages/web/src/components/admin/GrantDialog.jsx +++ b/packages/web/src/components/admin/GrantDialog.jsx @@ -3,7 +3,17 @@ * Dialog for creating grants */ -import { Dialog } from '@corates/ui'; +import { + Dialog, + DialogBackdrop, + DialogPositioner, + DialogContent, + DialogHeader, + DialogTitle, + DialogBody, + DialogCloseTrigger, +} from '@/components/ui/dialog'; +import { FiX } from 'solid-icons/fi'; /** * Grant Dialog component @@ -29,53 +39,66 @@ export default function GrantDialog(props) { const expiresAt = () => props.expiresAt; return ( - -
-
- - -
-
- - props.onStartsAtChange?.(e.target.value)} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- - props.onExpiresAtChange?.(e.target.value)} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- - -
-
+ props.onOpenChange?.(details.open)}> + + + + + Create Grant + + + + + +
+
+ + +
+
+ + props.onStartsAtChange?.(e.target.value)} + class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ + props.onExpiresAtChange?.(e.target.value)} + class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ + Cancel + + +
+
+
+
+
); } diff --git a/packages/web/src/components/admin/OrgDetail.jsx b/packages/web/src/components/admin/OrgDetail.jsx index ffa8d5472..43383f9d2 100644 --- a/packages/web/src/components/admin/OrgDetail.jsx +++ b/packages/web/src/components/admin/OrgDetail.jsx @@ -6,7 +6,7 @@ import { createSignal, Show } from 'solid-js'; import { useParams, A } from '@solidjs/router'; -import { FiArrowLeft, FiHome, FiUsers, FiFolder, FiShield, FiLoader } from 'solid-icons/fi'; +import { FiArrowLeft, FiHome, FiUsers, FiFolder, FiShield, FiLoader, FiX } from 'solid-icons/fi'; import { useAdminOrgDetails, useAdminOrgBilling } from '@primitives/useAdminQueries.js'; import { createOrgSubscription, @@ -19,7 +19,17 @@ import { isAdminChecked, isAdmin, } from '@/stores/adminStore.js'; -import { Dialog, showToast } from '@corates/ui'; +import { showToast } from '@corates/ui'; +import { + Dialog, + DialogBackdrop, + DialogPositioner, + DialogContent, + DialogHeader, + DialogTitle, + DialogBody, + DialogCloseTrigger, +} from '@/components/ui/dialog'; import { handleError } from '@/lib/error-utils.js'; import { AdminBox } from './ui/index.js'; import OrgBillingSummary from './OrgBillingSummary.jsx'; @@ -447,43 +457,53 @@ export default function OrgDetail() { /> {/* Confirm Dialog */} - !open && setConfirmDialog(null)} - title={ - confirmDialog()?.type === 'cancel-subscription' ? 'Cancel Subscription' : 'Revoke Grant' - } - role='alertdialog' - > -
-

- {confirmDialog()?.type === 'cancel-subscription' ? - 'Are you sure you want to cancel this subscription?' - : 'Are you sure you want to revoke this grant?'} -

-
- - -
-
+ !open && setConfirmDialog(null)}> + + + + + + {confirmDialog()?.type === 'cancel-subscription' ? + 'Cancel Subscription' + : 'Revoke Grant'} + + + + + + +
+

+ {confirmDialog()?.type === 'cancel-subscription' ? + 'Are you sure you want to cancel this subscription?' + : 'Are you sure you want to revoke this grant?'} +

+
+ + +
+
+
+
+
diff --git a/packages/web/src/components/admin/ProjectDetail.jsx b/packages/web/src/components/admin/ProjectDetail.jsx index 9030a263f..44377f152 100644 --- a/packages/web/src/components/admin/ProjectDetail.jsx +++ b/packages/web/src/components/admin/ProjectDetail.jsx @@ -20,6 +20,7 @@ import { FiCopy, FiHardDrive, FiUserMinus, + FiX, } from 'solid-icons/fi'; import { useAdminProjectDetails } from '@primitives/useAdminQueries.js'; import { @@ -28,7 +29,17 @@ import { isAdminChecked, isAdmin, } from '@/stores/adminStore.js'; -import { Dialog, Avatar, showToast } from '@corates/ui'; +import { Avatar, showToast } from '@corates/ui'; +import { + Dialog, + DialogBackdrop, + DialogPositioner, + DialogContent, + DialogHeader, + DialogTitle, + DialogBody, + DialogCloseTrigger, +} from '@/components/ui/dialog'; import { handleError } from '@/lib/error-utils.js'; import { AdminBox } from './ui/index.js'; import { table } from './styles/admin-tokens.js'; @@ -507,55 +518,63 @@ export default function ProjectDetail() { {/* Confirm Dialog */} - !open && setConfirmDialog(null)} - title={ - confirmDialog()?.type === 'delete-project' ? 'Delete Project' - : confirmDialog()?.type === 'remove-member' ? - 'Remove Member' - : 'Confirm' - } - role='alertdialog' - > -
- -

- This will permanently delete the project and all associated data including files, - members, and invitations. This action cannot be undone. -

-
- -

- Are you sure you want to remove{' '} - - {confirmDialog()?.member?.userDisplayName || - confirmDialog()?.member?.userName || - confirmDialog()?.member?.userEmail} - {' '} - from this project? -

-
-
- - -
-
+ !open && setConfirmDialog(null)}> + + + + + + {confirmDialog()?.type === 'delete-project' ? 'Delete Project' + : confirmDialog()?.type === 'remove-member' ? + 'Remove Member' + : 'Confirm'} + + + + + + +
+ +

+ This will permanently delete the project and all associated data including files, + members, and invitations. This action cannot be undone. +

+
+ +

+ Are you sure you want to remove{' '} + + {confirmDialog()?.member?.userDisplayName || + confirmDialog()?.member?.userName || + confirmDialog()?.member?.userEmail} + {' '} + from this project? +

+
+
+ + +
+
+
+
+
diff --git a/packages/web/src/components/admin/StorageManagement.jsx b/packages/web/src/components/admin/StorageManagement.jsx index be249acdf..e0b9406ef 100644 --- a/packages/web/src/components/admin/StorageManagement.jsx +++ b/packages/web/src/components/admin/StorageManagement.jsx @@ -13,10 +13,21 @@ import { FiCheckSquare, FiSquare, FiLoader, + FiX, } from 'solid-icons/fi'; import { deleteStorageDocuments } from '@/stores/adminStore.js'; import { useStorageDocuments } from '@primitives/useAdminQueries.js'; -import { Dialog, showToast } from '@corates/ui'; +import { showToast } from '@corates/ui'; +import { + Dialog, + DialogBackdrop, + DialogPositioner, + DialogContent, + DialogHeader, + DialogTitle, + DialogBody, + DialogCloseTrigger, +} from '@/components/ui/dialog'; import { DashboardHeader, AdminSection, AdminBox } from './ui/index.js'; import { input, table } from './styles/admin-tokens.js'; @@ -405,34 +416,42 @@ export default function StorageManagement() { {/* Delete Confirmation Dialog */} - !open && setDeleteDialog(null)} - title='Delete Documents' - role='alertdialog' - > -
-

- Are you sure you want to delete {deleteDialog()?.length || 0} document - {deleteDialog()?.length === 1 ? '' : 's'}? This action cannot be undone. -

-
- - -
-
+ !open && setDeleteDialog(null)}> + + + + + Delete Documents + + + + + +
+

+ Are you sure you want to delete {deleteDialog()?.length || 0} document + {deleteDialog()?.length === 1 ? '' : 's'}? This action cannot be undone. +

+
+ + +
+
+
+
+
); diff --git a/packages/web/src/components/admin/SubscriptionDialog.jsx b/packages/web/src/components/admin/SubscriptionDialog.jsx index e259adce4..2b49ffa2c 100644 --- a/packages/web/src/components/admin/SubscriptionDialog.jsx +++ b/packages/web/src/components/admin/SubscriptionDialog.jsx @@ -3,7 +3,17 @@ * Dialog for creating and editing subscriptions */ -import { Dialog } from '@corates/ui'; +import { + Dialog, + DialogBackdrop, + DialogPositioner, + DialogContent, + DialogHeader, + DialogTitle, + DialogBody, + DialogCloseTrigger, +} from '@/components/ui/dialog'; +import { FiX } from 'solid-icons/fi'; /** * Subscription Dialog component @@ -63,138 +73,155 @@ export default function SubscriptionDialog(props) { }; return ( - -
-
- - -
-
- - -
-
- - props.onPeriodStartChange?.(e.target.value)} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- - props.onPeriodEndChange?.(e.target.value)} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- props.onCancelAtPeriodEndChange?.(e.target.checked)} - class='h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500' - /> - -
-
- - - props.onCanceledAtChange?.(e.target.value ? new Date(e.target.value) : null) - } - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- - props.onEndedAtChange?.(e.target.value ? new Date(e.target.value) : null)} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- - props.onStripeCustomerIdChange?.(e.target.value)} - placeholder='cus_...' - class='w-full rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- - props.onStripeSubscriptionIdChange?.(e.target.value)} - placeholder='sub_...' - class='w-full rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' - /> -
-
- - -
-
+ + + + + + {isEdit() ? 'Edit Subscription' : 'Create Subscription'} + + + + + +
+
+ + +
+
+ + +
+
+ + props.onPeriodStartChange?.(e.target.value)} + class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ + props.onPeriodEndChange?.(e.target.value)} + class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ props.onCancelAtPeriodEndChange?.(e.target.checked)} + class='h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500' + /> + +
+
+ + + props.onCanceledAtChange?.(e.target.value ? new Date(e.target.value) : null) + } + class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ + + props.onEndedAtChange?.(e.target.value ? new Date(e.target.value) : null) + } + class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ + props.onStripeCustomerIdChange?.(e.target.value)} + placeholder='cus_...' + class='w-full rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ + props.onStripeSubscriptionIdChange?.(e.target.value)} + placeholder='sub_...' + class='w-full rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none' + /> +
+
+ + +
+
+
+
+
); } diff --git a/packages/web/src/components/admin/UserDetail.jsx b/packages/web/src/components/admin/UserDetail.jsx index beaf3d7ed..35266a17c 100644 --- a/packages/web/src/components/admin/UserDetail.jsx +++ b/packages/web/src/components/admin/UserDetail.jsx @@ -24,6 +24,7 @@ import { FiCheckCircle, FiCopy, FiExternalLink, + FiX, } from 'solid-icons/fi'; import { useAdminUserDetails } from '@primitives/useAdminQueries.js'; import { @@ -36,7 +37,17 @@ import { isAdminChecked, isAdmin, } from '@/stores/adminStore.js'; -import { Dialog, Avatar, showToast } from '@corates/ui'; +import { Avatar, showToast } from '@corates/ui'; +import { + Dialog, + DialogBackdrop, + DialogPositioner, + DialogContent, + DialogHeader, + DialogTitle, + DialogBody, + DialogCloseTrigger, +} from '@/components/ui/dialog'; import { handleError } from '@/lib/error-utils.js'; import { AdminBox } from './ui/index.js'; import { table } from './styles/admin-tokens.js'; @@ -631,77 +642,100 @@ export default function UserDetail() { {/* Ban Dialog */} - -
-

- This will ban the user and revoke all their sessions. -

-
- -