Skip to content

Conversation

@nishika26
Copy link
Contributor

@nishika26 nishika26 commented Jan 29, 2026

This is how the UI looks like right now, please take a pull from this branch to try it out, also note that creation and deletion of knowledge base takes a little time to get updated in the UI, while the process already starts in the backend -
image

Summary

  • New Features

    • Introduced Knowledge Base page with collection management UI, enabling users to create, view, and delete collections.
    • Added job status tracking functionality for background operations.
    • Added Knowledge Base menu item to navigation sidebar.
  • Improvements

    • Enhanced date formatting with improved error handling and consistent "DD Mon YYYY, HH:MM" display format.

Summary by CodeRabbit

  • New Features
    • Launched Knowledge Base feature for comprehensive document collection management
    • Create, organize, view, and delete document collections through an intuitive interface
    • Added Knowledge Base navigation menu item for quick access
    • Collections display associated documents with enrichment tracking and live status updates
    • Document selection interface and confirmation dialogs for secure operations

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Adds a Knowledge Base UI and supporting API proxy routes: collection list/create, collection get/delete, and job-status polling; consolidates date formatting into a shared utility and adds a Knowledge base item to the sidebar. All API routes forward X-API-KEY to the backend.

Changes

Cohort / File(s) Summary
Collections API (list/create)
app/api/collections/route.ts
Added GET to proxy list collections and POST to create collections; validates X-API-KEY, forwards requests to backendUrl /api/v1/collections/, handles empty responses, and propagates backend status.
Collection item API (get/delete)
app/api/collections/[collection_id]/route.ts
Added GET to fetch a single collection with include_docs=true and DELETE to remove a collection; both validate X-API-KEY, forward to backend, parse responses, and return appropriate statuses.
Job status API
app/api/collections/jobs/[job_id]/route.ts
Added GET handler to proxy job status requests to backend /api/v1/collections/jobs/{job_id}, validating X-API-KEY and returning backend status and payload.
Knowledge Base page
app/knowledge-base/page.tsx
New client-side KnowledgeBasePage: two-panel UI for collection CRUD, document listing/enrichment, job polling, API-key persistence, document picker and confirmation modals, and client-side caching of enrichment metadata.
Date utils & consumers
app/components/utils.ts, app/document/page.tsx
Made formatDate(dateString?: string) optional and robust (returns 'N/A' or original input on parse failure), converts to explicit "day month year, HH:MM" format; document page now imports and uses the shared utility.
Sidebar navigation
app/components/Sidebar.tsx
Added a new "Knowledge base" submenu item under Capabilities routing to /knowledge-base.

Sequence Diagram

sequenceDiagram
    participant Client as Client/Browser
    participant APIRoute as API Route (Next.js)
    participant Backend as Backend Service
    participant Poller as Polling Component

    Client->>APIRoute: POST /api/collections (X-API-KEY, body)
    APIRoute->>Backend: POST /api/v1/collections (forward key & body)
    Backend-->>APIRoute: 200 { job_id }
    APIRoute-->>Client: 200 { job_id }

    Client->>Poller: Start polling job_id
    loop until job complete
        Poller->>APIRoute: GET /api/collections/jobs/{job_id} (X-API-KEY)
        APIRoute->>Backend: GET /api/v1/collections/jobs/{job_id}
        Backend-->>APIRoute: job status
        APIRoute-->>Poller: job status
        Poller->>Client: update UI
    end

    Client->>APIRoute: GET /api/collections/{collection_id} (X-API-KEY)
    APIRoute->>Backend: GET /api/v1/collections/{collection_id}?include_docs=true
    Backend-->>APIRoute: collection + docs
    APIRoute-->>Client: collection payload
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • Prajna1999
  • vprashrex

Poem

🐰 I hopped in with keys held tight,
Collections sprouted in the night,
Jobs I polled with patient cheer,
Dates now bloom and menus clear,
hop hop — Knowledge grows, delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Knowledge base: Creation and deletion UI' accurately describes the main changes, which introduce a Knowledge Base page with creation and deletion functionality for collections.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feature/knowledge_base

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Reviews will stop working after February 8, 2026 if the new IP is not added to your allowlist.


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.

@nishika26 nishika26 changed the base branch from main to feature/doc_upload January 30, 2026 05:26
@nishika26 nishika26 changed the base branch from feature/doc_upload to main January 31, 2026 07:45
@nishika26 nishika26 marked this pull request as ready for review February 2, 2026 05:13
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

