Skip to content

feat: Added URLs as document objects#1783

Open
AshishViradiya153 wants to merge 3 commits intomfts:mainfrom
AshishViradiya153:309/URLsAsDocument
Open

feat: Added URLs as document objects#1783
AshishViradiya153 wants to merge 3 commits intomfts:mainfrom
AshishViradiya153:309/URLsAsDocument

Conversation

@AshishViradiya153
Copy link
Contributor

@AshishViradiya153 AshishViradiya153 commented Jul 25, 2025

Summary by CodeRabbit

  • New Features

    • Added support for uploading and viewing external links as documents, including a dedicated "Link" tab in the document upload modal.
    • Introduced a new Link Viewer with auto-redirect countdown and domain preview for link-type documents.
    • Implemented link-specific analytics and visitor tracking, including a new Link Analytics dashboard and updated visitor tables with redirect time display.
    • Added API endpoints to record link clicks and fetch link analytics data.
    • Enabled exporting link visit data with appropriate placeholders for duration and completion.
  • Improvements

    • Updated icons and UI elements to visually distinguish link-type documents.
    • Enhanced document and view handling to support link-type documents across analytics, exports, API responses, and UI components.
    • Refined conditional rendering logic to exclude link-type documents from certain features like orientation change and download options.
  • Bug Fixes

    • Ensured correct file retrieval and response handling for link-type document versions.
    • Fixed UI consistency and conditional checks related to link-type documents.
  • Database

    • Added a new timestamp field to track link redirection times for improved analytics.

@AshishViradiya153 AshishViradiya153 requested a review from mfts as a code owner July 25, 2025 06:37
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 25, 2025

Walkthrough

These changes introduce comprehensive support for "link"-type documents across the application. This includes the ability to upload external links as documents, render and auto-redirect to links in the viewer, track and analyze link visits, and export link-specific analytics. The backend, frontend, and database schema are all updated to accommodate the new "link" document type, with conditional logic and UI adjustments throughout the codebase.

Changes

File(s) / Group Change Summary
app/api/views-dataroom/route.ts, app/api/views/route.ts Updated to support fetching and returning file data for "link" document versions, with nested selects and conditional logic.
components/documents/add-document-modal.tsx Added UI and logic for uploading external links as documents, with validation, POST handling, and analytics integration.
components/documents/document-header.tsx Refactored conditional rendering to exclude "link" type from certain UI actions using .includes() checks.
components/documents/link-analytics.tsx New component to display analytics for link-type documents, with SWR data fetching and team member exclusion toggle.
components/analytics/links-table.tsx Updated icon usage and reordered imports; minor styling adjustment.
components/links/links-table.tsx Added isLink prop to LinksVisitors component for rows representing "link" document types.
components/links/links-visitors.tsx Added isLink prop; displays redirect time badge/tooltip for link documents instead of duration.
components/view/view-data.tsx Added conditional rendering for "link" file type using new LinkViewer component.
components/view/viewer/link-viewer.tsx New component to preview and auto-redirect to external links, with countdown, analytics, and UI elements.
components/visitors/visitors-table.tsx Added isLink prop; displays "Redirect Time" and badge/tooltip for link documents.
lib/trigger/export-visits.ts Updated export logic to handle "link" documents with static placeholders, skipping duration/user agent lookups.
lib/utils/get-file-icon.tsx Added LinkIcon for "link" file type in icon rendering function.
pages/api/links/[id]/visits.ts Refactored and extended to handle "link" document visits, returning redirect info and bypassing duration calculations.
pages/api/record_link_click.ts New API endpoint to record link click events by updating the redirectAt timestamp for a view.
pages/api/teams/[teamId]/documents/[id]/link-analytics.ts New API endpoint to fetch analytics data for link-type documents, with internal/external filtering.
pages/api/teams/[teamId]/documents/[id]/views/index.ts Added getLinkViews function and conditional logic for processing views of "link" documents.
pages/documents/[id]/index.tsx Integrated LinkAnalytics for link documents; updated VisitorsTable to handle link-specific display.
prisma/migrations/20250724143745_added_redirect_at/migration.sql, prisma/schema/schema.prisma Added redirectAt field to the View model/table for tracking link redirections; minor formatting.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI (AddDocumentModal)
    participant API (/documents)
    participant DB

    User->>UI (AddDocumentModal): Selects "Link" tab and submits URL
    UI (AddDocumentModal)->>API (/documents): POST new document (type: "link", url)
    API (/documents)->>DB: Create document record (type: "link")
    DB-->>API (/documents): Document created
    API (/documents)-->>UI (AddDocumentModal): Success response
    UI (AddDocumentModal)-->>User: Redirects to new document page
Loading
sequenceDiagram
    participant User
    participant UI (ViewData/LinkViewer)
    participant API (/views)
    participant DB

    User->>UI (ViewData): Opens link-type document
    UI (ViewData)->>API (/views): Fetches document version and file
    API (/views)->>DB: Query document version (type: "link")
    DB-->>API (/views): Returns version with link URL
    API (/views)-->>UI (ViewData): Sends link data
    UI (ViewData)-->>User: Shows LinkViewer with countdown
    Note over UI (ViewData),User: After countdown or button click
    UI (ViewData)->>API (/record_link_click): Records redirect event
    API (/record_link_click)->>DB: Update view.redirectAt
    DB-->>API (/record_link_click): Success
    API (/record_link_click)-->>UI (ViewData): Acknowledge
    UI (ViewData)-->>User: Redirects to external link
Loading
sequenceDiagram
    participant User
    participant UI (LinkAnalytics)
    participant API (/link-analytics)
    participant DB

    User->>UI (LinkAnalytics): Opens analytics tab for link document
    UI (LinkAnalytics)->>API (/link-analytics): GET analytics data
    API (/link-analytics)->>DB: Query link views (optionally filter internal users)
    DB-->>API (/link-analytics): Returns views data
    API (/link-analytics)-->>UI (LinkAnalytics): Sends analytics
    UI (LinkAnalytics)-->>User: Displays stats (views, clicks, last redirect)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18–25 minutes

Complexity label: Moderate

  • The changes span backend, frontend, and database schema, introducing a new document type and associated logic/UI.
  • Most changes are medium in complexity, with some new components and endpoints but no deeply nested or highly complex logic.
  • Estimated review time reflects the need to check integration points, new components, and API/data flow.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-25T08_00_52_596Z-debug-0.log

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c21b2a6 and f11d655.

📒 Files selected for processing (1)
  • components/visitors/visitors-table.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/visitors/visitors-table.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@vercel
Copy link

vercel bot commented Jul 25, 2025

@AshishViradiya153 is attempting to deploy a commit to the mftsio Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

@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: 3

🧹 Nitpick comments (4)
components/view/view-data.tsx (1)

113-121: Consider defensive programming for linkUrl prop

The conditional rendering logic is well-structured and properly placed in the rendering chain. However, the use of non-null assertion (viewData.file!) could be risky if viewData.file is unexpectedly null or undefined for link-type documents.

Consider adding a null check or providing a fallback:

-      linkUrl={viewData.file!}
+      linkUrl={viewData.file || ""}

Alternatively, add validation earlier in the conditional:

-  ) : viewData.fileType === "link" ? (
+  ) : viewData.fileType === "link" && viewData.file ? (
pages/api/teams/[teamId]/documents/[id]/link-analytics.ts (1)

72-94: Well-implemented filtering logic with good performance considerations.

The code efficiently handles view filtering with proper team member exclusion. Using a Set for email lookups is a good performance optimization.

Consider adding early return for empty views to avoid unnecessary team user queries:

        const allViews = document.views ?? [];
        if (allViews.length === 0) {
            return res.status(200).json({ views: [], totalViews: 0 });
        }

        let excludedEmails = new Set<string>();

-        if (excludeTeamMembers) {
+        if (excludeTeamMembers && allViews.length > 0) {
            const teamUsers = await prisma.user.findMany({

This avoids the database query when there are no views to filter.

components/documents/add-document-modal.tsx (1)

517-634: Comprehensive link upload implementation with good error handling.

The function follows the established pattern from handleNotionUpload with proper validation, error handling, analytics tracking, and support for both regular documents and dataroom integration.

Consider extracting the URL validation regex to a shared constant to avoid duplication:

+const URL_VALIDATION_REGEX = /^(https?:\/\/)?([a-zA-Z0-9-]+\.){1,}[a-zA-Z]{2,}([a-zA-Z0-9-._~:/?#[\]@!$&'()*+,;=]+)?$/;

-    const isValidURL =
-      /^(https?:\/\/)?([a-zA-Z0-9-]+\.){1,}[a-zA-Z]{2,}([a-zA-Z0-9-._~:/?#[\]@!$&'()*+,;=]+)?$/;
+    const isValidURL = URL_VALIDATION_REGEX;

This regex is also used in handleNotionUpload, so sharing it would improve maintainability.

components/view/viewer/link-viewer.tsx (1)

90-99: Good manual link opening implementation with one UX consideration.

The function properly clears the auto-redirect timer, tracks the click, and opens the link securely in a new tab. However, redirecting the current window to the Papermark homepage might not be the best UX choice.

Consider alternative approaches for the current window after opening the external link:

- window.location.replace("https://www.papermark.com/home");
+ // Option 1: Close the tab if possible
+ window.close();
+ // Option 2: Show a completion message instead of redirecting
+ // Option 3: Allow user to choose what happens next

The automatic redirect to the homepage might be unexpected for users who want to keep the original window context.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f51892 and 899ca7b.

📒 Files selected for processing (20)
  • app/api/views-dataroom/route.ts (3 hunks)
  • app/api/views/route.ts (3 hunks)
  • components/analytics/links-table.tsx (3 hunks)
  • components/documents/add-document-modal.tsx (4 hunks)
  • components/documents/document-header.tsx (4 hunks)
  • components/documents/link-analytics.tsx (1 hunks)
  • components/links/links-table.tsx (1 hunks)
  • components/links/links-visitors.tsx (2 hunks)
  • components/view/view-data.tsx (2 hunks)
  • components/view/viewer/link-viewer.tsx (1 hunks)
  • components/visitors/visitors-table.tsx (3 hunks)
  • lib/trigger/export-visits.ts (2 hunks)
  • lib/utils/get-file-icon.tsx (2 hunks)
  • pages/api/links/[id]/visits.ts (1 hunks)
  • pages/api/record_link_click.ts (1 hunks)
  • pages/api/teams/[teamId]/documents/[id]/link-analytics.ts (1 hunks)
  • pages/api/teams/[teamId]/documents/[id]/views/index.ts (2 hunks)
  • pages/documents/[id]/index.tsx (4 hunks)
  • prisma/migrations/20250724143745_added_redirect_at/migration.sql (1 hunks)
  • prisma/schema/schema.prisma (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/trigger/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/rule-trigger-typescript.mdc)

**/trigger/**/*.ts: You MUST use @trigger.dev/sdk/v3 when implementing Trigger.dev tasks.
You MUST NEVER use client.defineJob in Trigger.dev task files, as it is deprecated and will break the application.
You MUST export every task, including subtasks, in Trigger.dev task files.
If you are able to generate an example payload for a task, do so.
When implementing a Trigger.dev task, always use the task function from @trigger.dev/sdk/v3 and follow the correct pattern for task definition.
When implementing scheduled (cron) tasks, use schedules.task from @trigger.dev/sdk/v3 and follow the correct pattern for schedule definition.
When implementing schema-validated tasks, use schemaTask from @trigger.dev/sdk/v3 and provide a schema using Zod or another supported library.
When triggering tasks from backend code, use the tasks.trigger, tasks.batchTrigger, or tasks.triggerAndPoll methods from @trigger.dev/sdk/v3 and use type-only imports for type safety.
When triggering a task from inside another task, use the correct methods (trigger, batchTrigger, triggerAndWait, batchTriggerAndWait) on the task instance as shown in the guide.
When using metadata in tasks, use the metadata API from @trigger.dev/sdk/v3 only inside run functions or task lifecycle hooks.
When using idempotency, use the idempotencyKeys API from @trigger.dev/sdk/v3 and provide an idempotencyKey when triggering tasks.
When logging inside tasks, use the logger API from @trigger.dev/sdk/v3 and provide a message and a key-value object.

Files:

  • lib/trigger/export-visits.ts
🧬 Code Graph Analysis (6)
components/links/links-visitors.tsx (2)
components/ui/tooltip.tsx (1)
  • BadgeTooltip (53-107)
lib/utils.ts (2)
  • timeAgo (153-171)
  • durationFormat (173-183)
components/visitors/visitors-table.tsx (4)
components/view/viewer/dataroom-viewer.tsx (1)
  • DocumentVersion (47-54)
components/ui/table.tsx (1)
  • TableHead (114-114)
components/ui/tooltip.tsx (1)
  • BadgeTooltip (53-107)
lib/utils.ts (2)
  • timeAgo (153-171)
  • durationFormat (173-183)
components/view/view-data.tsx (1)
components/view/viewer/link-viewer.tsx (1)
  • LinkViewer (26-182)
lib/trigger/export-visits.ts (1)
lib/tinybird/pipes.ts (3)
  • getViewPageDuration (24-36)
  • getViewUserAgent (95-107)
  • getViewUserAgent_v2 (79-93)
components/documents/link-analytics.tsx (3)
components/view/viewer/dataroom-viewer.tsx (1)
  • DocumentVersion (47-54)
lib/utils.ts (1)
  • fetcher (36-50)
components/ui/card.tsx (2)
  • Card (80-80)
  • CardContent (85-85)
components/documents/add-document-modal.tsx (2)
components/ui/card.tsx (5)
  • Card (80-80)
  • CardHeader (81-81)
  • CardTitle (83-83)
  • CardDescription (84-84)
  • CardContent (85-85)
components/ui/button.tsx (1)
  • Button (71-71)
🔇 Additional comments (52)
prisma/schema/schema.prisma (1)

286-288: LGTM: Well-structured schema addition for link tracking.

The optional redirectAt field is properly typed and documented. The placement within the View model is logical and aligns with the PR's objective to support link-type documents.

prisma/migrations/20250724143745_added_redirect_at/migration.sql (1)

1-2: LGTM: Correct migration for schema update.

The migration properly adds the redirectAt column with appropriate timestamp precision and nullable constraint, matching the schema definition.

components/links/links-table.tsx (1)

899-899: LGTM: Proper conditional prop for link-type documents.

The isLink prop is correctly set based on both targetType and primaryVersion?.type, ensuring proper identification of link documents for specialized rendering.

lib/utils/get-file-icon.tsx (2)

1-1: LGTM: Proper import addition for link icon.

The LinkIcon import is correctly added to support the new link file type case.


67-68: LGTM: Consistent file type handling for links.

The new "link" case follows the established pattern and properly returns the LinkIcon with the provided className.

components/analytics/links-table.tsx (4)

25-25: LGTM: Icon import updated for consistency.

The change from Link2Icon to LinkIcon aligns with the icon usage in other components.


30-34: LGTM: Improved import organization.

Consolidating utility imports together improves code organization and readability.


318-318: LGTM: CSS class reordering.

The class order change doesn't affect functionality and may follow a consistent style guide.


368-368: LGTM: Consistent icon usage.

The JSX update to use LinkIcon matches the import change and maintains consistency across components.

components/view/view-data.tsx (1)

28-28: LGTM: Clean import statement

The import follows the established pattern and correctly references the new LinkViewer component.

pages/documents/[id]/index.tsx (4)

15-15: LGTM: Clean import statement

The import follows the established pattern and correctly references the new LinkAnalytics component.


93-99: LGTM: Logical exclusion of link documents from stats

The condition correctly excludes both video and link document types from the general StatsComponent, which makes sense as these document types have specialized analytics components.


110-116: LGTM: Consistent implementation pattern

The LinkAnalytics conditional rendering follows the same pattern as VideoAnalytics and properly passes all required props. The placement in the component hierarchy is logical.


130-130: LGTM: Consistent prop pattern

The addition of the isLink prop follows the same pattern as the existing isVideo prop and enables appropriate handling of link-type documents in the visitors table.

pages/api/teams/[teamId]/documents/[id]/views/index.ts (2)

170-188: LGTM: Well-structured function for link document views

The getLinkViews function follows the established pattern of other view processing functions and appropriately handles link documents with fixed values that make sense for this document type:

  • Empty duration data (links don't have page-based viewing)
  • Zero total duration (no time-based metrics)
  • 100% completion rate (accessing a link is considered complete)

The version matching logic is consistent with existing implementations.


319-320: LGTM: Logical conditional placement

The conditional for link documents is appropriately placed in the type-specific handling chain and correctly calls getLinkViews with the proper parameters.

app/api/views/route.ts (2)

101-112: LGTM: Efficient nested query structure

The enhanced Prisma query correctly fetches the document version file data needed for link document handling. The nested structure with proper filtering and selective field retrieval is efficient and well-structured.


656-657: LGTM: Correct inclusion of link type

The addition of "link" type to the conditional check ensures that link documents properly return their file data in the API response, which is necessary for the LinkViewer component to function correctly.

pages/api/record_link_click.ts (1)

1-55: Enhance rate limiting and duplicate-click prevention in record_link_click.ts

The endpoint is currently public-by-design for click tracking, but to mitigate abuse you can:

• Add per-IP rate limiting with the existing ratelimit helper
• Extract the client IP via ipAddress(req) from @vercel/functions
• Short-circuit when view.redirectAt is already set to avoid duplicate updates

Example diff:

 import { NextApiRequest, NextApiResponse } from "next";
 import { z } from "zod";
 import prisma from "@/lib/prisma";
 import { log } from "@/lib/utils";
+import { ratelimit } from "@/lib/redis";
+import { ipAddress } from "@vercel/functions";

 export default async function handle(
     req: NextApiRequest,
     res: NextApiResponse,
 ) {
     if (req.method !== "POST") {
         res.status(405).json({ message: "Method Not Allowed" });
         return;
     }

+    // Rate limit by IP
+    const ip = ipAddress(req);
+    const { success } = await ratelimit(10, "1 m").limit(`record-click:${ip}`);
+    if (!success) {
+        return res.status(429).json({ message: "Too many requests" });
+    }

     const { viewId, linkId } = req.body as {
         viewId: string;
         linkId: string;
     };

     const result = bodyValidation.safeParse({ viewId, linkId });
     if (!result.success) {
         return res.status(400).json({ error: `Invalid body: ${result.error.message}` });
     }

     try {
         const link = await prisma.link.findUnique({ where: { id: linkId } });
         if (!link) return res.status(404).json({ message: "Link not found" });

         const view = await prisma.view.findUnique({
             where: { id: viewId },
         });
         if (!view || view.linkId !== linkId) {
             return res.status(404).json({ message: "View not found" });
         }

+        // Prevent duplicate recordings
+        if (view.redirectAt) {
+            return res
+                .status(200)
+                .json({ message: "Click already recorded", view });
+        }

         const updated = await prisma.view.update({
             where: { id: viewId },
             data: { redirectAt: new Date().toISOString() },
         });

         res.status(200).json({ message: "Link click recorded", view: updated });
     } catch (error) {
         log({
             message: `Failed to record link click for ${linkId}.\n\n ${error}`,
             type: "error",
             mention: true,
         });
         res.status(500).json({ message: (error as Error).message });
     }
 }

Please confirm whether this endpoint should remain publicly accessible for tracking (no auth) or if an authentication layer is required.

components/links/links-visitors.tsx (3)

2-2: LGTM! Import additions are appropriate.

The new imports for durationFormat, timeAgo, and BadgeTooltip are necessary for the conditional rendering functionality.

Also applies to: 7-7


13-13: LGTM! Prop addition follows best practices.

The isLink prop with a default value of false ensures backward compatibility while enabling the new link-specific functionality.

Also applies to: 17-17


42-57: redirectAt field is correctly typed and available

Verified that:

  • The Prisma schema defines redirectAt as an optional DateTime? (see prisma/schema/schema.prisma)
  • Migration 20250724143745_added_redirect_at adds the column to the View table
  • pages/api/record_link_click.ts updates redirectAt on link clicks
  • Front-end components (components/links/links-visitors.tsx and components/visitors/visitors-table.tsx) reference view.redirectAt without any TypeScript errors

The conditional rendering for link vs non-link views is clean and the tooltip behavior is spot on. Great work!

components/visitors/visitors-table.tsx (2)

67-67: LGTM! Consistent prop pattern.

The isLink prop follows the same pattern as the LinksVisitors component, maintaining consistency across the codebase.

Also applies to: 71-71


143-145: Excellent UX improvement with conditional table header.

The header clearly indicates whether users are viewing "Redirect Time" or "Visit Duration", providing important context for the data displayed.

pages/api/teams/[teamId]/documents/[id]/link-analytics.ts (3)

1-12: LGTM! Clean imports and well-defined interface.

The API response interface is properly typed and the imports are appropriate for the functionality.


18-28: LGTM! Proper method validation and authentication.

The authentication flow follows Next.js best practices with appropriate error responses.


37-65: Excellent access control and validation logic.

The implementation properly:

  • Verifies team membership through the Prisma query
  • Validates document existence and access
  • Ensures the document is of type "link"
  • Returns appropriate HTTP status codes
app/api/views-dataroom/route.ts (3)

123-134: LGTM! Nested selection enables access to linked document files.

The nested query structure properly retrieves the document version file data needed for link-type documents.


836-840: Proper handling of link-type document versions.

The logic correctly retrieves the file from the linked document's first version with appropriate null checks.


937-938: LGTM! Condition update includes link types appropriately.

Adding "link" to the file return condition ensures link-type documents return their file data consistently with other supported types.

lib/trigger/export-visits.ts (2)

198-198: LGTM! Necessary query addition for document type.

Adding the document type to the query enables the conditional processing logic for link documents.


292-398: Excellent conditional processing for link vs non-link documents.

The implementation appropriately handles the fundamental differences:

For link documents:

  • Avoids unnecessary Tinybird API calls (performance benefit)
  • Uses sensible placeholder values (0 duration, 100% completion)
  • Maintains consistent CSV structure

For non-link documents:

  • Preserves existing rate-limited API behavior
  • Calculates actual duration and completion metrics

The logic is clean, well-structured, and maintains backward compatibility while adding link support.

components/documents/document-header.tsx (4)

540-542: Good refactoring to improve readability and exclude link documents from orientation changes.

The refactoring from explicit inequality comparisons to array membership checks using .includes() makes the code more readable and maintainable. Excluding "link" documents from orientation changes is logical since external links don't have document orientation.


565-565: Appropriate exclusion of link documents from version uploads.

Correctly excludes "link" documents from the "Upload new version" functionality, which makes sense since external links cannot have file versions uploaded to them.


728-754: Correctly excludes link documents from download-only toggle.

The "Set download only/viewable" option is appropriately excluded for "link" documents since external links inherently redirect users to external sites and cannot be restricted to download-only mode.


792-801: Properly excludes link documents from download functionality.

Excluding "link" documents from the "Download latest version" option is correct since external links cannot be downloaded as files. The refactoring also improves code readability.

components/documents/add-document-modal.tsx (5)

75-75: State variable addition follows existing patterns.

The linkUrl state variable is consistent with the existing notionLink pattern and follows React best practices.


507-515: Well-implemented URL-to-filename helper function.

Good implementation with proper error handling, sensible fallback behavior, and clean URL parsing logic that removes common prefixes like "www.".


639-639: Proper state cleanup for link URL.

Correctly adds linkUrl state reset to the modal state clearing function, maintaining consistency with other state variables.


679-682: Clean tab list expansion for link functionality.

Properly updates the tab grid layout and adds the new "Link" tab trigger with appropriate styling and labeling.


814-861: Well-structured Link tab implementation.

The new tab content follows established UI patterns with proper form structure, semantic HTML (type="url"), appropriate loading states, and helpful user guidance. The implementation is consistent with existing document and notion tabs.

components/view/viewer/link-viewer.tsx (4)

17-24: Well-implemented domain extraction with proper error handling.

Good performance optimization by placing the function outside the component scope. The error handling and fallback behavior are appropriate for URL parsing.


48-61: Excellent use of sendBeacon for reliable link click tracking.

Using navigator.sendBeacon() is the correct approach for tracking as it ensures the request is sent even if the page unloads immediately. The conditional checks and error handling are well implemented.


63-68: Robust redirect implementation with duplicate protection.

The use of useCallback, duplicate redirect protection via hasRedirected ref, and window.location.replace() creates a reliable redirect mechanism that prevents issues with browser navigation.


70-88: Well-implemented auto-redirect with proper cleanup.

The timer implementation correctly handles component unmounting with the isMounted flag, provides user feedback with countdown updates, and properly cleans up both the timeout and interval to prevent memory leaks.

components/documents/link-analytics.tsx (3)

51-56: Clean implementation of toggle functionality with URL sync.

Properly synchronizes the toggle state with URL parameters, enabling bookmarkable and shareable links. The use of URLSearchParams ensures correct query parameter handling.


112-122: Robust analytics calculations with proper null handling.

The calculations correctly handle the redirectAt field specific to link documents, with appropriate filtering, sorting, and type checking for edge cases.


124-142: Well-structured statistics with appropriate formatting.

The statistics are properly configured for link documents with human-readable time formatting and appropriate handling of cases where no redirections have occurred.

pages/api/links/[id]/visits.ts (4)

18-21: Good early method validation with proper HTTP response.

Adding upfront method validation with appropriate headers and status codes is a good practice that prevents unnecessary processing.


32-66: Improved query structure with proper validation.

Good additions including the document type field in the query, explicit document existence checking, and appropriate 404 responses for missing documents.


96-131: Appropriate conditional handling for link documents.

The conditional logic correctly handles link documents by providing appropriate placeholder values (empty duration data, 100% completion rate) while maintaining existing functionality for other document types.


133-140: Consistent response structure and error handling.

The response maintains the expected data structure while preserving robust error handling and logging patterns.

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