Skip to content

feat: implement data source and data view management features#5

Merged
danships merged 2 commits intomainfrom
feat/add-datasource-features
Oct 30, 2025
Merged

feat: implement data source and data view management features#5
danships merged 2 commits intomainfrom
feat/add-datasource-features

Conversation

@danships
Copy link
Owner

@danships danships commented Oct 30, 2025

  • 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.

Summary by CodeRabbit

  • New Features

    • Data sources and views: full create/read/update endpoints, UI for creating/selecting data sources and views, tabbed page view with per-view rendering and "Add View" flow
    • New SWR-based data-fetching hooks for pages, page details, page blocks, pages tree, and data sources
    • Added CSS utility .is-pulled-right
  • Bug Fixes

    • Safer emoji and blocks handling; faster debounce in page editor
  • Refactor

    • Migrated from store-based fetching to SWR hooks; pre-commit runs lint-staged
  • Chores

    • VSCode, Prettier, ESLint integrations; pnpm cache config and package updates

- 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.
@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Walkthrough

Replaces 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

Cohort / File(s) Summary
Tooling & Editor
.github/workflows/ci.yaml, .husky/pre-commit, .vscode/settings.json, lint-staged.config.js, package.json, tsconfig.json
CI cache switched to pnpm-lock.yaml; pre-commit now runs pnpm lint-staged; added VSCode formatting/ESLint settings; added lint-staged devDependency and swr dependency; introduced path aliases.
Data Sources API
src/app/api/v1/data-sources/route.ts, src/app/api/v1/data-sources/[id]/route.ts
New GET/POST list/create and GET/PATCH retrieve/update endpoints for data sources with user-scoped queries, schema validation, ownership checks, and shaped responses.
Data Views API
src/app/api/v1/views/route.ts, src/app/api/v1/views/[id]/route.ts
New GET/POST list/create and GET/PATCH retrieve/update endpoints for data views; includes workspace/data-source validation, optional page linking, and user-scoped queries.
Pages API & Tree
src/app/api/v1/pages/route.ts, src/app/api/v1/pages/[id]/route.ts, src/app/api/v1/pages/[id]/blocks/route.ts, src/app/api/v1/pages/tree/route.ts
Added parentId/dataSourceId query handling and validations, includeBlocks query for details, linked data views retrieval, guarded blocks/emoji handling, and stricter POST validation/parent ownership checks.
SWR + Hooks
src/lib/swr/config.ts, src/lib/swr/fetcher.ts, src/lib/hooks/api/* (use-data-sources.ts, use-page-blocks.ts, use-page-details.ts, use-pages-tree.ts, use-pages.ts)
New swrFetcher and swrConfig; new SWR-based hooks for pages, page details/blocks, pages tree, and data sources with conditional keys and typed responses.
Removed Nanostores
src/lib/store/fetcher.ts, src/lib/store/query/get-page-blocks.ts, src/lib/store/query/get-page-details.ts, src/lib/store/query/get-pages-tree.ts
Removed nanoquery/nanostores-based fetcher and reactive store modules.
Database & Entities
src/lib/database/entities/data-view.ts, src/lib/database/entities/index.ts, src/lib/database/index.ts
New DataView entity definition, registered entity in DB init, and new getDataViewRepository() accessor.
Types & Schemas (API)
src/types/api/endpoints/* (create-data-source, create-data-view, get-data-source(s), get-data-view(s), get-pages, update-data-source, update-data-view, get-page-details, index.ts)
Added many endpoint constants, Zod schemas and types for DataSource and DataView CRUD, pages querying, extended page-details response/query (includeBlocks, views), and re-exported new endpoints.
Types & Schemas (Entities)
src/types/api/entities.ts, src/types/database/index.ts, src/types/schemas/entities/* (container.ts, data-view.ts, index.ts)
Added dataViewSchema and DataView types, added views field to page container schema, updated container discriminated union, and exported new entity types (DataView, DataSource, Page).
UI Components (new)
src/components/organisms/data-view-render.tsx, src/components/molecules/view-creator/data-source-selector.tsx, src/components/organisms/view-creator.tsx
New components: DataViewRender (list + create page), DataSourceSelector (select or create data source), and ViewCreator (3-step modal to create a view).
UI Components (updated)
src/app/pages/[id]/page.tsx, src/components/layout.tsx, src/components/molecules/sidebar/logged-in-container.tsx, src/components/organisms/page-detail-editor.tsx
Replaced store usage with SWR hooks (usePageDetails, usePagesTree), added tabs for views, Add View modal integration, changed Image width prop to number with eager loading, and reduced PageDetailEditor debounce to 1500ms.
Utilities & Misc
src/lib/errors/bad-request-error.ts, src/lib/api/server/get-workspace.ts, src/lib/auth/config.ts, src/app/globals.css
Added BadRequestError class, getWorkspace helper, updated auth/config creation payload typing and timestamps, and added .is-pulled-right CSS utility.

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 }
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus review areas:
    • Verify user/workspace scoping and ownership checks across new API routes (data-sources, views, pages).
    • Ensure DB registration and repository accessor for DataView are correctly wired and typed.
    • Validate SWR hook conditional keys and that removed nanostores usages were fully migrated.
    • Confirm schema compositions (dataView, dataSource, page container) and endpoint exports align with handlers and frontend usage.
    • Check ViewCreator/DataSourceSelector integration (mutate/navigation) and edge-case error handling.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: implement data source and data view management features" clearly and specifically relates to the primary changes in the changeset. The PR introduces numerous new API endpoints for data source and data view operations (GET, POST, PATCH routes), corresponding SWR-based hooks for data fetching, new components like ViewCreator and DataSourceSelector, and updated page rendering logic to integrate these features. The title accurately summarizes these feature additions in a concise, single sentence that follows conventional commit formatting. A teammate scanning the repository history would immediately understand that this PR adds data source and data view management capabilities to the application.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-datasource-features

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (8)
src/app/globals.css (1)

13-15: Consider using modern layout methods instead of float.

The new .is-pulled-right utility uses float: 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-right is 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 userId is 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 making includeBlocks parameter 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 URLSearchParams or encodeURIComponent to 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 global DataView.

The name DataView conflicts with JavaScript's built-in DataView class (used for binary data in ArrayBuffers). While the context makes the distinction clear, this could cause confusion if the built-in DataView is needed in this file or related modules.

Consider renaming to DataViewEntity or ViewEntity across the codebase to eliminate ambiguity. As per static analysis tools.

src/components/organisms/view-creator.tsx (1)

33-49: Consider removing viewForm from the dependency array.

The viewForm object from useForm has 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

📥 Commits

Reviewing files that changed from the base of the PR and between 35e859f and 2563497.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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) over interface when defining shapes

Prefer type aliases over interface in TypeScript.

Files:

  • src/lib/database/entities/index.ts
  • src/components/organisms/page-detail-editor.tsx
  • src/lib/hooks/api/use-page-blocks.ts
  • src/types/schemas/entities/data-view.ts
  • src/types/api/endpoints/get-pages.ts
  • src/types/api/endpoints/get-data-views.ts
  • src/types/api/endpoints/get-data-sources.ts
  • src/components/layout.tsx
  • src/lib/hooks/api/use-data-sources.ts
  • src/lib/hooks/api/use-page-details.ts
  • src/types/schemas/entities/index.ts
  • src/app/api/v1/data-sources/[id]/route.ts
  • src/lib/api/server/get-workspace.ts
  • src/types/api/endpoints/update-data-view.ts
  • src/lib/errors/bad-request-error.ts
  • src/lib/swr/fetcher.ts
  • src/lib/database/entities/data-view.ts
  • src/app/api/v1/data-sources/route.ts
  • src/types/api/endpoints/create-data-source.ts
  • src/components/organisms/data-view-render.tsx
  • src/types/api/endpoints/create-data-view.ts
  • src/lib/hooks/api/use-pages.ts
  • src/types/api/endpoints/index.ts
  • src/components/organisms/view-creator.tsx
  • src/types/database/index.ts
  • src/types/schemas/entities/container.ts
  • src/types/api/endpoints/get-data-source.ts
  • src/app/api/v1/pages/[id]/route.ts
  • src/lib/auth/config.ts
  • src/lib/database/index.ts
  • src/components/molecules/view-creator/data-source-selector.tsx
  • src/app/api/v1/views/route.ts
  • src/lib/swr/config.ts
  • src/components/molecules/sidebar/logged-in-container.tsx
  • src/app/api/v1/pages/tree/route.ts
  • src/app/api/v1/pages/[id]/blocks/route.ts
  • src/app/api/v1/views/[id]/route.ts
  • src/types/api/endpoints/update-data-source.ts
  • src/lib/hooks/api/use-pages-tree.ts
  • src/types/api/entities.ts
  • src/types/api/endpoints/get-data-view.ts
  • src/types/api/endpoints/get-page-details.ts
  • src/app/pages/[id]/page.tsx
  • src/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.tsx
  • src/components/organisms/data-view-render.tsx
  • src/components/organisms/view-creator.tsx
  • src/components/molecules/view-creator/data-source-selector.tsx
  • src/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.tsx
  • src/components/layout.tsx
  • src/components/organisms/data-view-render.tsx
  • src/components/organisms/view-creator.tsx
  • src/components/molecules/view-creator/data-source-selector.tsx
  • src/components/molecules/sidebar/logged-in-container.tsx
  • src/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.ts
  • src/types/api/endpoints/get-data-views.ts
  • src/types/api/endpoints/get-data-sources.ts
  • src/types/api/endpoints/update-data-view.ts
  • src/types/api/endpoints/create-data-source.ts
  • src/types/api/endpoints/create-data-view.ts
  • src/types/api/endpoints/index.ts
  • src/types/api/endpoints/get-data-source.ts
  • src/types/api/endpoints/update-data-source.ts
  • src/types/api/endpoints/get-data-view.ts
  • src/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.ts
  • src/app/api/v1/data-sources/route.ts
  • src/app/api/v1/pages/[id]/route.ts
  • src/app/api/v1/views/route.ts
  • src/app/api/v1/pages/tree/route.ts
  • src/app/api/v1/pages/[id]/blocks/route.ts
  • src/app/api/v1/views/[id]/route.ts
  • src/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.ts
  • src/app/api/v1/data-sources/route.ts
  • src/app/api/v1/pages/[id]/route.ts
  • src/app/api/v1/views/route.ts
  • src/app/api/v1/pages/tree/route.ts
  • src/app/api/v1/pages/[id]/blocks/route.ts
  • src/app/api/v1/views/[id]/route.ts
  • src/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/app with page.tsx, layout.tsx, loading.tsx, and error.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.json
  • src/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.ts
  • src/types/api/endpoints/get-pages.ts
  • src/types/api/endpoints/get-data-views.ts
  • src/types/api/endpoints/get-data-sources.ts
  • src/types/schemas/entities/index.ts
  • src/types/api/endpoints/update-data-view.ts
  • src/app/api/v1/data-sources/route.ts
  • src/types/api/endpoints/create-data-source.ts
  • src/types/api/endpoints/create-data-view.ts
  • src/types/api/endpoints/index.ts
  • src/types/database/index.ts
  • src/types/api/endpoints/get-data-source.ts
  • src/types/api/endpoints/update-data-source.ts
  • src/types/api/entities.ts
  • src/types/api/endpoints/get-data-view.ts
  • src/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.ts
  • src/lib/swr/fetcher.ts
  • src/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.json
  • src/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.json
  • src/app/api/v1/views/route.ts
  • src/app/pages/[id]/page.tsx
  • src/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.ts
  • src/app/api/v1/data-sources/route.ts
  • src/types/api/endpoints/create-data-source.ts
  • src/types/database/index.ts
  • src/app/api/v1/pages/[id]/route.ts
  • src/app/api/v1/views/route.ts
  • src/app/api/v1/views/[id]/route.ts
  • src/types/api/endpoints/update-data-source.ts
  • src/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.ts
  • src/app/api/v1/data-sources/route.ts
  • src/app/api/v1/views/route.ts
  • src/app/api/v1/views/[id]/route.ts
  • src/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.ts
  • src/app/api/v1/data-sources/route.ts
  • src/app/api/v1/pages/[id]/route.ts
  • src/app/api/v1/views/route.ts
  • src/app/api/v1/views/[id]/route.ts
  • src/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.ts
  • src/lib/api/server/get-workspace.ts
  • src/app/api/v1/data-sources/route.ts
  • src/app/api/v1/pages/[id]/route.ts
  • src/lib/auth/config.ts
  • src/lib/database/index.ts
  • src/app/api/v1/views/route.ts
  • src/app/api/v1/pages/tree/route.ts
  • src/app/api/v1/views/[id]/route.ts
  • src/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.ts
  • src/app/api/v1/data-sources/route.ts
  • src/app/api/v1/views/route.ts
  • src/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.ts
  • src/app/api/v1/data-sources/route.ts
  • src/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.ts
  • src/app/api/v1/pages/[id]/route.ts
  • src/app/api/v1/views/route.ts
  • src/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.ts
  • src/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 width prop (matching the existing numeric height) 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.yaml instead of package-lock.json ensures proper cache invalidation when dependencies change.

.husky/pre-commit (1)

1-1: LGTM!

The migration from pnpm lint to pnpm lint-staged is 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: true setting 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 PageContainerCreate type improves type safety and consistency. Adding createdAt and lastUpdated fields 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 parentId or dataSourceId must 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: true option 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 blocks field 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 views field to pageContainerSchema correctly 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_ENDPOINT is 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 DataView naming concern (shadowing global) was already flagged in src/lib/database/index.ts.

src/lib/database/entities/data-view.ts (2)

7-13: Clarify the commented-out dataSource relation.

The dataSource relation is commented out but appears to be a logical connection given the dataSourceId field in the schema. Is this intentionally disabled or a TODO?

Please confirm whether this relation should be:

  1. Enabled now (if the feature is ready)
  2. Tracked as a TODO with a comment explaining why it's deferred
  3. 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 filterSortFields are 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:

  1. Data source selection
  2. View naming and creation
  3. 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 container and 'emoji' in child properly 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 dataViewSchema is 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 calling router.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 doViewCreated callback properly:

  1. Closes the modal
  2. Refreshes page data via mutate()
  3. 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 addUserIdToQuery to 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 pageId is 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 a NotFoundError should be thrown when pageId is 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 satisfies for type checking

The implementation follows the established patterns for route handlers.

As per coding guidelines

Comment on lines +4 to +5
'*.(js|cjs|mjs|jsx|ts|tsx|svelte)': 'eslint --max-warnings 0',
'*.(ts|tsx)': () => 'tsc -p tsconfig.json --noEmit',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
'*.(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.

Comment on lines 27 to 29
if (!dataSource) {
throw new Error('Data source not found');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
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.

Comment on lines +52 to +54
if (query.includeBlocks) {
returnValue.page.blocks = page.blocks ?? [];
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines 23 to 25
if (!dataView) {
throw new Error('Data view not found');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines 34 to 47
return {
...payload,
inputProps: {
...payload.inputProps,
onChange: (event: ChangeEvent<HTMLInputElement>) => {
dataSourceForm.setFieldValue('name', event.target.value);
if (event.target.value) {
setDataSourceSelectorEnabled(true);
} else {
setDataSourceSelectorEnabled(false);
}
},
},
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines 34 to 43
if (isLoading) {
return (
<Stack align="center" py="xl">
<Loader />
<Text size="sm" c="dimmed">
<Loader />
</Text>
</Stack>
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/components/organisms/data-view-render.tsx (1)

5-5: Resolve global property shadowing (previously flagged).

The DataView import 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 Error bubbles through apiRoute as a 500, but this is a resource-not-found case. The past review already flagged this and suggested NotFoundError.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2563497 and 50c14c0.

📒 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) over interface when defining shapes

Prefer type aliases over interface in TypeScript.

Files:

  • src/app/api/v1/views/[id]/route.ts
  • src/app/api/v1/data-sources/[id]/route.ts
  • src/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.ts
  • src/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.ts
  • src/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.ts
  • src/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.ts
  • src/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.ts
  • src/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.ts
  • src/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.ts
  • src/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.ts
  • src/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.ts
  • src/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 type over interface.


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 dataSourceId is 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.

Comment on lines +47 to +49
if (!body) {
throw new Error('Body is required');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +17 to +32
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]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

@danships danships merged commit 0a454a4 into main Oct 30, 2025
2 checks passed
@danships danships deleted the feat/add-datasource-features branch October 30, 2025 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant