feat: implement data source and data view management features#5
feat: implement data source and data view management features#5
Conversation
- Removed `@nanostores/query` dependency and replaced it with SWR for data fetching. - Added new API endpoints for creating, retrieving, and updating data sources and data views. - Introduced new hooks for managing data sources and data views using SWR. - Updated page and view components to integrate new data management features. - Enhanced error handling and validation for API interactions.
WalkthroughReplaces nanostores/nanoquery fetching with SWR hooks, adds DataView and DataSource APIs and schemas, updates many UI components to use SWR hooks, introduces new repositories/entities for DataView, and adds tooling/editor config and lint-staged integration. Changes
Sequence Diagram(s)sequenceDiagram
participant Component
participant SWRHook as SWR Hook
participant swrFetcher
participant apiClient as API Client
participant Server as API Server
Component->>SWRHook: usePageDetails(id)
SWRHook->>swrFetcher: swrFetcher("/pages/{id}?includeBlocks=true")
swrFetcher->>apiClient: apiClient.get(url)
apiClient->>Server: HTTP GET
Server-->>apiClient: { data: { data: ... } }
apiClient-->>swrFetcher: r.data.data
swrFetcher-->>SWRHook: data
SWRHook-->>Component: { data, error, isLoading, mutate }
sequenceDiagram
participant User
participant ViewModal as ViewCreator Modal
participant DataSourceSel as DataSourceSelector
participant API
participant PageComp as PageComponent
User->>ViewModal: Open "Add View"
ViewModal->>DataSourceSel: Step 1 select/create data source
alt create new
DataSourceSel->>API: POST /data-sources
API-->>DataSourceSel: new data source
end
DataSourceSel-->>ViewModal: onSelect(dataSource)
User->>ViewModal: Enter view name & submit
ViewModal->>API: POST /views { name, dataSourceId, pageId }
API-->>ViewModal: created view
ViewModal->>PageComp: onCreated -> mutate() + navigate to view
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (8)
src/app/globals.css (1)
13-15: Consider using modern layout methods instead offloat.The new
.is-pulled-rightutility usesfloat: right;, which is a legacy layout approach. For better alignment with modern CSS practices, consider using flexbox (margin-left: auto;in a flex context) or grid-based positioning depending on the layout context.Without visibility into where this class is being used, I cannot definitively recommend a refactor. If you're refactoring UI components as part of this PR, it would be worth verifying whether a more modern layout utility would be better suited or if this aligns with existing layout patterns in the codebase.
Could you clarify where
.is-pulled-rightis being used? This will help determine if an alternative approach would be more appropriate.src/lib/api/server/get-workspace.ts (1)
4-13: Consider adding input validation for userId.The function logic is correct and follows the repository pattern. However, consider validating that
userIdis not empty before querying the repository to fail fast on invalid input.Apply this diff to add validation:
export async function getWorkspace(userId: string) { + if (!userId) { + throw new BadRequestError('User ID is required'); + } const workspaceRepository = await getWorkspaceRepository(); const workspace = await workspaceRepository.getById(userId);src/lib/hooks/api/use-page-details.ts (1)
5-6: Consider makingincludeBlocksparameter configurable.The hook hardcodes
includeBlocks=true, which may limit flexibility. Consider making this configurable to support use cases where blocks are not needed.Apply this diff to make it configurable:
-export const usePageDetails = (pageId: string | null) => - useSWR<GetPageDetailsResponse>(pageId ? `/pages/${pageId}?includeBlocks=true` : null, swrFetcher); +export const usePageDetails = (pageId: string | null, includeBlocks = true) => + useSWR<GetPageDetailsResponse>( + pageId ? `/pages/${pageId}?includeBlocks=${includeBlocks}` : null, + swrFetcher + );src/lib/swr/fetcher.ts (1)
3-3: Consider adding return type annotation for better type safety.The fetcher implementation is correct and follows the expected pattern for unwrapping axios response data. However, adding an explicit return type would improve type safety and documentation.
Apply this diff to add return type annotation:
-export const swrFetcher = (url: string) => apiClient.get(url).then((r) => r.data.data); +export const swrFetcher = <T = unknown>(url: string): Promise<T> => + apiClient.get(url).then((r) => r.data.data);src/lib/hooks/api/use-pages.ts (1)
5-31: Consider URL-encoding query parameters.The query parameters are directly interpolated into the URL without encoding. While this may work for typical IDs, it's safer to use
URLSearchParamsorencodeURIComponentto handle special characters.Apply this diff to add URL encoding:
export function usePagesByParent(parentId: string | null) { const { data, error, isLoading, mutate } = useSWR<GetPagesResponse>( - parentId ? `${GET_PAGES_ENDPOINT}?parentId=${parentId}` : null, + parentId ? `${GET_PAGES_ENDPOINT}?parentId=${encodeURIComponent(parentId)}` : null, swrFetcher ); return { data, error, isLoading, mutate, }; } export function usePagesByDataSource(dataSourceId: string | null) { const { data, error, isLoading, mutate } = useSWR<GetPagesResponse>( - dataSourceId ? `${GET_PAGES_ENDPOINT}?dataSourceId=${dataSourceId}` : null, + dataSourceId ? `${GET_PAGES_ENDPOINT}?dataSourceId=${encodeURIComponent(dataSourceId)}` : null, swrFetcher ); return { data, error, isLoading, mutate, }; }src/components/organisms/data-view-render.tsx (1)
30-30: Consider implementing optimistic updates.The TODO comment suggests implementing inline mutation for better UX. This would show the new page immediately before the server confirms.
Would you like me to generate code for optimistic updates using SWR's mutate with optimistic data?
src/lib/database/index.ts (1)
2-2: Consider renaming to avoid shadowing the globalDataView.The name
DataViewconflicts with JavaScript's built-inDataViewclass (used for binary data in ArrayBuffers). While the context makes the distinction clear, this could cause confusion if the built-inDataViewis needed in this file or related modules.Consider renaming to
DataViewEntityorViewEntityacross the codebase to eliminate ambiguity. As per static analysis tools.src/components/organisms/view-creator.tsx (1)
33-49: Consider removingviewFormfrom the dependency array.The
viewFormobject fromuseFormhas a stable reference and doesn't typically need to be included in the dependency array. Including it may cause unnecessary re-renders without adding safety.Apply this diff:
[post, selectedDataSource, onCreated, pageId, viewForm] + [post, selectedDataSource, onCreated, pageId]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (55)
.github/workflows/ci.yaml(1 hunks).husky/pre-commit(1 hunks).vscode/settings.json(1 hunks)lint-staged.config.js(1 hunks)package.json(2 hunks)src/app/api/v1/data-sources/[id]/route.ts(1 hunks)src/app/api/v1/data-sources/route.ts(1 hunks)src/app/api/v1/pages/[id]/blocks/route.ts(1 hunks)src/app/api/v1/pages/[id]/route.ts(1 hunks)src/app/api/v1/pages/route.ts(2 hunks)src/app/api/v1/pages/tree/route.ts(2 hunks)src/app/api/v1/views/[id]/route.ts(1 hunks)src/app/api/v1/views/route.ts(1 hunks)src/app/globals.css(1 hunks)src/app/pages/[id]/page.tsx(4 hunks)src/components/layout.tsx(1 hunks)src/components/molecules/sidebar/logged-in-container.tsx(2 hunks)src/components/molecules/view-creator/data-source-selector.tsx(1 hunks)src/components/organisms/data-view-render.tsx(1 hunks)src/components/organisms/page-detail-editor.tsx(1 hunks)src/components/organisms/view-creator.tsx(1 hunks)src/lib/api/server/get-workspace.ts(1 hunks)src/lib/auth/config.ts(3 hunks)src/lib/database/entities/data-view.ts(1 hunks)src/lib/database/entities/index.ts(1 hunks)src/lib/database/index.ts(3 hunks)src/lib/errors/bad-request-error.ts(1 hunks)src/lib/hooks/api/use-data-sources.ts(1 hunks)src/lib/hooks/api/use-page-blocks.ts(1 hunks)src/lib/hooks/api/use-page-details.ts(1 hunks)src/lib/hooks/api/use-pages-tree.ts(1 hunks)src/lib/hooks/api/use-pages.ts(1 hunks)src/lib/store/fetcher.ts(0 hunks)src/lib/store/query/get-page-blocks.ts(0 hunks)src/lib/store/query/get-page-details.ts(0 hunks)src/lib/store/query/get-pages-tree.ts(0 hunks)src/lib/swr/config.ts(1 hunks)src/lib/swr/fetcher.ts(1 hunks)src/types/api/endpoints/create-data-source.ts(1 hunks)src/types/api/endpoints/create-data-view.ts(1 hunks)src/types/api/endpoints/get-data-source.ts(1 hunks)src/types/api/endpoints/get-data-sources.ts(1 hunks)src/types/api/endpoints/get-data-view.ts(1 hunks)src/types/api/endpoints/get-data-views.ts(1 hunks)src/types/api/endpoints/get-page-details.ts(2 hunks)src/types/api/endpoints/get-pages.ts(1 hunks)src/types/api/endpoints/index.ts(1 hunks)src/types/api/endpoints/update-data-source.ts(1 hunks)src/types/api/endpoints/update-data-view.ts(1 hunks)src/types/api/entities.ts(1 hunks)src/types/database/index.ts(2 hunks)src/types/schemas/entities/container.ts(1 hunks)src/types/schemas/entities/data-view.ts(1 hunks)src/types/schemas/entities/index.ts(1 hunks)tsconfig.json(1 hunks)
💤 Files with no reviewable changes (4)
- src/lib/store/query/get-page-blocks.ts
- src/lib/store/fetcher.ts
- src/lib/store/query/get-pages-tree.ts
- src/lib/store/query/get-page-details.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Prefer TypeScript type aliases (
type) overinterfacewhen defining shapesPrefer type aliases over interface in TypeScript.
Files:
src/lib/database/entities/index.tssrc/components/organisms/page-detail-editor.tsxsrc/lib/hooks/api/use-page-blocks.tssrc/types/schemas/entities/data-view.tssrc/types/api/endpoints/get-pages.tssrc/types/api/endpoints/get-data-views.tssrc/types/api/endpoints/get-data-sources.tssrc/components/layout.tsxsrc/lib/hooks/api/use-data-sources.tssrc/lib/hooks/api/use-page-details.tssrc/types/schemas/entities/index.tssrc/app/api/v1/data-sources/[id]/route.tssrc/lib/api/server/get-workspace.tssrc/types/api/endpoints/update-data-view.tssrc/lib/errors/bad-request-error.tssrc/lib/swr/fetcher.tssrc/lib/database/entities/data-view.tssrc/app/api/v1/data-sources/route.tssrc/types/api/endpoints/create-data-source.tssrc/components/organisms/data-view-render.tsxsrc/types/api/endpoints/create-data-view.tssrc/lib/hooks/api/use-pages.tssrc/types/api/endpoints/index.tssrc/components/organisms/view-creator.tsxsrc/types/database/index.tssrc/types/schemas/entities/container.tssrc/types/api/endpoints/get-data-source.tssrc/app/api/v1/pages/[id]/route.tssrc/lib/auth/config.tssrc/lib/database/index.tssrc/components/molecules/view-creator/data-source-selector.tsxsrc/app/api/v1/views/route.tssrc/lib/swr/config.tssrc/components/molecules/sidebar/logged-in-container.tsxsrc/app/api/v1/pages/tree/route.tssrc/app/api/v1/pages/[id]/blocks/route.tssrc/app/api/v1/views/[id]/route.tssrc/types/api/endpoints/update-data-source.tssrc/lib/hooks/api/use-pages-tree.tssrc/types/api/entities.tssrc/types/api/endpoints/get-data-view.tssrc/types/api/endpoints/get-page-details.tssrc/app/pages/[id]/page.tsxsrc/app/api/v1/pages/route.ts
src/components/@(atoms|molecules|organisms|templates)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Organize UI components following Atomic Design: atoms, molecules, organisms, templates
Files:
src/components/organisms/page-detail-editor.tsxsrc/components/organisms/data-view-render.tsxsrc/components/organisms/view-creator.tsxsrc/components/molecules/view-creator/data-source-selector.tsxsrc/components/molecules/sidebar/logged-in-container.tsx
src/{components,app}/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Use Mantine UI 8 components for building React UI
Files:
src/components/organisms/page-detail-editor.tsxsrc/components/layout.tsxsrc/components/organisms/data-view-render.tsxsrc/components/organisms/view-creator.tsxsrc/components/molecules/view-creator/data-source-selector.tsxsrc/components/molecules/sidebar/logged-in-container.tsxsrc/app/pages/[id]/page.tsx
src/types/api/endpoints/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Define Zod schemas and exported types for new endpoints under src/types/api/endpoints/.
Files:
src/types/api/endpoints/get-pages.tssrc/types/api/endpoints/get-data-views.tssrc/types/api/endpoints/get-data-sources.tssrc/types/api/endpoints/update-data-view.tssrc/types/api/endpoints/create-data-source.tssrc/types/api/endpoints/create-data-view.tssrc/types/api/endpoints/index.tssrc/types/api/endpoints/get-data-source.tssrc/types/api/endpoints/update-data-source.tssrc/types/api/endpoints/get-data-view.tssrc/types/api/endpoints/get-page-details.ts
src/app/api/**/route.@(ts|tsx)
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Implement API endpoints as Next.js route handlers under
src/app/api/**/route.ts(x)
Files:
src/app/api/v1/data-sources/[id]/route.tssrc/app/api/v1/data-sources/route.tssrc/app/api/v1/pages/[id]/route.tssrc/app/api/v1/views/route.tssrc/app/api/v1/pages/tree/route.tssrc/app/api/v1/pages/[id]/blocks/route.tssrc/app/api/v1/views/[id]/route.tssrc/app/api/v1/pages/route.ts
src/app/api/**/route.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/app/api/**/route.ts: Place Next.js API route handlers at src/app/api//route.ts and export functions named GET, POST, etc.
Use apiRoute<Resp, Query, Body>(options, handler) in all API route handlers to enforce Zod validation and typed session.
In routes, import and use expectedQuerySchema/expectedBodySchema from src/types/api when validating input.
Obtain repositories via getWorkspaceRepository()/getContainerRepository() from src/lib/database in API routes.
Always scope database queries with addUserIdToQuery(..., session.user.id) to prevent cross-user data leaks.
Routes must only live under src/app/api/*; do not place route handlers elsewhere.
Files:
src/app/api/v1/data-sources/[id]/route.tssrc/app/api/v1/data-sources/route.tssrc/app/api/v1/pages/[id]/route.tssrc/app/api/v1/views/route.tssrc/app/api/v1/pages/tree/route.tssrc/app/api/v1/pages/[id]/blocks/route.tssrc/app/api/v1/views/[id]/route.tssrc/app/api/v1/pages/route.ts
src/app/**/@(page|layout|loading|error).tsx
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Define routes using Next.js App Router conventions under
src/appwithpage.tsx,layout.tsx,loading.tsx, anderror.tsx
Files:
src/app/pages/[id]/page.tsx
src/app/!(api)/**/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
On the client, use useAuth() to access user/session state via the auth provider.
Files:
src/app/pages/[id]/page.tsx
src/app/!(api)/**
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Front-end code should call backend endpoints through src/lib/api/client.ts rather than raw fetch/axios calls.
Files:
src/app/pages/[id]/page.tsx
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/lib/store/**/*.ts : Use Nanostores with the createFetcherStore helper for data fetching in stores.
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Run pnpm build, pnpm lint, and fix TypeScript errors (pnpm lint:tsc) before opening a PR.
Applied to files:
.github/workflows/ci.yaml.husky/pre-commit
📚 Learning: 2025-10-23T17:57:41.115Z
Learnt from: CR
PR: danships/thoth#0
File: .cursor/rules/basics.mdc:0-0
Timestamp: 2025-10-23T17:57:41.115Z
Learning: Applies to src/{components,app}/**/*.tsx : Use Mantine UI 8 components for building React UI
Applied to files:
package.jsonsrc/components/layout.tsx
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/types/api/endpoints/**/*.ts : Define Zod schemas and exported types for new endpoints under src/types/api/endpoints/.
Applied to files:
src/types/schemas/entities/data-view.tssrc/types/api/endpoints/get-pages.tssrc/types/api/endpoints/get-data-views.tssrc/types/api/endpoints/get-data-sources.tssrc/types/schemas/entities/index.tssrc/types/api/endpoints/update-data-view.tssrc/app/api/v1/data-sources/route.tssrc/types/api/endpoints/create-data-source.tssrc/types/api/endpoints/create-data-view.tssrc/types/api/endpoints/index.tssrc/types/database/index.tssrc/types/api/endpoints/get-data-source.tssrc/types/api/endpoints/update-data-source.tssrc/types/api/entities.tssrc/types/api/endpoints/get-data-view.tssrc/types/api/endpoints/get-page-details.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/lib/store/**/*.ts : Use Nanostores with the createFetcherStore helper for data fetching in stores.
Applied to files:
src/lib/hooks/api/use-data-sources.tssrc/lib/swr/fetcher.tssrc/lib/swr/config.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to **/*.{ts,tsx} : Prefer type aliases over interface in TypeScript.
Applied to files:
tsconfig.json
📚 Learning: 2025-10-23T17:57:41.115Z
Learnt from: CR
PR: danships/thoth#0
File: .cursor/rules/basics.mdc:0-0
Timestamp: 2025-10-23T17:57:41.115Z
Learning: Applies to src/components/@(atoms|molecules|organisms|templates)/**/*.{ts,tsx} : Organize UI components following Atomic Design: atoms, molecules, organisms, templates
Applied to files:
tsconfig.jsonsrc/components/organisms/view-creator.tsx
📚 Learning: 2025-10-23T17:57:41.115Z
Learnt from: CR
PR: danships/thoth#0
File: .cursor/rules/basics.mdc:0-0
Timestamp: 2025-10-23T17:57:41.115Z
Learning: Applies to **/*.{ts,tsx} : Prefer TypeScript type aliases (`type`) over `interface` when defining shapes
Applied to files:
tsconfig.json
📚 Learning: 2025-10-23T17:57:41.115Z
Learnt from: CR
PR: danships/thoth#0
File: .cursor/rules/basics.mdc:0-0
Timestamp: 2025-10-23T17:57:41.115Z
Learning: Applies to src/app/**/@(page|layout|loading|error).tsx : Define routes using Next.js App Router conventions under `src/app` with `page.tsx`, `layout.tsx`, `loading.tsx`, and `error.tsx`
Applied to files:
tsconfig.jsonsrc/app/api/v1/views/route.tssrc/app/pages/[id]/page.tsxsrc/app/api/v1/pages/route.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/app/api/**/route.ts : In routes, import and use expectedQuerySchema/expectedBodySchema from src/types/api when validating input.
Applied to files:
src/app/api/v1/data-sources/[id]/route.tssrc/app/api/v1/data-sources/route.tssrc/types/api/endpoints/create-data-source.tssrc/types/database/index.tssrc/app/api/v1/pages/[id]/route.tssrc/app/api/v1/views/route.tssrc/app/api/v1/views/[id]/route.tssrc/types/api/endpoints/update-data-source.tssrc/app/api/v1/pages/route.ts
📚 Learning: 2025-10-23T17:57:41.115Z
Learnt from: CR
PR: danships/thoth#0
File: .cursor/rules/basics.mdc:0-0
Timestamp: 2025-10-23T17:57:41.115Z
Learning: Applies to src/app/api/**/route.@(ts|tsx) : Implement API endpoints as Next.js route handlers under `src/app/api/**/route.ts(x)`
Applied to files:
src/app/api/v1/data-sources/[id]/route.tssrc/app/api/v1/data-sources/route.tssrc/app/api/v1/views/route.tssrc/app/api/v1/views/[id]/route.tssrc/app/api/v1/pages/route.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/app/api/**/route.ts : Place Next.js API route handlers at src/app/api/<route>/route.ts and export functions named GET, POST, etc.
Applied to files:
src/app/api/v1/data-sources/[id]/route.tssrc/app/api/v1/data-sources/route.tssrc/app/api/v1/pages/[id]/route.tssrc/app/api/v1/views/route.tssrc/app/api/v1/views/[id]/route.tssrc/app/api/v1/pages/route.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/app/api/**/route.ts : Obtain repositories via getWorkspaceRepository()/getContainerRepository() from src/lib/database in API routes.
Applied to files:
src/app/api/v1/data-sources/[id]/route.tssrc/lib/api/server/get-workspace.tssrc/app/api/v1/data-sources/route.tssrc/app/api/v1/pages/[id]/route.tssrc/lib/auth/config.tssrc/lib/database/index.tssrc/app/api/v1/views/route.tssrc/app/api/v1/pages/tree/route.tssrc/app/api/v1/views/[id]/route.tssrc/app/api/v1/pages/route.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/app/api/**/route.ts : Routes must only live under src/app/api/*; do not place route handlers elsewhere.
Applied to files:
src/app/api/v1/data-sources/[id]/route.tssrc/app/api/v1/data-sources/route.tssrc/app/api/v1/views/route.tssrc/app/api/v1/views/[id]/route.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/app/api/**/route.ts : Use apiRoute<Resp, Query, Body>(options, handler) in all API route handlers to enforce Zod validation and typed session.
Applied to files:
src/app/api/v1/data-sources/[id]/route.tssrc/app/api/v1/data-sources/route.tssrc/app/api/v1/views/route.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/app/api/**/route.ts : Always scope database queries with addUserIdToQuery(..., session.user.id) to prevent cross-user data leaks.
Applied to files:
src/app/api/v1/data-sources/route.tssrc/app/api/v1/pages/[id]/route.tssrc/app/api/v1/views/route.tssrc/app/api/v1/views/[id]/route.ts
📚 Learning: 2025-10-24T20:04:42.977Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:42.977Z
Learning: Applies to src/lib/api/client.ts : After implementing an endpoint, add a typed client helper in src/lib/api/client.ts (baseURL is /api/v1).
Applied to files:
src/types/api/endpoints/create-data-source.tssrc/types/api/endpoints/index.ts
🧬 Code graph analysis (38)
src/lib/hooks/api/use-page-blocks.ts (2)
src/types/api/endpoints/get-page-blocks.ts (1)
GetPageBlocksResponse(17-19)src/lib/swr/fetcher.ts (1)
swrFetcher(3-3)
src/types/schemas/entities/data-view.ts (3)
src/types/api/entities.ts (1)
dataViewSchema(17-23)src/types/database/index.ts (1)
dataViewSchema(35-35)src/types/schemas/utilities.ts (4)
withTrackUpdatesSchema(11-14)withWorkspaceIdSchema(20-22)withUserIdSchema(16-18)withIdSchema(3-5)
src/types/api/endpoints/get-pages.ts (2)
src/types/api/entities.ts (1)
pageSchema(5-12)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/types/api/endpoints/get-data-views.ts (2)
src/types/api/entities.ts (1)
dataViewSchema(17-23)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/types/api/endpoints/get-data-sources.ts (2)
src/types/api/entities.ts (1)
dataSourceSchema(26-31)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/lib/hooks/api/use-data-sources.ts (2)
src/types/api/endpoints/get-data-sources.ts (1)
GetDataSourcesResponse(10-10)src/lib/swr/fetcher.ts (1)
swrFetcher(3-3)
src/lib/hooks/api/use-page-details.ts (2)
src/types/api/endpoints/get-page-details.ts (1)
GetPageDetailsResponse(15-15)src/lib/swr/fetcher.ts (1)
swrFetcher(3-3)
src/app/api/v1/data-sources/[id]/route.ts (5)
src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/types/api/endpoints/get-data-source.ts (3)
GetDataSourceResponse(10-10)GetDataSourceParameters(17-17)getDataSourceParametersSchema(14-16)src/lib/database/index.ts (1)
getContainerRepository(22-25)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)src/types/api/endpoints/update-data-source.ts (5)
UpdateDataSourceResponse(22-22)UpdateDataSourceParameters(29-29)UpdateDataSourceBody(21-21)updateDataSourceBodySchema(9-13)updateDataSourceParametersSchema(26-28)
src/lib/api/server/get-workspace.ts (2)
src/lib/database/index.ts (1)
getWorkspaceRepository(27-30)src/lib/errors/not-found-error.ts (1)
NotFoundError(3-7)
src/types/api/endpoints/update-data-view.ts (3)
src/types/schemas/entities/data-view.ts (1)
dataViewSchema(4-13)src/types/api/endpoints/get-data-views.ts (1)
getDataViewsResponseSchema(15-15)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/lib/errors/bad-request-error.ts (1)
src/lib/errors/http-error.ts (1)
HttpError(1-17)
src/lib/swr/fetcher.ts (1)
src/lib/api/client.ts (1)
apiClient(3-8)
src/lib/database/entities/data-view.ts (3)
src/lib/database/entities/index.ts (4)
NAME(1-1)NAME(2-2)NAME(3-3)DataView(2-2)src/types/api/entities.ts (1)
DataView(24-24)src/types/database/index.ts (1)
DataView(39-39)
src/app/api/v1/data-sources/route.ts (6)
src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/lib/database/index.ts (2)
getContainerRepository(22-25)getWorkspaceRepository(27-30)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)src/types/api/endpoints/create-data-source.ts (3)
CreateDataSourceResponse(16-16)CreateDataSourceBody(15-15)createDataSourceBodySchema(10-12)src/lib/errors/not-found-error.ts (1)
NotFoundError(3-7)src/types/database/index.ts (1)
DataSourceContainerCreate(14-14)
src/types/api/endpoints/create-data-source.ts (3)
src/types/schemas/entities/container.ts (1)
dataSourceContainerSchema(30-34)src/types/api/endpoints/get-data-sources.ts (1)
getDataSourcesResponseSchema(9-9)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/components/organisms/data-view-render.tsx (5)
src/lib/database/entities/data-view.ts (1)
DataView(5-21)src/types/api/entities.ts (1)
DataView(24-24)src/lib/hooks/api/use-pages.ts (1)
usePagesByDataSource(19-31)src/lib/hooks/use-cud-api.ts (1)
useCudApi(5-44)src/types/api/endpoints/create-page.ts (3)
CreatePageResponse(12-12)CreatePageBody(22-22)CREATE_PAGE_ENDPOINT(6-6)
src/types/api/endpoints/create-data-view.ts (3)
src/types/schemas/entities/data-view.ts (1)
dataViewSchema(4-13)src/types/api/endpoints/get-data-views.ts (1)
getDataViewsResponseSchema(15-15)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/lib/hooks/api/use-pages.ts (2)
src/types/api/endpoints/get-pages.ts (2)
GetPagesResponse(19-19)GET_PAGES_ENDPOINT(5-5)src/lib/swr/fetcher.ts (1)
swrFetcher(3-3)
src/components/organisms/view-creator.tsx (5)
src/types/api/endpoints/get-data-views.ts (1)
GetDataViewsResponse(16-16)src/types/api/endpoints/get-data-sources.ts (1)
GetDataSourcesResponse(10-10)src/lib/hooks/use-cud-api.ts (1)
useCudApi(5-44)src/types/api/endpoints/create-data-view.ts (2)
CreateDataViewResponse(21-21)CreateDataViewBody(20-20)src/components/molecules/view-creator/data-source-selector.tsx (1)
DataSourceSelector(13-113)
src/types/database/index.ts (4)
src/types/schemas/entities/container.ts (3)
pageContainerSchema(20-28)dataSourceContainerSchema(30-34)containerSchema(11-18)src/types/api/entities.ts (2)
dataViewSchema(17-23)DataView(24-24)src/types/schemas/entities/data-view.ts (1)
dataViewSchema(4-13)src/lib/database/entities/data-view.ts (1)
DataView(5-21)
src/types/schemas/entities/container.ts (2)
src/types/schemas/utilities.ts (1)
withParentIdSchema(7-9)src/types/database/index.ts (2)
dataSourceContainerSchema(7-7)containerSchema(18-18)
src/types/api/endpoints/get-data-source.ts (2)
src/types/api/entities.ts (1)
dataSourceSchema(26-31)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/app/api/v1/pages/[id]/route.ts (5)
src/app/api/v1/pages/[id]/blocks/route.ts (1)
GET(12-31)src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/types/api/endpoints/get-page-details.ts (5)
GetPageDetailsResponse(15-15)GetPageDetailsQuery(26-26)GetPageDetailsParameters(21-21)getPageDetailsParametersSchema(18-20)getPageDetailsQuerySchema(23-25)src/lib/database/index.ts (2)
getContainerRepository(22-25)getDataViewRepository(32-35)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)
src/lib/auth/config.ts (1)
src/types/database/index.ts (1)
PageContainerCreate(10-10)
src/lib/database/index.ts (4)
src/lib/database/entities/data-view.ts (1)
DataView(5-21)src/lib/database/entities/index.ts (1)
DataView(2-2)src/types/api/entities.ts (1)
DataView(24-24)src/types/database/index.ts (1)
DataView(39-39)
src/components/molecules/view-creator/data-source-selector.tsx (4)
src/types/api/endpoints/get-data-sources.ts (1)
GetDataSourcesResponse(10-10)src/lib/hooks/api/use-data-sources.ts (1)
useDataSources(5-5)src/lib/hooks/use-cud-api.ts (1)
useCudApi(5-44)src/types/api/endpoints/create-data-source.ts (2)
CreateDataSourceResponse(16-16)CreateDataSourceBody(15-15)
src/app/api/v1/views/route.ts (7)
src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/types/api/endpoints/get-data-views.ts (3)
GetDataViewsResponse(16-16)GetDataViewsQuery(12-12)getDataViewsQuerySchema(9-11)src/lib/database/index.ts (3)
getDataViewRepository(32-35)getWorkspaceRepository(27-30)getContainerRepository(22-25)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)src/types/api/endpoints/create-data-view.ts (3)
CreateDataViewResponse(21-21)CreateDataViewBody(20-20)createDataViewBodySchema(10-17)src/lib/errors/not-found-error.ts (1)
NotFoundError(3-7)src/types/database/index.ts (2)
Container(20-20)PageContainer(9-9)
src/lib/swr/config.ts (1)
src/lib/swr/fetcher.ts (1)
swrFetcher(3-3)
src/components/molecules/sidebar/logged-in-container.tsx (2)
src/lib/hooks/api/use-pages-tree.ts (1)
usePagesTree(5-5)src/components/molecules/pages-tree.tsx (1)
PagesTree(9-25)
src/app/api/v1/pages/tree/route.ts (1)
src/types/api/entities.ts (1)
Page(13-13)
src/app/api/v1/views/[id]/route.ts (6)
src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/types/api/endpoints/get-data-view.ts (3)
GetDataViewResponse(15-15)GetDataViewParameters(22-22)getDataViewParametersSchema(19-21)src/lib/database/index.ts (2)
getDataViewRepository(32-35)getContainerRepository(22-25)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)src/types/api/endpoints/update-data-view.ts (5)
UpdateDataViewResponse(25-25)UpdateDataViewParameters(32-32)UpdateDataViewBody(24-24)updateDataViewBodySchema(16-21)updateDataViewParametersSchema(29-31)src/lib/errors/not-found-error.ts (1)
NotFoundError(3-7)
src/types/api/endpoints/update-data-source.ts (2)
src/types/schemas/entities/container.ts (1)
dataSourceContainerSchema(30-34)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/lib/hooks/api/use-pages-tree.ts (2)
src/types/api/endpoints/get-pages-tree.ts (2)
GetPagesTreeResponse(23-23)GET_PAGES_TREE_ENDPOINT(6-6)src/lib/swr/fetcher.ts (1)
swrFetcher(3-3)
src/types/api/entities.ts (3)
src/types/schemas/entities/container.ts (2)
pageContainerSchema(20-28)dataSourceContainerSchema(30-34)src/types/schemas/entities/data-view.ts (1)
dataViewSchema(4-13)src/lib/database/entities/data-view.ts (1)
DataView(5-21)
src/types/api/endpoints/get-data-view.ts (3)
src/types/api/entities.ts (1)
dataViewSchema(17-23)src/types/schemas/entities/data-view.ts (1)
dataViewSchema(4-13)src/types/api/utilities.ts (1)
DataWrapper(2-4)
src/types/api/endpoints/get-page-details.ts (3)
src/types/api/entities.ts (2)
pageSchema(5-12)dataViewSchema(17-23)src/types/api/endpoints/get-page-blocks.ts (1)
getPageBlocksResponseSchema(13-15)src/types/schemas/entities/data-view.ts (1)
dataViewSchema(4-13)
src/app/pages/[id]/page.tsx (5)
src/lib/hooks/api/use-page-details.ts (1)
usePageDetails(5-6)src/lib/hooks/use-cud-api.ts (1)
useCudApi(5-44)src/components/organisms/page-detail-editor.tsx (1)
PageDetailEditor(16-42)src/components/organisms/data-view-render.tsx (1)
DataViewRender(14-89)src/components/organisms/view-creator.tsx (1)
ViewCreator(13-75)
src/app/api/v1/pages/route.ts (5)
src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/types/api/endpoints/get-pages.ts (3)
GetPagesResponse(19-19)GetPagesQuery(16-16)getPagesQuerySchema(7-14)src/lib/database/index.ts (1)
getContainerRepository(22-25)src/lib/errors/bad-request-error.ts (1)
BadRequestError(3-7)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)
🪛 Biome (2.1.2)
src/lib/database/entities/data-view.ts
[error] 5-5: Do not shadow the global "DataView" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
src/components/organisms/data-view-render.tsx
[error] 5-5: Do not shadow the global "DataView" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
src/lib/database/index.ts
[error] 2-2: Do not shadow the global "DataView" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci
🔇 Additional comments (42)
src/components/layout.tsx (1)
47-47: LGTM! Minor type safety and loading optimization improvements.The changes correctly use a numeric
widthprop (matching the existing numericheight) and add eager loading for the header logo, which is appropriate for critical above-the-fold content.tsconfig.json (1)
23-26: LGTM!The new path aliases are well-structured and will simplify imports across the codebase. The mapping of
@components/*to./src/lib/components/*aligns with the library organization structure introduced in this PR..github/workflows/ci.yaml (1)
59-62: LGTM!The cache key update correctly aligns with the pnpm package manager. Using
pnpm-lock.yamlinstead ofpackage-lock.jsonensures proper cache invalidation when dependencies change..husky/pre-commit (1)
1-1: LGTM!The migration from
pnpm linttopnpm lint-stagedis appropriate and will improve pre-commit performance by only running checks on staged files rather than the entire codebase.package.json (2)
37-37: Migration from Nanostores to SWR looks good.The addition of SWR for data fetching aligns with the PR objectives. Note that this makes the retrieved learning about using "Nanostores with the createFetcherStore helper" obsolete.
Based on learnings (now obsolete due to this migration).
58-58: I've confirmed both versions are current. Now let me verify they're free from known vulnerabilities.Both dependency versions are current and secure.
The latest version of swr is 2.3.6 (tagged Aug 11, 2025), and no direct vulnerabilities have been found for lint-staged in Snyk's vulnerability database. The package.json already uses both latest versions. Version 16.2.0 removed chalk and debug after the supply chain vulnerability issues, and given the recent vulnerabilities in the npm ecosystem, the maintainers are being careful with dependency updates.
.vscode/settings.json (1)
1-19: LGTM!The VSCode settings provide a comprehensive configuration for automatic formatting and linting. The setup aligns well with the lint-staged configuration and ensures consistent code quality across the development workflow.
src/lib/api/server/get-workspace.ts (1)
1-3: LGTM!The imports are clean and follow the established patterns for database access and error handling.
src/lib/hooks/api/use-page-blocks.ts (1)
1-6: LGTM!The hook implementation is clean and follows SWR best practices. The conditional fetching pattern (null key when pageId is null) is correct.
src/lib/swr/config.ts (1)
3-8: LGTM!The SWR configuration is well-structured with sensible defaults. The
revalidateOnFocus: truesetting will keep data fresh when users return to the app, which is appropriate for a data management application.src/lib/auth/config.ts (1)
4-4: LGTM!The refactoring to use
PageContainerCreatetype improves type safety and consistency. AddingcreatedAtandlastUpdatedfields is appropriate and aligns with the pattern used for workspace creation above.Also applies to: 49-60
src/types/api/endpoints/get-pages.ts (1)
1-20: LGTM!The endpoint type definitions are well-structured and follow the coding guidelines. The query schema correctly enforces that at least one of
parentIdordataSourceIdmust be provided, which aligns with the business logic.src/types/api/endpoints/create-data-source.ts (1)
1-17: LGTM! Well-structured API contract definition.The schema definitions follow best practices:
- Correctly derives the body schema from the container schema
- Reuses the response schema from get-data-sources for consistency
- Properly exports all necessary types
src/components/organisms/data-view-render.tsx (1)
53-88: Well-implemented table with inline page creation.The table correctly:
- Renders existing pages with links
- Provides inline creation via Enter key
- Disables input during creation
- Handles the disabled state properly
src/types/schemas/entities/index.ts (1)
2-2: LGTM! Proper barrel export addition.The new data-view schema export follows the existing pattern.
src/lib/database/entities/index.ts (1)
2-2: LGTM! Consistent entity export.The DataView entity export follows the established pattern with the NAME alias.
src/components/organisms/page-detail-editor.tsx (1)
27-27: Improved editor responsiveness.Reducing the debounce delay from 5 seconds to 1.5 seconds provides better user feedback. The
flushOnUnmount: trueoption ensures changes are saved even if the user navigates away.src/app/api/v1/pages/[id]/blocks/route.ts (1)
28-28: Good defensive programming.The property existence check prevents potential runtime errors when the
blocksfield is conditionally included based on query parameters.src/components/molecules/sidebar/logged-in-container.tsx (1)
3-20: Clean migration to SWR-based data fetching.The component successfully migrates from the store-based approach to the new hook-based pattern, with proper loading states and conditional rendering.
src/lib/hooks/api/use-pages-tree.ts (1)
1-5: LGTM! Clean SWR hook implementation.The hook follows the established pattern for SWR-based data fetching with proper typing and the shared fetcher utility.
src/lib/errors/bad-request-error.ts (1)
1-7: LGTM!The BadRequestError class is well-implemented, correctly extends HttpError with appropriate defaults (400 status code, visibleError=true for client-facing errors).
src/types/schemas/entities/container.ts (1)
26-26: LGTM!The addition of the optional
viewsfield topageContainerSchemacorrectly supports the new data view associations with pages.src/types/api/entities.ts (1)
1-32: LGTM!The API entity schemas are well-structured, consistently using
.pick()to expose only the necessary fields from the underlying schemas. The type inference pattern is appropriate.src/lib/database/index.ts (2)
18-18: LGTM!The DataView entity registration is correctly integrated into the database initialization sequence.
32-35: LGTM!The
getDataViewRepository()function follows the established pattern used for other repository accessors.src/types/api/endpoints/get-data-sources.ts (1)
1-11: LGTM!The endpoint definition follows the established pattern with proper schema validation and type exports. The constant
GET_DATA_SOURCES_ENDPOINTis well-defined for reuse across the codebase.src/types/database/index.ts (1)
1-41: LGTM!The database type exports are well-organized and follow consistent patterns. The DataView schema integration aligns with the new entity support introduced in this PR.
Note: The
DataViewnaming concern (shadowing global) was already flagged insrc/lib/database/index.ts.src/lib/database/entities/data-view.ts (2)
7-13: Clarify the commented-out dataSource relation.The
dataSourcerelation is commented out but appears to be a logical connection given thedataSourceIdfield in the schema. Is this intentionally disabled or a TODO?Please confirm whether this relation should be:
- Enabled now (if the feature is ready)
- Tracked as a TODO with a comment explaining why it's deferred
- Removed entirely if not needed
If it should be enabled, apply this diff:
relations: [ - // { - // name: 'dataSource', - // field: 'dataSourceId', - // multiple: false, - // }, + { + name: 'dataSource', + field: 'dataSourceId', + multiple: false, + }, ],
15-20: LGTM!The
filterSortFieldsare properly defined with appropriate types for all searchable/sortable fields.src/components/organisms/view-creator.tsx (1)
13-75: LGTM! Well-structured component.The ViewCreator component is well-organized with a clear three-step flow:
- Data source selection
- View naming and creation
- Completion
The component properly uses Mantine UI, follows Atomic Design principles, and integrates cleanly with the SWR-based data management approach.
src/app/api/v1/pages/tree/route.ts (2)
42-42: LGTM! Defensive emoji handling.The guards using
'emoji' in containerand'emoji' in childproperly handle cases where the emoji property may not exist on container objects, ensuring type safety and avoiding runtime errors.Also applies to: 54-54
50-50: LGTM! Explicit type annotation improves type safety.The explicit return type annotation
: { page: Page }on the map callback ensures the returned objects match the expected Page type structure.src/types/schemas/entities/data-view.ts (1)
4-13: LGTM! Schema follows project conventions.The
dataViewSchemais well-defined with appropriate validations:
- Non-empty strings for name and dataSourceId
- Array of non-empty strings for columns
- Proper extension of standard metadata schemas
The schema follows the established pattern used in other entity definitions.
src/types/api/endpoints/get-data-views.ts (1)
1-17: LGTM! Endpoint definition follows established patterns.The endpoint definition is clean and consistent:
- Clear endpoint path constant
- Optional dataSourceId query parameter for filtering
- Response schema as array of data views
- Proper type inference and DataWrapper usage
The pattern matches other endpoint definitions in the codebase.
src/app/pages/[id]/page.tsx (3)
33-40: Auto-selection logic looks correct.The useEffect properly guards against infinite loops by checking
!searchParameters.get('v')before callingrouter.replace. This ensures the first view is auto-selected only when no view is currently specified in the URL.
53-61: LGTM! Clean view creation callback.The
doViewCreatedcallback properly:
- Closes the modal
- Refreshes page data via
mutate()- Navigates to the newly created view
The flow is clean and handles the post-creation UX well.
89-129: Excellent refactor to tab-based view management.The refactored UI provides:
- Clean tab-based navigation between views and page contents
- Modal-based view creation workflow
- Proper integration with SWR data fetching
- Good error and loading state handling
The migration from store-based to SWR-based data management is well-executed.
src/types/api/endpoints/index.ts (1)
2-12: LGTM! Endpoint re-exports complete.All new endpoint modules (data sources, data views, pages, updates) are properly re-exported, providing a clean unified API surface for the new data management features.
src/types/api/endpoints/get-data-source.ts (1)
1-17: LGTM! Clean endpoint definition.The get-data-source endpoint definition:
- Properly aliases the dataSourceSchema for the response
- Validates ID parameter as non-empty string
- Follows the established pattern used in other get-by-id endpoints
src/app/api/v1/views/route.ts (3)
9-32: LGTM! Properly scoped GET handler.The GET handler correctly:
- Scopes queries with
addUserIdToQueryto prevent cross-user data leaks- Sorts by createdAt descending
- Supports optional filtering by dataSourceId
- Maps results to the expected response shape
As per coding guidelines
60-67: Verify: Silent failure on invalid pageId.When
pageIdis provided in the request body, the code attempts to fetch the page (lines 60-67) but doesn't throw an error if the page is not found or the user lacks access. The view is created successfully, but the page-view link is silently skipped (lines 82-87).This could be confusing for API consumers who expect an error when providing an invalid
pageId. Consider whether this silent failure is intentional or if aNotFoundErrorshould be thrown whenpageIdis provided but the page cannot be found.If the silent failure is unintentional, apply this diff:
let pageToLink: Container | undefined | null; if (body.pageId) { pageToLink = await containerRepository.getOneByQuery( addUserIdToQuery(containerRepository.createQuery().eq('id', body.pageId), session.user.id) .eq('workspaceId', workspace.id) .eq('type', 'page') ); + + if (!pageToLink) { + throw new NotFoundError('Page not found or access denied.'); + } }Also applies to: 82-87
38-96: POST handler implements proper ownership validation.The POST handler correctly:
- Validates workspace existence and ownership
- Verifies data source access with user/workspace scoping
- Creates data view with proper metadata (timestamps, userId, workspaceId)
- Updates the linked page's views array when applicable
- Uses
satisfiesfor type checkingThe implementation follows the established patterns for route handlers.
As per coding guidelines
| '*.(js|cjs|mjs|jsx|ts|tsx|svelte)': 'eslint --max-warnings 0', | ||
| '*.(ts|tsx)': () => 'tsc -p tsconfig.json --noEmit', |
There was a problem hiding this comment.
Fix the glob pattern syntax.
The glob patterns use incorrect syntax with parentheses () instead of curly braces {}. Lint-staged uses micromatch for glob matching, which requires curly braces for alternation patterns.
Apply this diff to fix the glob patterns:
- '*.(js|cjs|mjs|jsx|ts|tsx|svelte)': 'eslint --max-warnings 0',
- '*.(ts|tsx)': () => 'tsc -p tsconfig.json --noEmit',
+ '*.{js,cjs,mjs,jsx,ts,tsx,svelte}': 'eslint --max-warnings 0',
+ '*.{ts,tsx}': () => 'tsc -p tsconfig.json --noEmit',📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| '*.(js|cjs|mjs|jsx|ts|tsx|svelte)': 'eslint --max-warnings 0', | |
| '*.(ts|tsx)': () => 'tsc -p tsconfig.json --noEmit', | |
| '*.{js,cjs,mjs,jsx,ts,tsx,svelte}': 'eslint --max-warnings 0', | |
| '*.{ts,tsx}': () => 'tsc -p tsconfig.json --noEmit', |
🤖 Prompt for AI Agents
In lint-staged.config.js around lines 4 to 5, the glob patterns use parentheses
for alternation which is invalid for micromatch; replace the patterns using
parentheses with curly-brace alternation. Change
'*.(js|cjs|mjs|jsx|ts|tsx|svelte)' to '*.{js,cjs,mjs,jsx,ts,tsx,svelte}' and
'*.(ts|tsx)' to '*.{ts,tsx}', keeping the same command strings so eslint and tsc
run against the corrected globs.
| if (!dataSource) { | ||
| throw new Error('Data source not found'); | ||
| } |
There was a problem hiding this comment.
Return 404 instead of a 500 for missing data sources.
Throwing a plain Error bubbles through apiRoute, which catches it as an unknown error and emits a 500 “Internal server error”. That breaks the contract for a missing resource and hides the useful message. Please throw NotFoundError (as done in other routes) so clients get a 404. Apply the same change in the PATCH handler.
-import { apiRoute } from '@/lib/api/route-wrapper';
-import { getContainerRepository } from '@/lib/database';
-import { addUserIdToQuery } from '@/lib/database/helpers';
+import { apiRoute } from '@/lib/api/route-wrapper';
+import { getContainerRepository } from '@/lib/database';
+import { addUserIdToQuery } from '@/lib/database/helpers';
+import { NotFoundError } from '@/lib/errors/not-found-error';
@@
- if (!dataSource) {
- throw new Error('Data source not found');
+ if (!dataSource) {
+ throw new NotFoundError('Data source not found', true);
}
@@
- if (!existingDataSource) {
- throw new Error('Data source not found');
+ if (!existingDataSource) {
+ throw new NotFoundError('Data source not found', true);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!dataSource) { | |
| throw new Error('Data source not found'); | |
| } | |
| if (!dataSource) { | |
| throw new NotFoundError('Data source not found', true); | |
| } |
🤖 Prompt for AI Agents
In src/app/api/v1/data-sources/[id]/route.ts around lines 27 to 29, the GET
handler throws a generic Error when a data source is missing which results in a
500; replace that throw with a NotFoundError (the same error class used in other
routes) so the API returns a 404 and preserves the message; also make the
identical change in the PATCH handler where a missing data source currently
throws a generic Error.
| if (query.includeBlocks) { | ||
| returnValue.page.blocks = page.blocks ?? []; | ||
| } |
There was a problem hiding this comment.
Coerce includeBlocks before checking it.
query.includeBlocks is still the raw string from URLSearchParams. Because apiRoute only validates but does not replace query with the Zod-parsed value, calling the endpoint with ?includeBlocks=false still yields a truthy string and we send blocks unexpectedly. Parse the query (or reuse the parsed result) before the conditional so we work with a real boolean.
- if (query.includeBlocks) {
- returnValue.page.blocks = page.blocks ?? [];
- }
+ const { includeBlocks } = getPageDetailsQuerySchema.parse(query);
+
+ if (includeBlocks) {
+ returnValue.page.blocks = page.blocks ?? [];
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/app/api/v1/pages/[id]/route.ts around lines 52 to 54, query.includeBlocks
is still a raw string from URLSearchParams so '?includeBlocks=false' is truthy;
coerce or reuse the Zod-parsed query before the conditional so you check a real
boolean. Replace uses of query.includeBlocks with the parsed value (e.g.,
parsedQuery.includeBlocks) or explicitly parse/coerce it to a boolean (e.g.,
parsed = schema.parse(query) or Boolean(query.includeBlocks === 'true')) and
then use that boolean in the if statement to decide whether to attach
page.blocks.
| if (!dataView) { | ||
| throw new Error('Data view not found'); | ||
| } |
There was a problem hiding this comment.
Return a 404 when the view is missing
Throwing a plain Error here bubbles up as a 500 response. We need to surface a proper 404 so clients can distinguish “not found” from server faults. Please throw new NotFoundError('Data view not found') instead.
🤖 Prompt for AI Agents
In src/app/api/v1/views/[id]/route.ts around lines 23 to 25, the code currently
throws a generic Error when a data view is missing which produces a 500; replace
the generic throw with throw new NotFoundError('Data view not found') and ensure
NotFoundError is imported from the proper errors module (or defined) at the top
of the file so the handler returns a 404 instead of a 500.
| return { | ||
| ...payload, | ||
| inputProps: { | ||
| ...payload.inputProps, | ||
| onChange: (event: ChangeEvent<HTMLInputElement>) => { | ||
| dataSourceForm.setFieldValue('name', event.target.value); | ||
| if (event.target.value) { | ||
| setDataSourceSelectorEnabled(true); | ||
| } else { | ||
| setDataSourceSelectorEnabled(false); | ||
| } | ||
| }, | ||
| }, | ||
| }; |
There was a problem hiding this comment.
Fix selector enablement logic
When the name field is cleared (e.g., user hits backspace to empty), setDataSourceSelectorEnabled(false) fires, so the Select becomes disabled precisely when no new name is being entered. The Select only re-enables after typing a character, which blocks the intended “pick an existing data source” flow. Please invert the toggle so the Select stays enabled when the field is empty, and ensure the clear button also re-enables it.
Apply this diff:
@@
- onChange: (event: ChangeEvent<HTMLInputElement>) => {
- dataSourceForm.setFieldValue('name', event.target.value);
- if (event.target.value) {
- setDataSourceSelectorEnabled(true);
- } else {
- setDataSourceSelectorEnabled(false);
- }
- },
+ onChange: (event: ChangeEvent<HTMLInputElement>) => {
+ const { value } = event.target;
+ dataSourceForm.setFieldValue('name', value);
+ setDataSourceSelectorEnabled(value === '');
+ },
@@
- <ActionIcon variant="outline" onClick={() => dataSourceForm.setValues({ name: '' })}>
+ <ActionIcon
+ variant="outline"
+ onClick={() => {
+ dataSourceForm.setValues({ name: '' });
+ setDataSourceSelectorEnabled(true);
+ }}
+ >Also applies to: 101-104
🤖 Prompt for AI Agents
In src/components/molecules/view-creator/data-source-selector.tsx around lines
34-47 (and also apply same change at lines 101-104), the logic that toggles
setDataSourceSelectorEnabled is inverted: it disables the Select when the name
field is empty and only enables it when typing a character. Reverse the
condition so the Select is enabled when the name field is empty and disabled
when it has a value; also ensure any clear-button handler calls
setDataSourceSelectorEnabled(true) when clearing the field so the Select is
re-enabled immediately. Make these changes in both places mentioned.
| if (isLoading) { | ||
| return ( | ||
| <Stack align="center" py="xl"> | ||
| <Loader /> | ||
| <Text size="sm" c="dimmed"> | ||
| <Loader /> | ||
| </Text> | ||
| </Stack> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Fix duplicate Loader in loading state.
The loading state renders two <Loader /> components. The second one (line 39) should display a text message instead.
Apply this diff to fix the loading text:
<Stack align="center" py="xl">
<Loader />
<Text size="sm" c="dimmed">
- <Loader />
+ Loading pages...
</Text>
</Stack>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (isLoading) { | |
| return ( | |
| <Stack align="center" py="xl"> | |
| <Loader /> | |
| <Text size="sm" c="dimmed"> | |
| <Loader /> | |
| </Text> | |
| </Stack> | |
| ); | |
| } | |
| if (isLoading) { | |
| return ( | |
| <Stack align="center" py="xl"> | |
| <Loader /> | |
| <Text size="sm" c="dimmed"> | |
| Loading pages... | |
| </Text> | |
| </Stack> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/components/organisms/data-view-render.tsx around lines 34 to 43, the
loading branch currently renders two <Loader /> components; replace the second
<Loader /> inside the Text component with a human-readable message (e.g.,
"Loading...") so the UI shows one spinner and a descriptive text. Update the
Text children to a string message and keep layout/styling unchanged.
| import type { GetDataSourcesResponse } from '@/types/api'; | ||
| import { swrFetcher } from '@/lib/swr/fetcher'; | ||
|
|
||
| export const useDataSources = () => useSWR<GetDataSourcesResponse>('/data-sources', swrFetcher); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use the endpoint constant instead of hardcoding the path.
The endpoint path '/data-sources' is hardcoded here but GET_DATA_SOURCES_ENDPOINT is defined in src/types/api/endpoints/get-data-sources.ts. Using the constant ensures consistency and reduces duplication.
Apply this diff to use the constant:
+import { GET_DATA_SOURCES_ENDPOINT } from '@/types/api/endpoints/get-data-sources';
import useSWR from 'swr';
import type { GetDataSourcesResponse } from '@/types/api';
import { swrFetcher } from '@/lib/swr/fetcher';
-export const useDataSources = () => useSWR<GetDataSourcesResponse>('/data-sources', swrFetcher);
+export const useDataSources = () => useSWR<GetDataSourcesResponse>(GET_DATA_SOURCES_ENDPOINT, swrFetcher);Note: The migration from Nanostores to SWR appears intentional per the PR summary, though this differs from the previous architectural guidance. Based on learnings.
🤖 Prompt for AI Agents
In src/lib/hooks/api/use-data-sources.ts around line 5, the hook currently
hardcodes the string '/data-sources'; replace this with the exported
GET_DATA_SOURCES_ENDPOINT constant from
src/types/api/endpoints/get-data-sources.ts by importing that constant at the
top of the file and using it as the first argument to useSWR (keep the
GetDataSourcesResponse generic and swrFetcher unchanged).
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/components/organisms/data-view-render.tsx (1)
5-5: Resolve global property shadowing (previously flagged).The
DataViewimport still shadows the browser global. Apply the type-only import fix from the previous review.src/app/api/v1/data-sources/[id]/route.ts (1)
58-60: Return 404 for missing data source (unresolved from past review).Throwing a generic
Errorbubbles throughapiRouteas a 500, but this is a resource-not-found case. The past review already flagged this and suggestedNotFoundError.Apply this diff:
if (!existingDataSource) { - throw new Error('Data source not found'); + throw new NotFoundError('Data source not found', true); }
🧹 Nitpick comments (1)
src/components/organisms/data-view-render.tsx (1)
62-62: Consider using Mantine spacing.The inline
style={{ marginRight: '8px' }}could use Mantine's spacing system for consistency.Apply this diff:
- {page.emoji && <span style={{ marginRight: '8px' }}>{page.emoji}</span>} + {page.emoji && <span style={{ marginInlineEnd: 'var(--mantine-spacing-xs)' }}>{page.emoji}</span>}Or wrap in a Box/Group component with
gap="xs".
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/app/api/v1/data-sources/[id]/route.ts(1 hunks)src/app/api/v1/views/[id]/route.ts(1 hunks)src/components/molecules/view-creator/data-source-selector.tsx(1 hunks)src/components/organisms/data-view-render.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/molecules/view-creator/data-source-selector.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Prefer TypeScript type aliases (
type) overinterfacewhen defining shapesPrefer type aliases over interface in TypeScript.
Files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.tssrc/components/organisms/data-view-render.tsx
src/app/api/**/route.@(ts|tsx)
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Implement API endpoints as Next.js route handlers under
src/app/api/**/route.ts(x)
Files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
src/app/api/**/route.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/app/api/**/route.ts: Place Next.js API route handlers at src/app/api//route.ts and export functions named GET, POST, etc.
Use apiRoute<Resp, Query, Body>(options, handler) in all API route handlers to enforce Zod validation and typed session.
In routes, import and use expectedQuerySchema/expectedBodySchema from src/types/api when validating input.
Obtain repositories via getWorkspaceRepository()/getContainerRepository() from src/lib/database in API routes.
Always scope database queries with addUserIdToQuery(..., session.user.id) to prevent cross-user data leaks.
Routes must only live under src/app/api/*; do not place route handlers elsewhere.
Files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
src/components/@(atoms|molecules|organisms|templates)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Organize UI components following Atomic Design: atoms, molecules, organisms, templates
Files:
src/components/organisms/data-view-render.tsx
src/{components,app}/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/basics.mdc)
Use Mantine UI 8 components for building React UI
Files:
src/components/organisms/data-view-render.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/lib/store/**/*.ts : Use Nanostores with the createFetcherStore helper for data fetching in stores.
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : Place Next.js API route handlers at src/app/api/<route>/route.ts and export functions named GET, POST, etc.
Applied to files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
📚 Learning: 2025-10-23T17:57:41.144Z
Learnt from: CR
PR: danships/thoth#0
File: .cursor/rules/basics.mdc:0-0
Timestamp: 2025-10-23T17:57:41.144Z
Learning: Applies to src/app/api/**/route.@(ts|tsx) : Implement API endpoints as Next.js route handlers under `src/app/api/**/route.ts(x)`
Applied to files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : Always scope database queries with addUserIdToQuery(..., session.user.id) to prevent cross-user data leaks.
Applied to files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : In routes, import and use expectedQuerySchema/expectedBodySchema from src/types/api when validating input.
Applied to files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : Routes must only live under src/app/api/*; do not place route handlers elsewhere.
Applied to files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : Use apiRoute<Resp, Query, Body>(options, handler) in all API route handlers to enforce Zod validation and typed session.
Applied to files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
PR: danships/thoth#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : Obtain repositories via getWorkspaceRepository()/getContainerRepository() from src/lib/database in API routes.
Applied to files:
src/app/api/v1/views/[id]/route.tssrc/app/api/v1/data-sources/[id]/route.ts
🧬 Code graph analysis (3)
src/app/api/v1/views/[id]/route.ts (6)
src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/types/api/endpoints/get-data-view.ts (3)
GetDataViewResponse(15-15)GetDataViewParameters(22-22)getDataViewParametersSchema(19-21)src/lib/database/index.ts (2)
getDataViewRepository(32-35)getContainerRepository(22-25)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)src/lib/errors/not-found-error.ts (1)
NotFoundError(3-7)src/types/api/endpoints/update-data-view.ts (5)
UpdateDataViewResponse(25-25)UpdateDataViewParameters(32-32)UpdateDataViewBody(24-24)updateDataViewBodySchema(16-21)updateDataViewParametersSchema(29-31)
src/app/api/v1/data-sources/[id]/route.ts (6)
src/lib/api/route-wrapper.ts (1)
apiRoute(15-128)src/types/api/endpoints/get-data-source.ts (3)
GetDataSourceResponse(10-10)GetDataSourceParameters(17-17)getDataSourceParametersSchema(14-16)src/lib/database/index.ts (1)
getContainerRepository(22-25)src/lib/database/helpers.ts (1)
addUserIdToQuery(3-5)src/lib/errors/not-found-error.ts (1)
NotFoundError(3-7)src/types/api/endpoints/update-data-source.ts (5)
UpdateDataSourceResponse(22-22)UpdateDataSourceParameters(29-29)UpdateDataSourceBody(21-21)updateDataSourceBodySchema(9-13)updateDataSourceParametersSchema(26-28)
src/components/organisms/data-view-render.tsx (4)
src/types/api/entities.ts (1)
DataView(24-24)src/lib/hooks/api/use-pages.ts (1)
usePagesByDataSource(19-31)src/lib/hooks/use-cud-api.ts (1)
useCudApi(5-44)src/types/api/endpoints/create-page.ts (3)
CreatePageResponse(12-12)CreatePageBody(22-22)CREATE_PAGE_ENDPOINT(6-6)
🪛 Biome (2.1.2)
src/components/organisms/data-view-render.tsx
[error] 5-5: Do not shadow the global "DataView" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci
🔇 Additional comments (7)
src/components/organisms/data-view-render.tsx (4)
10-12: LGTM!Type definition follows the coding guidelines to use
typeoverinterface.
34-40: Loading state fixed.The duplicate
<Loader />issue from the previous review has been resolved. The loading state now correctly displays a single loader.
42-48: LGTM!Error state properly displays fetch errors with clear messaging.
68-82: LGTM!The new page input row with Enter key handling and disabled state during creation provides good UX.
src/app/api/v1/views/[id]/route.ts (2)
13-35: LGTM! Proper user scoping and error handling.The GET handler correctly implements user-scoped queries and throws NotFoundError for missing resources, ensuring proper 404 responses.
37-85: LGTM! Thorough ownership validation for cross-entity updates.The PATCH handler properly validates ownership of both the data view and the related data source when
dataSourceIdis updated. The filtering of undefined values (line 69) correctly implements partial updates.src/app/api/v1/data-sources/[id]/route.ts (1)
18-39: LGTM! Correctly handles missing resources.The GET handler properly uses NotFoundError and scopes queries to the authenticated user.
| if (!body) { | ||
| throw new Error('Body is required'); | ||
| } |
There was a problem hiding this comment.
Use a 400-level error for missing body.
Throwing a generic Error here produces a 500 response. Since this is a client validation failure, it should return 400 instead. Consider defining a BadRequestError (similar to NotFoundError) or allowing the apiRoute wrapper to handle missing bodies when expectedBodySchema is present.
🤖 Prompt for AI Agents
In src/app/api/v1/data-sources/[id]/route.ts around lines 47 to 49, throwing a
generic Error for a missing request body causes a 500 response; replace it with
a 400-level client error. Either throw a new BadRequestError (implement a
BadRequestError class matching the existing NotFoundError pattern and use it
here), or return a Response with status 400 and a clear message; alternatively,
update the apiRoute wrapper to detect & validate expectedBodySchema and
automatically respond 400 when the body is missing. Ensure the chosen solution
produces an HTTP 400 status and a descriptive error message.
| const { post, inProgress } = useCudApi(); | ||
| const [newPageName, setNewPageName] = useState(''); | ||
|
|
||
| const createPage = useCallback(async () => { | ||
| const name = newPageName.trim(); | ||
| if (!name || inProgress) { | ||
| return; | ||
| } | ||
| await post<CreatePageResponse, CreatePageBody>(CREATE_PAGE_ENDPOINT, { | ||
| name, | ||
| emoji: null, | ||
| parentId: view.dataSourceId, | ||
| }); | ||
| mutate(); // TODO mutate inline | ||
| setNewPageName(''); | ||
| }, [newPageName, inProgress, post, view.dataSourceId, mutate]); |
There was a problem hiding this comment.
Display creation errors to the user.
The error state from useCudApi is not captured or displayed. If page creation fails, the user receives no feedback, degrading the user experience.
Apply this diff to capture and display creation errors:
- const { post, inProgress } = useCudApi();
+ const { post, inProgress, error: createError } = useCudApi();
const [newPageName, setNewPageName] = useState('');Then add an Alert above the table to display creation errors:
+ {createError && (
+ <Alert color="red" title="Error creating page" mb="md">
+ {createError}
+ </Alert>
+ )}
<Table striped highlightOnHover w="full" mt="lg">🤖 Prompt for AI Agents
In src/components/organisms/data-view-render.tsx around lines 17 to 32, the
createPage flow ignores errors from useCudApi so failures are never shown to the
user; wrap the post call in a try/catch, add a local createError state
(useState<string | null>), clear createError before attempting the request, set
createError to the caught error message on failure and rethrow or return, and
keep resetting newPageName on success; then render an Alert above the Table that
displays createError when present (matching the provided Alert markup), and
include any new state/setters in the component and createPage hook dependency
array.
@nanostores/querydependency and replaced it with SWR for data fetching.Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Chores