🤖 Fix all issues with AI agents
In `@app/components/utils.ts`:
- Around line 10-21: The formatDate function currently assumes new
Date(dateString) is valid; update formatDate to guard against invalid dates by
checking Number.isNaN(date.getTime()) (or isNaN(date.getTime())) right after
creating const date = new Date(dateString) and before applying the IST offset
and formatting; if the check fails return 'N/A' (or the current fallback) so you
never format an Invalid Date and produce NaN values. Ensure you keep the
existing IST conversion and formatting logic in formatDate unchanged except for
this validity guard.

In `@app/knowledge-base/page.tsx`:
- Around line 170-188: The fetchCollections function assumes the API returns an
object with result.data, so when /api/collections returns a raw array the UI
shows zero items; change the parsing to accept both shapes by treating the
response as const collections = result.data || result || [] before
enriching—then continue to call enrichCollectionWithCache on that array and pass
the enrichedCollections to setCollections.

Comment on lines +10 to +21
export const formatDate = (dateString?: string): string => {
if (!dateString) return 'N/A';
try {
const date = new Date(dateString);
const istDate = new Date(date.getTime() + (5.5 * 60 * 60 * 1000));
const day = istDate.getDate();
const month = istDate.toLocaleDateString('en-US', { month: 'short' });
const year = istDate.getFullYear();
const hours = String(istDate.getHours()).padStart(2, '0');
const minutes = String(istDate.getMinutes()).padStart(2, '0');
return `${day} ${month} ${year}, ${hours}:${minutes}`;
} catch {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "utils.ts" -path "*/app/components/*" -type f

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 101


🏁 Script executed:

cat -n app/components/utils.ts | head -30

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 1356


🏁 Script executed:

# Test JavaScript Date behavior with invalid input
node <<'EOF'
// Test invalid date behavior
const invalidDate = new Date("invalid-date-string");
console.log("Invalid date object:", invalidDate);
console.log("getTime() result:", invalidDate.getTime());
console.log("isNaN(getTime()):", Number.isNaN(invalidDate.getTime()));
console.log("getDate() result:", invalidDate.getDate());
console.log("getHours() result:", invalidDate.getHours());

// Test formatting with NaN values
const result = `${invalidDate.getDate()} ${invalidDate.getHours()}:00`;
console.log("Formatted with NaN:", result);
EOF

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 230


🏁 Script executed:

# Search for tests or usages of formatDate
rg "formatDate" --type ts --type tsx -B 2 -A 2

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 103


🏁 Script executed:

# Search for tests or usages of formatDate with correct file types
rg "formatDate" -B 2 -A 2

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 6249


🏁 Script executed:

# Check if there are test files
find . -name "*.test.ts" -o -name "*.spec.ts" | head -10

Repository: ProjectTech4DevAI/kaapi-frontend

Length of output: 58


Guard against invalid date inputs. new Date() doesn't throw on invalid input—it returns an Invalid Date object with NaN values that render as "NaN NaN:00" in the output. The current try/catch won't prevent this. Add a check for Number.isNaN(date.getTime()) before formatting.

Proposed fix
 export const formatDate = (dateString?: string): string => {
   if (!dateString) return 'N/A';
   try {
     const date = new Date(dateString);
+    if (Number.isNaN(date.getTime())) {
+      return dateString;
+    }
     const istDate = new Date(date.getTime() + (5.5 * 60 * 60 * 1000));
     const day = istDate.getDate();
     const month = istDate.toLocaleDateString('en-US', { month: 'short' });
📝 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
export const formatDate = (dateString?: string): string => {
if (!dateString) return 'N/A';
try {
const date = new Date(dateString);
const istDate = new Date(date.getTime() + (5.5 * 60 * 60 * 1000));
const day = istDate.getDate();
const month = istDate.toLocaleDateString('en-US', { month: 'short' });
const year = istDate.getFullYear();
const hours = String(istDate.getHours()).padStart(2, '0');
const minutes = String(istDate.getMinutes()).padStart(2, '0');
return `${day} ${month} ${year}, ${hours}:${minutes}`;
} catch {
export const formatDate = (dateString?: string): string => {
if (!dateString) return 'N/A';
try {
const date = new Date(dateString);
if (Number.isNaN(date.getTime())) {
return dateString;
}
const istDate = new Date(date.getTime() + (5.5 * 60 * 60 * 1000));
const day = istDate.getDate();
const month = istDate.toLocaleDateString('en-US', { month: 'short' });
const year = istDate.getFullYear();
const hours = String(istDate.getHours()).padStart(2, '0');
const minutes = String(istDate.getMinutes()).padStart(2, '0');
return `${day} ${month} ${year}, ${hours}:${minutes}`;
} catch {
🤖 Prompt for AI Agents
In `@app/components/utils.ts` around lines 10 - 21, The formatDate function
currently assumes new Date(dateString) is valid; update formatDate to guard
against invalid dates by checking Number.isNaN(date.getTime()) (or
isNaN(date.getTime())) right after creating const date = new Date(dateString)
and before applying the IST offset and formatting; if the check fails return
'N/A' (or the current fallback) so you never format an Invalid Date and produce
NaN values. Ensure you keep the existing IST conversion and formatting logic in
formatDate unchanged except for this validity guard.

Comment on lines +170 to +188
// Fetch collections
const fetchCollections = async () => {
if (!apiKey) return;

setIsLoading(true);
try {
const response = await fetch('/api/collections', {
headers: { 'X-API-KEY': apiKey.key },
});

if (response.ok) {
const result = await response.json();
const collections = result.data || [];

// Enrich collections with cached names and live status
const enrichedCollections = await Promise.all(
collections.map(collection => enrichCollectionWithCache(collection))
);
setCollections(enrichedCollections);
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

Handle array responses from /api/collections. If the API returns a raw array (as the proxy route can), result.data is undefined and the UI will show zero collections. Mirror the document fetch pattern to accept both shapes.

Proposed fix
       if (response.ok) {
         const result = await response.json();
-        const collections = result.data || [];
+        const collections = Array.isArray(result) ? result : (result.data || []);
 
         // Enrich collections with cached names and live status
         const enrichedCollections = await Promise.all(
           collections.map(collection => enrichCollectionWithCache(collection))
📝 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
// Fetch collections
const fetchCollections = async () => {
if (!apiKey) return;
setIsLoading(true);
try {
const response = await fetch('/api/collections', {
headers: { 'X-API-KEY': apiKey.key },
});
if (response.ok) {
const result = await response.json();
const collections = result.data || [];
// Enrich collections with cached names and live status
const enrichedCollections = await Promise.all(
collections.map(collection => enrichCollectionWithCache(collection))
);
setCollections(enrichedCollections);
// Fetch collections
const fetchCollections = async () => {
if (!apiKey) return;
setIsLoading(true);
try {
const response = await fetch('/api/collections', {
headers: { 'X-API-KEY': apiKey.key },
});
if (response.ok) {
const result = await response.json();
const collections = Array.isArray(result) ? result : (result.data || []);
// Enrich collections with cached names and live status
const enrichedCollections = await Promise.all(
collections.map(collection => enrichCollectionWithCache(collection))
);
setCollections(enrichedCollections);
🤖 Prompt for AI Agents
In `@app/knowledge-base/page.tsx` around lines 170 - 188, The fetchCollections
function assumes the API returns an object with result.data, so when
/api/collections returns a raw array the UI shows zero items; change the parsing
to accept both shapes by treating the response as const collections =
result.data || result || [] before enriching—then continue to call
enrichCollectionWithCache on that array and pass the enrichedCollections to
setCollections.

@nishika26 nishika26 self-assigned this Feb 2, 2026
@nishika26 nishika26 added the enhancement New feature or request label Feb 2, 2026
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

🤖 Fix all issues with AI agents
In `@app/knowledge-base/page.tsx`:
- Around line 598-609: The onChange handler for the batch size input can set
batchSize to NaN because parseInt(e.target.value) may return NaN; update the
handler used with setBatchSize so you first coerce/validate the parsed value
(e.g., const n = parseInt(e.target.value); const valid = Number.isNaN(n) ? 1 :
n) and then call setBatchSize(Math.max(1, valid)) — modify the inline onChange
arrow function that currently uses setBatchSize(Math.max(1,
parseInt(e.target.value) || 1)) to ensure NaN is handled explicitly before
Math.max.
- Around line 270-275: In handleConfirmCreate add an explicit null guard for
apiKey before using apiKey.key: check if apiKey is null (e.g., if (!apiKey) {
handle the error/return early }) and only call fetch('/api/collections', ...)
when apiKey is non-null; update any UI state (close modal or set an error) in
the early-return branch so TypeScript no longer complains about accessing
apiKey.key and future regressions are prevented.
🧹 Nitpick comments (4)
app/knowledge-base/page.tsx (4)

9-17: Duplicate Document interface definition.

This interface is identical to the one in app/document/page.tsx. Consider importing it from there or extracting to a shared types file to avoid drift and reduce maintenance overhead.

-export interface Document {
-  id: string;
-  fname: string;
-  object_store_url: string;
-  signed_url?: string;
-  file_size?: number;
-  inserted_at?: string;
-  updated_at?: string;
-}
+import { Document } from '@/app/document/page';

79-141: Sequential API calls in enrichCollectionWithCache may cause slow loading.

This function is called via Promise.all on line 185-186, but internally it performs sequential API calls within loops (lines 99, 120). For users with many collections, this creates a waterfall of network requests during enrichment.

Consider batching job status requests or fetching all job statuses in a single call if the backend supports it, then matching locally.


367-372: Missing dependencies in useEffect may cause stale closures.

The effect depends on apiKey but calls fetchCollections and fetchDocuments which are recreated on each render. Either add them to the dependency array with useCallback, or inline the fetch logic.

+  const fetchCollections = useCallback(async () => {
+    // ... existing implementation
+  }, [apiKey]);
+
+  const fetchDocuments = useCallback(async () => {
+    // ... existing implementation  
+  }, [apiKey]);
+
   useEffect(() => {
     if (apiKey) {
       fetchCollections();
       fetchDocuments();
     }
-  }, [apiKey]);
+  }, [apiKey, fetchCollections, fetchDocuments]);

Alternatively, suppress the lint warning if the current behavior is intentional, but document why.


707-711: Use theme color instead of hardcoded hex value.

The delete button uses hardcoded #ef4444 while the project has colors.status.error (#dc2626) defined in the theme. Consider using the theme color for consistency.

                 style={{
                   backgroundColor: 'transparent',
-                  color: '#ef4444',
-                  border: '1px solid `#ef4444`',
+                  color: colors.status.error,
+                  border: `1px solid ${colors.status.error}`,
                 }}

Comment on lines +270 to +275
const response = await fetch('/api/collections', {
method: 'POST',
headers: {
'X-API-KEY': apiKey.key,
'Content-Type': 'application/json',
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add null guard for apiKey in handleConfirmCreate.

TypeScript will flag apiKey.key as potentially null since apiKey is typed as APIKey | null. Although the modal is only shown after handleCreateClick validates apiKey, adding an explicit guard improves type safety and prevents future regressions.

   const handleConfirmCreate = async () => {
     setShowConfirmCreate(false);
+    if (!apiKey) return;
     setIsCreating(true);
     try {
       const response = await fetch('/api/collections', {
🤖 Prompt for AI Agents
In `@app/knowledge-base/page.tsx` around lines 270 - 275, In handleConfirmCreate
add an explicit null guard for apiKey before using apiKey.key: check if apiKey
is null (e.g., if (!apiKey) { handle the error/return early }) and only call
fetch('/api/collections', ...) when apiKey is non-null; update any UI state
(close modal or set an error) in the early-return branch so TypeScript no longer
complains about accessing apiKey.key and future regressions are prevented.

Comment on lines +598 to +609
<input
type="number"
value={batchSize}
onChange={(e) => setBatchSize(Math.max(1, parseInt(e.target.value) || 1))}
min="1"
className="w-full px-4 py-2 rounded-md border text-sm"
style={{
borderColor: colors.border,
backgroundColor: colors.bg.secondary,
color: colors.text.primary,
}}
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle NaN when parsing batch size input.

parseInt(e.target.value) returns NaN for non-numeric input, and Math.max(1, NaN) evaluates to NaN. This could result in an invalid batchSize being sent to the API.

-                onChange={(e) => setBatchSize(Math.max(1, parseInt(e.target.value) || 1))}
+                onChange={(e) => {
+                  const parsed = parseInt(e.target.value, 10);
+                  setBatchSize(Number.isNaN(parsed) ? 1 : Math.max(1, parsed));
+                }}
📝 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
<input
type="number"
value={batchSize}
onChange={(e) => setBatchSize(Math.max(1, parseInt(e.target.value) || 1))}
min="1"
className="w-full px-4 py-2 rounded-md border text-sm"
style={{
borderColor: colors.border,
backgroundColor: colors.bg.secondary,
color: colors.text.primary,
}}
/>
<input
type="number"
value={batchSize}
onChange={(e) => {
const parsed = parseInt(e.target.value, 10);
setBatchSize(Number.isNaN(parsed) ? 1 : Math.max(1, parsed));
}}
min="1"
className="w-full px-4 py-2 rounded-md border text-sm"
style={{
borderColor: colors.border,
backgroundColor: colors.bg.secondary,
color: colors.text.primary,
}}
/>
🤖 Prompt for AI Agents
In `@app/knowledge-base/page.tsx` around lines 598 - 609, The onChange handler for
the batch size input can set batchSize to NaN because parseInt(e.target.value)
may return NaN; update the handler used with setBatchSize so you first
coerce/validate the parsed value (e.g., const n = parseInt(e.target.value);
const valid = Number.isNaN(n) ? 1 : n) and then call setBatchSize(Math.max(1,
valid)) — modify the inline onChange arrow function that currently uses
setBatchSize(Math.max(1, parseInt(e.target.value) || 1)) to ensure NaN is
handled explicitly before Math.max.

@nishika26 nishika26 changed the title Feature/knowledge base Knowledge base: Creation and deletion UI Feb 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants