Skip to content

UN-3154 [FEAT] Card based layout to list Pipelines and API Deployments#1769

Merged
chandrasekharan-zipstack merged 31 commits intomainfrom
UN-3154-card-grid-view-pipelines-deployments
Mar 2, 2026
Merged

UN-3154 [FEAT] Card based layout to list Pipelines and API Deployments#1769
chandrasekharan-zipstack merged 31 commits intomainfrom
UN-3154-card-grid-view-pipelines-deployments

Conversation

@chandrasekharan-zipstack
Copy link
Copy Markdown
Contributor

@chandrasekharan-zipstack chandrasekharan-zipstack commented Jan 29, 2026

What

Introduces a reusable CardGridView component for displaying Pipelines and API Deployments in a modern card-based layout with execution status tracking, pagination, search, and scroll restoration.

Why

The existing table-based view does not effectively display rich metadata like execution statuses, connector icons, and scheduling info. Cards provide better UX for scanning pipeline/deployment details at a glance.

How

Commits

# Commit Description
1 61132896e Backend: Add execution status tracking and list enhancements
2 ec69cc3cb Frontend: Add reusable CardGridView component
3 f938be528 Frontend: Refactor API Deployments to use CardGridView
4 558dbb81d Frontend: Refactor Pipelines to use CardGridView
5 ab0190ca1 Frontend: Improve navigation with scroll restoration
6 4fddcda01 Fix: Address CodeRabbit review comments

Backend Changes

  • Add WorkflowExecution.get_last_run_statuses() method with dynamic PARTIAL_SUCCESS computation
  • Extend serializers with last_5_run_statuses, run_count, last_run_time, next_run_time
  • Add source_instance_name and destination_instance_name to pipeline representation
  • Enable CustomPagination and search filters for both endpoints

Frontend Changes

  • New CardGridView widget with card/list modes, expand/collapse, status badges
  • PipelineCardConfig with StatusPills showing clickable execution statuses
  • ApiDeploymentCardConfig with similar card layout
  • Scroll restoration: clicking back from workflow/logs returns to the original card
  • Search and pagination integrated into list views

Can this PR break any existing features? If yes, please list possible items. If no, please explain why.

No. The changes replace the table view with card view but maintain the same underlying data and functionality. All existing actions (edit, delete, share, toggle, etc.) are preserved.

Database Migrations

Not applicable.

Env Config

Not applicable.

Relevant Docs

Related Issues or PRs

Dependencies Versions

Notes on Testing

  • Navigate to Pipelines list - verify cards display with all metadata
  • Navigate to API Deployments list - verify cards display correctly
  • Click status pill on a card -> navigate to logs -> click back -> verify card scrolls into view
  • Click workflow link on a card -> navigate to workflow -> click back -> verify card scrolls into view
  • Test search functionality filters cards correctly
  • Test pagination navigates through pages

Screenshots

image

Checklist

I have read and understood the Contribution Guidelines.

🤖 Generated with Claude Code

- Add WorkflowExecution.get_last_run_statuses() method to fetch last N
  executions with PARTIAL_SUCCESS computed dynamically
- Add last_5_run_statuses, run_count, last_run_time to API deployment serializer
- Add last_5_run_statuses, next_run_time to pipeline serializer
- Add source/destination instance names to pipeline representation
- Enable pagination (CustomPagination) for both pipelines and deployments
- Add search filter by name for both endpoints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create CardGridView widget for displaying items in card/list layouts
- Add CardItem component with expand/collapse, status badges, actions
- Add LoadingSkeleton for loading states
- Support pagination, search, scroll restoration, and card configuration
- Add dateFormatter helper for consistent date formatting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ApiDeploymentCardConfig for card layout configuration
- Refactor ApiDeployment to use CardGridView with pagination
- Update api-deployments-service with pagination and search support
- Refactor Body component to use CardGridView instead of Table
- Add search functionality to Header component
- Update Layout to pass card configuration and pagination props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add PipelineCardConfig with StatusPills component for execution status
- Display source/destination connector icons and instance names
- Show last 5 run statuses with clickable navigation to execution logs
- Add schedule display, next run time, and run count
- Support pagination and search functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update MenuLayout to forward scrollToCardId state on back navigation
- Pass scrollToCardId from workflow links in card configs
- Add back route with state to ExecutionLogs for scroll restoration
- Remove redundant back button from DetailedLogs header
- Add previousRoute and previousRouteState props to ToolNavBar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds server-driven pagination and search to deployments and pipelines, augments serializers with run-count and recent-run metadata, implements WorkflowExecution.get_last_run_statuses, and replaces table lists with a reusable CardGridView UI plus routing/state-preservation for navigation, search, and pagination.

Changes

Cohort / File(s) Summary
Backend: Viewsets
backend/api_v2/api_deployment_views.py, backend/pipeline_v2/views.py
Enable CustomPagination on viewsets; add optional ?search= filtering (__icontains); PipelineViewSet adds OrderingFilter, ordering_fields, and default ordering including last_run_time and created_at.
Backend: Serializers
backend/api_v2/serializers.py, backend/pipeline_v2/serializers/crud.py
Add SerializerMethodFields for recent-run metadata: run_count, last_run_time, last_5_run_statuses to deployments; add last_5_run_statuses and next_run_time to PipelineSerializer and include connector instance name context.
Backend: Workflow model
backend/workflow_manager/workflow_v2/models/execution.py
Add WorkflowExecution.get_last_run_statuses(pipeline_id, limit=5) returning recent executions with per-run success/failure counts and mapping mixed outcomes to PARTIAL_SUCCESS.
Frontend: Card grid core
frontend/src/components/widgets/card-grid-view/...
CardGridView.jsx, CardItem.jsx, LoadingSkeleton.jsx, CardFieldComponents.jsx, CardGridView.css, index.js
Introduce reusable CardGridView system: CardGridView, CardItem, LoadingSkeleton, CardFieldComponents, responsive CSS and barrel exports; supports grid/list modes, expansion, and pagination.
Frontend: Deployments UI & service
frontend/src/components/deployments/api-deployment/ApiDeployment.jsx, .../ApiDeploymentCardConfig.jsx, .../api-deployments-service.js
Replace table with card-based UI and createApiDeploymentCardConfig; add server pagination/search state and handlers; update service call to accept page, page_size, and optional search.
Frontend: Pipelines UI & config
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx, .../PipelineCardConfig.jsx
Switch pipelines to card-based layout with createPipelineCardConfig and StatusPills; add server pagination/search, optimistic toggles, and centralized per-card action handlers.
Frontend: Layout & wiring
frontend/src/components/deployments/body/Body.jsx, .../header/Header.jsx, .../layout/Layout.jsx
Body now consumes cardConfig and renders CardGridView; Header/Layout accept and propagate search and pagination props to support card lists and listMode.
Frontend: Navigation & logs state
frontend/src/components/logging/execution-logs/ExecutionLogs.jsx, .../detailed-logs/DetailedLogs.jsx, .../tool-nav-bar/ToolNavBar.jsx, frontend/src/layouts/menu-layout/MenuLayout.jsx
Preserve and pass previous route state for back-navigation and scroll restoration (previousRouteState); remove back button UI in DetailedLogs; make menu back navigation conditional on location.state.from.
Frontend: Helpers & formatters
frontend/src/helpers/GetStaticData.js, frontend/src/helpers/dateFormatter.js
Add shortenApiEndpoint and improved number formatting; add date utilities: formatCompactDate, formatSmartDate, formatFullDate, checkIsToday.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant Frontend
participant API
participant DB
Client->>Frontend: Request list (page, pageSize, search)
Frontend->>API: GET /api/.../?page=...&page_size=...&search=...
API->>DB: Query deployments/pipelines with optional icontains filter, apply limit/offset
DB-->>API: Return rows + total count
API->>Frontend: Return paginated response (results, count)
Frontend->>Frontend: Render CardGridView, show pagination and status pills

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.22% 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
Title check ✅ Passed The PR title clearly and specifically summarizes the main change: introducing a card-based layout for Pipelines and API Deployments.
Description check ✅ Passed The PR description comprehensively covers all required sections (What, Why, How, Breaking Changes, Testing, etc.) with detailed backend and frontend changes listed.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch UN-3154-card-grid-view-pipelines-deployments

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
Copy Markdown
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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

53-53: Avoid using require() inside component body.

Using require() inside a React component is non-idiomatic and can cause issues with tree-shaking and bundling. This should be a static import at the top of the file.

♻️ Proposed fix: Use static import
  import { LogsModal } from "../log-modal/LogsModal.jsx";
+ import { fetchExecutionLogs } from "../log-modal/fetchExecutionLogs.js";
  import { EtlTaskDeploy } from "../etl-task-deploy/EtlTaskDeploy.jsx";

  // ... inside component, remove:
- const { fetchExecutionLogs } = require("../log-modal/fetchExecutionLogs.js");
🤖 Fix all issues with AI agents
In `@frontend/src/components/logging/execution-logs/ExecutionLogs.jsx`:
- Around line 29-30: In ExecutionLogs.jsx remove the unused "type" variable from
the destructuring of useParams() so only the needed "id" is extracted (i.e.
change the const { id, type } = useParams() usage to only extract id); this
keeps the component clean since the child component DetailedLogs already calls
useParams() to get type.

In
`@frontend/src/components/pipelines-or-deployments/pipelines/PipelineCardConfig.jsx`:
- Around line 199-201: The code uses cronstrue.toString(pipeline.cron_string)
directly which can throw on invalid cron expressions; wrap the conversion in a
try-catch inside the component (the logic that sets scheduleDisplay) so that if
cronstrue.toString throws, you fall back to "Not scheduled" or another safe
message and optionally log the error; update the assignment for scheduleDisplay
(the pipeline.cron_string -> cronstrue.toString usage) to handle exceptions
gracefully.

In `@frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx`:
- Around line 262-270: After enabling/disabling a pipeline the code calls
getPipelineList() with no args which resets pagination to page 1; update the
success handler of axiosPrivate in the enable/disable flow to call
getPipelineList with the current page state (the same pagination value used by
the delete handler) so the list refresh preserves the user's current page—locate
the axiosPrivate call and replace getPipelineList() with
getPipelineList(currentPage) (or the existing pagination variable name) while
keeping the existing failure rollback (handleLoaderInTableData) and error
handling (setAlertDetails(handleException(err))).

In `@frontend/src/components/widgets/card-grid-view/CardGridView.jsx`:
- Around line 125-126: The comparisons using strict equality between
forceExpandedId/scrollToId and item.id can fail if one side is a string and the
other a number; update the checks in CardGridView (where
forceExpanded={forceExpandedId === item.id} and scrollIntoView={scrollToId ===
item.id}) to perform a type-safe comparison (e.g., coerce both sides to string
before comparing: String(forceExpandedId) === String(item.id) and
String(scrollToId) === String(item.id)), and update the component PropTypes for
forceExpandedId and scrollToId (in the PropTypes block) to accept
oneOfType([PropTypes.string, PropTypes.number]) so callers and type-checking
align.

In `@frontend/src/helpers/GetStaticData.js`:
- Around line 548-554: shortenApiEndpoint currently uses split("/") which can
include query/hash or return the host for host-only URLs; update
shortenApiEndpoint to parse the input (using the URL constructor where possible,
falling back to treating it as a relative path), strip search and hash, trim
trailing slashes, then take only the last pathname segment as the suffix (with a
safe fallback to the entire sanitized path or the original input when parsing
fails) so you don't leak query tokens or produce host-only labels.

In `@frontend/src/layouts/menu-layout/MenuLayout.jsx`:
- Around line 38-44: The onClick handler in MenuLayout.jsx currently calls
navigate(location.state.from, { state: location.state?.scrollToCardId ? {
scrollToCardId: ... } : undefined }) which overwrites any existing state on
location.state.from; change the call so if location.state.from is a location
object with its own state you merge its state with { scrollToCardId } (e.g.,
build a mergedState = { ...(location.state.from.state || {}),
...(location.state?.scrollToCardId ? { scrollToCardId: ... } : {}) } and pass
that mergedState to navigate) and ensure you still pass the original location
object (location.state.from) to navigate so pathname/key/search are preserved.
🧹 Nitpick comments (19)
backend/workflow_manager/workflow_v2/models/execution.py (2)

401-406: Consider using ExecutionStatus enum values instead of hardcoded strings.

The ExecutionStatus enum is already imported in this file (line 18). Using the enum values would be more maintainable and type-safe.

♻️ Suggested refactor
             successful = WorkflowFileExecution.objects.filter(
-                workflow_execution_id=e.id, status="COMPLETED"
+                workflow_execution_id=e.id, status=ExecutionStatus.COMPLETED.value
             ).count()
             failed = WorkflowFileExecution.objects.filter(
-                workflow_execution_id=e.id, status="ERROR"
+                workflow_execution_id=e.id, status=ExecutionStatus.ERROR.value
             ).count()

Also update the condition on line 410:

-            if e.status == "COMPLETED" and failed > 0 and successful > 0:
+            if e.status == ExecutionStatus.COMPLETED.value and failed > 0 and successful > 0:

391-406: Acknowledged: N+1 query pattern documented for future optimization.

The TODO comment appropriately documents the performance concern. Given the small default limit (5), this is acceptable for now. For future optimization, consider using Django's annotate() with conditional aggregation:

from django.db.models import Count, Q

executions = cls.objects.filter(pipeline_id=pipeline_id).annotate(
    successful=Count('file_executions', filter=Q(file_executions__status='COMPLETED')),
    failed=Count('file_executions', filter=Q(file_executions__status='ERROR'))
).order_by("-created_at")[:limit]

This would reduce 11 queries to 1.

backend/pipeline_v2/serializers/crud.py (1)

213-222: Consider logging the exception for debugging purposes.

The bare except Exception catch is flagged by static analysis (BLE001). While catching a broad exception is acceptable here since croniter can raise various exception types for edge cases, logging the error would aid debugging when next_run_time unexpectedly returns None.

♻️ Suggested improvement
     def get_next_run_time(self, instance: Pipeline) -> str | None:
         """Calculate next scheduled run time from cron expression."""
         if not instance.cron_string or not instance.active:
             return None
         try:
             cron = croniter(instance.cron_string, timezone.now())
             next_run: datetime = cron.get_next(datetime)
             return next_run.isoformat()
-        except Exception:
+        except Exception as e:
+            logger.warning(
+                f"Failed to calculate next run time for pipeline {instance.id}: {e}"
+            )
             return None
frontend/src/components/navigations/tool-nav-bar/ToolNavBar.jsx (1)

81-81: Consider using PropTypes.shape for stricter validation.

The previousRouteState prop is typed as a generic PropTypes.object. Based on the usage in ExecutionLogs.jsx, you could define a more specific shape for better documentation and validation.

🔧 Optional: Add shape validation
-  previousRouteState: PropTypes.object,
+  previousRouteState: PropTypes.shape({
+    scrollToCardId: PropTypes.string,
+    cardExpanded: PropTypes.bool,
+  }),
frontend/src/components/deployments/api-deployment/api-deployments-service.js (1)

4-4: Pre-existing: Shared mutable options variable could cause race conditions.

The module-level let options = {} is reassigned by every method, which could cause issues if multiple concurrent API calls are made (e.g., rapid pagination clicks). This is a pre-existing pattern, so not blocking, but worth noting for future improvement.

♻️ Suggested pattern: Use local `const` in each method
-let options = {};
-
 function apiDeploymentsService() {
   // ...
   return {
     getApiDeploymentsList: (page = 1, pageSize = 10, search = "") => {
       const params = { page, page_size: pageSize };
       if (search) params.search = search;
-      options = {
+      const options = {
         url: `${path}/api/deployment/`,
         method: "GET",
         params,
       };
       return axiosPrivate(options);
     },
     // Apply same pattern to other methods...
   };
 }
frontend/src/components/widgets/card-grid-view/CardGridView.css (1)

638-641: Consider removing the unused .connector-flow-arrow rule.

The comment states this element is "no longer used in vertical layout" and the rule just hides it. If the arrow element has been removed from the JSX, this CSS rule can be deleted entirely. If it might be reintroduced, consider adding a TODO comment.

frontend/src/helpers/dateFormatter.js (2)

64-76: Future dates may produce unexpected relative formatting.

When date is in the future, differenceInHours(now, date) and differenceInMinutes(now, date) return negative values. This means a date 5 minutes in the future would pass the minutesAgo < 1 check (since -5 < 1) and display "Just now", which is misleading.

If future dates are valid inputs (e.g., next_run_time), consider adding explicit handling:

💡 Proposed fix to handle future dates
   // Handle relative formatting for recent dates
   if (relative) {
     const hoursAgo = differenceInHours(now, date);
     const minutesAgo = differenceInMinutes(now, date);
 
+    // Skip relative formatting for future dates
+    if (minutesAgo < 0) {
+      return includeTime
+        ? format(date, "dd MMM yyyy HH:mm")
+        : format(date, "dd MMM yyyy");
+    }
+
     // "Just now" for < 1 minute
     if (minutesAgo < 1) {
       return "Just now";
     }

51-59: Consider extracting shared date parsing logic.

The try/catch parsing pattern is duplicated in formatCompactDate, formatFullDate, and checkIsToday. A small helper would reduce repetition.

♻️ Optional: Extract parsing helper
/**
 * Safely parse a date value
 * `@param` {string|Date} value - Date string or Date object
 * `@return` {Date|null} Parsed Date or null if invalid
 */
function safeParse(value) {
  if (!value) return null;
  try {
    const date = typeof value === "string" ? parseISO(value) : new Date(value);
    return isValid(date) ? date : null;
  } catch {
    return null;
  }
}

Then usage becomes:

const date = safeParse(isoDate);
if (!date) return isoDate; // or "-" or false depending on function
frontend/src/components/deployments/api-deployment/ApiDeploymentCardConfig.jsx (2)

320-333: PropTypes on a factory function have no runtime effect.

createApiDeploymentCardConfig.propTypes won't be validated by React since this is a plain function returning a config object, not a React component. This serves as documentation but provides no runtime type checking.

Consider either:

  1. Using JSDoc @param types (already partially done) for documentation
  2. Using TypeScript for actual type safety
  3. Moving PropTypes validation to where the config is consumed

257-261: Prop naming mismatch: pipelineId used for deployment ID.

StatusPills receives pipelineId={deployment.id} but this is an API deployment, not a pipeline. Consider renaming the prop to a more generic entityId in StatusPills to avoid confusion when reused across different entity types.

frontend/src/components/widgets/card-grid-view/CardGridView.jsx (1)

78-83: Index-based key fallback may cause reconciliation issues.

Using index as the fallback key (line 82) can cause incorrect component reuse if items are reordered, inserted, or deleted. While this is a reasonable fallback, consider logging a dev warning when the fallback is used to help identify data without proper keys.

frontend/src/components/deployments/api-deployment/ApiDeployment.jsx (3)

291-308: Missing dependencies in useMemo may cause stale closures.

The useMemo dependency array only includes [sessionDetails, location], but the config references multiple handler functions (handleEditDeployment, handleShareDeployment, handleDeleteDeployment, etc.) that are recreated on every render. Since these handlers reference state like pagination, they could become stale.

Consider either:

  1. Adding all handlers to the dependency array
  2. Wrapping handlers in useCallback with proper dependencies
  3. Using refs to access current values within handlers
♻️ Option: Wrap handlers in useCallback
+ const handleEditDeployment = useCallback(() => {
+   openAddModal(true);
+ }, []);

+ const handleDeleteDeployment = useCallback(() => {
+   setOpenDeleteModal(true);
+ }, []);

// ... similar for other handlers

  const apiDeploymentCardConfig = useMemo(
    () =>
      createApiDeploymentCardConfig({
        // ...
      }),
-   [sessionDetails, location]
+   [sessionDetails, location, handleEditDeployment, handleShareDeployment, handleDeleteDeployment, /* ... other handlers */]
  );

92-99: Consider combining effects or removing redundant state.

filteredData is set directly from tableData without any transformation. Since search is now server-side (line 96-99), this intermediate state appears unnecessary. You could pass tableData directly to the Layout component.

♻️ Simplify by removing filteredData
- const [filteredData, setFilteredData] = useState([]);

- useEffect(() => {
-   setFilteredData(tableData);
- }, [tableData]);

  // In the Layout component:
- tableData={filteredData}
+ tableData={tableData}

115-138: Pagination total calculation may be incorrect when data is not paginated.

On line 129, when the response is not paginated (data.count is undefined), the fallback uses the current page's result length rather than the actual total. This would break pagination controls if the backend returns unpaginated data.

total: data.count || (data.results ? data.results.length : data.length),

If the API can return both paginated and unpaginated responses, the non-paginated case should set total to the full array length, which is correct. However, if the backend always returns paginated responses now, consider simplifying this logic.

frontend/src/components/deployments/body/Body.jsx (1)

21-23: Redundant loading state handling.

The isTableLoading check at line 21-23 shows a SpinnerLoader and returns early, but loading={isTableLoading} is also passed to CardGridView at line 42. Since the early return prevents CardGridView from ever receiving loading={true}, the loading prop is effectively always false.

Either remove the early return and let CardGridView handle the loading state internally (via LoadingSkeleton), or remove the loading prop from CardGridView.

♻️ Option 1: Let CardGridView handle loading
- if (isTableLoading) {
-   return <SpinnerLoader />;
- }
  if (!tableData?.length) {
+   if (isTableLoading) {
+     return <SpinnerLoader />;
+   }
    return (
      <IslandLayout>
        <EmptyState

Also applies to: 39-49

frontend/src/components/pipelines-or-deployments/pipelines/PipelineCardConfig.jsx (1)

302-320: Popconfirm delete flow may have stale pipeline reference.

In the onConfirm callback (lines 305-308), setSelectedPorD(pipeline) is called before onDelete?.(pipeline). If onDelete triggers a re-render or async operation that depends on selectedPorD, there could be a race condition since setSelectedPorD is asynchronous.

However, since pipeline is passed directly to onDelete, this should work correctly. The setSelectedPorD call might be redundant here if onDelete doesn't rely on it.

frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (2)

465-490: Same useMemo dependency issue as ApiDeployment.jsx.

The dependency array [sessionDetails, isClearingFileHistory, location, type] is missing the handler functions that are referenced in the config. This could lead to stale closures when handlers depend on state that changes.


104-114: Scroll restoration timer works but doesn't clean up navigation state.

The scroll restoration correctly clears scrollRestoreId after 500ms, but the location.state.scrollToCardId remains in the browser history. If the user navigates away and back, it may trigger again unexpectedly. Consider using navigate with replace: true to clear the state after handling.

frontend/src/components/widgets/card-grid-view/CardItem.jsx (1)

82-98: Helper functions recreated on every render.

resolveValue, isFieldVisible, renderField, renderSection, and renderActions are all defined inside the component and recreated on every render. For a card component rendered multiple times in a grid, this could have minor performance implications.

However, since these functions depend on item and config from props, extracting them would require passing these as arguments. The current approach is acceptable for most use cases.

- Remove unused variables: type, filteredData, copyUrl, handleStatusRefresh,
  getPipelineData, sessionDetails
- Wrap cronstrue.toString() in try-catch for invalid cron expressions
- Use current pagination when refreshing after pipeline enable/disable
- Use String() coercion for type-safe ID comparison in CardGridView
- Update PropTypes to accept string or number for IDs
- Improve shortenApiEndpoint to strip query/hash and handle edge cases
- Merge state properly in MenuLayout back navigation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/deployments/api-deployment/ApiDeployment.jsx (1)

163-175: Direct object mutation in updateStatus may cause stale UI or React warnings.

Line 165 directly mutates the record object (record.is_active = !record?.is_active). This is an anti-pattern in React since:

  1. It mutates state directly instead of using setState
  2. The UI update relies on the component re-rendering from setIsTableLoading, not from actual data changes
  3. If the API call fails, there's no rollback mechanism

Consider using the optimistic update pattern like in Pipelines.jsx:

🔧 Proposed fix with optimistic update and rollback
   const updateStatus = (record) => {
-    setIsTableLoading(true);
-    record.is_active = !record?.is_active;
+    const newStatus = !record?.is_active;
+    // Optimistic update
+    setTableData((prevData) =>
+      prevData.map((item) =>
+        item.id === record.id ? { ...item, is_active: newStatus } : item
+      )
+    );
+
     apiDeploymentsApiService
-      .updateApiDeployment(record)
-      .then(() => {})
+      .updateApiDeployment({ ...record, is_active: newStatus })
+      .then(() => {
+        // Optionally refresh to sync with server
+        getApiDeploymentList(pagination.current, pagination.pageSize);
+      })
       .catch((err) => {
+        // Rollback on failure
+        setTableData((prevData) =>
+          prevData.map((item) =>
+            item.id === record.id ? { ...item, is_active: !newStatus } : item
+          )
+        );
         setAlertDetails(handleException(err));
-      })
-      .finally(() => {
-        setIsTableLoading(false);
       });
   };
🤖 Fix all issues with AI agents
In `@frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx`:
- Around line 98-100: The component calls setFilteredData but never declares the
corresponding state; add a useState declaration for filteredData and
setFilteredData (initialized from tableData, e.g., using useState(tableData ||
[])) near the top of the Pipelines component so the existing useEffect (which
calls setFilteredData(tableData)) and other calls to setFilteredData work
without runtime errors; also update any places that should read the derived
array to use filteredData instead of tableData (e.g., the render/logic that
currently references tableData on line ~464).
🧹 Nitpick comments (2)
frontend/src/components/pipelines-or-deployments/pipelines/PipelineCardConfig.jsx (1)

151-161: PropTypes for pipelineId should accept both string and number.

For consistency with similar changes in CardGridView.jsx, pipelineId should accept both types since pipeline.id could be either.

🔧 Proposed fix
 StatusPills.propTypes = {
   statuses: PropTypes.arrayOf(
     PropTypes.shape({
       status: PropTypes.string,
       timestamp: PropTypes.string,
       execution_id: PropTypes.string,
     })
   ),
   executionType: PropTypes.string,
-  pipelineId: PropTypes.string,
+  pipelineId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
 };
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

52-52: Using require() inside a function component is non-standard and may cause bundling issues.

Dynamic require() calls inside React components are typically avoided as they:

  1. Bypass static analysis and tree-shaking
  2. May behave differently in ESM vs CommonJS environments
  3. Are evaluated at runtime rather than build time

Consider moving this to a standard import at the top of the file.

🔧 Proposed fix

At the top of the file, add:

 import { SharePermission } from "../../widgets/share-permission/SharePermission";
 import { createPipelineCardConfig } from "./PipelineCardConfig.jsx";
+import { fetchExecutionLogs } from "../log-modal/fetchExecutionLogs";

Then remove line 52:

-  const { fetchExecutionLogs } = require("../log-modal/fetchExecutionLogs");

- Fix HIGH severity: Remove orphaned setFilteredData references in Pipelines.jsx
- Fix LOW severity: Add keyboard listeners to clickable spans for accessibility
  - PipelineCardConfig.jsx: Add role, tabIndex, onKeyDown to status badge
  - CardItem.jsx: Add keyboard handlers to action spans and expand chevron
- Reduce code duplication between ApiDeploymentCardConfig and PipelineCardConfig
  - Extract shared components to CardFieldComponents.jsx:
    CardActionBox, OwnerFieldRow, LastRunFieldRow, Last5RunsFieldRow,
    WorkflowFieldRow, ApiEndpointSection, CardHeaderRow
  - Net reduction: -289 lines in existing files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace regex /\/+$/ with iterative string manipulation to strip
trailing slashes, avoiding potential ReDoS concerns flagged by
SonarCloud security scanner.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

265-272: Fix stale selectedPorD when clearing file history.
clearFileMarkers ignores the passed workflow id and relies on selectedPorD, which may still be the previous pipeline due to async state updates. Use the workflow id argument directly.

🐛 Proposed fix
-  const clearFileMarkers = async () => {
-    const workflowId = selectedPorD?.workflow_id;
-    const success = await clearFileHistory(workflowId);
+  const clearFileMarkers = async (workflowId) => {
+    if (!workflowId) return;
+    const success = await clearFileHistory(workflowId);
     if (success && openDeleteModal) {
       setOpenDeleteModal(false);
     }
   };
@@
   const handleClearFileHistoryPipeline = (pipeline) => {
     setSelectedPorD(pipeline);
     clearFileMarkers(pipeline.workflow_id);
   };

Also applies to: 390-393

🤖 Fix all issues with AI agents
In `@frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx`:
- Around line 110-113: The server-side search term isn't persisted across
pagination/refresh; modify the component to store the query in state (e.g., add
a searchTerm state and setSearchTerm) and update handleSearch to set that state
instead of only calling getPipelineList; then ensure every call site that
invokes getPipelineList (including the pagination handlers, refresh/delete/share
callbacks referenced around lines where getPipelineList is called) passes the
current searchTerm state (trimmed or "") as the search parameter so paging and
subsequent refreshes keep the filter applied.
- Around line 418-444: The memoized pipelineCardConfig is missing pagination in
its dependency array which causes handlers that close over pagination
(handleEnablePipeline, handleSharePipeline, handleDeletePipeline) to use stale
pagination values; update the useMemo dependency array for
createPipelineCardConfig(...) to include pagination.current and
pagination.pageSize (or the pagination object) so pipelineCardConfig is
recomputed when pagination changes, ensuring those handlers refresh to the
correct page.

In `@frontend/src/components/widgets/card-grid-view/CardFieldComponents.jsx`:
- Around line 198-209: The Link target can be malformed if sessionDetails or
sessionDetails.orgName is undefined; update the Link rendering in
CardFieldComponents.jsx (the Link using sessionDetails?.orgName and workflowId)
to guard and provide a safe fallback (e.g., defaultOrgName or avoid rendering
the Link) by computing a safeOrg = sessionDetails?.orgName || '<fallback>'
before use, or conditionally render a non-navigating element when safeOrg is
missing, and ensure state (from, scrollToCardId) still uses location and itemId
correctly; adjust the Link's to prop to use safeOrg instead of
sessionDetails?.orgName so the route is never `/undefined/...`.
- Around line 38-86: These interactive icons (EditOutlined, ShareAltOutlined,
DeleteOutlined, MoreOutlined) need keyboard accessibility: add role="button" and
tabIndex={0} to each and implement onKeyDown handlers that listen for Enter and
Space to perform the same action as the onClick handlers (for EditOutlined call
setSelectedItem(item) then onEdit?.(item); for ShareAltOutlined call
setSelectedItem(item) then onShare?.(item); for DeleteOutlined call
setSelectedItem(item) then trigger the Popconfirm flow / onDelete?.(item); for
MoreOutlined ensure the kebabMenu dropdown is opened/activated), and ensure each
handler calls e.stopPropagation() like the click handlers.
🧹 Nitpick comments (2)
frontend/src/components/widgets/card-grid-view/CardFieldComponents.jsx (1)

165-169: Consider a more generic prop name for the StatusPillsComponent.

The prop pipelineId={itemId} uses pipeline-specific naming, but this component is reused for API Deployments as well. If StatusPillsComponent can accept a generic prop name like itemId or entityId, that would improve clarity.

frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

52-52: Prefer a static ES module import for fetchExecutionLogs.
Keeping require inside the component re-resolves the module each render and can be awkward with ESM tooling. Consider moving it to a top-level import (please verify your bundler supports this import path).

♻️ Proposed refactor
-import { useLocation } from "react-router-dom";
+import { useLocation } from "react-router-dom";
+import { fetchExecutionLogs } from "../log-modal/fetchExecutionLogs";
@@
-  const { fetchExecutionLogs } = require("../log-modal/fetchExecutionLogs");

Update clearFileMarkers to accept optional workflowId parameter
to match the call site that passes pipeline.workflow_id.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused useSessionStore import in DetailedLogs.jsx
- Add search term persistence across pagination in Pipelines.jsx
- Fix stale closure in pipelineCardConfig useMemo dependencies
- Add keyboard accessibility (role, tabIndex, onKeyDown) to action icons
- Guard against undefined orgName in WorkflowFieldRow link rendering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 1

🤖 Fix all issues with AI agents
In `@frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx`:
- Around line 143-149: The pagination total calculation in the setPagination
callback uses the || operator which treats data.count === 0 as falsy and falls
back incorrectly; update the logic in the setPagination call (where
setPagination is invoked to update current/pageSize/total) to use a nullish
check (or explicit undefined check) for data.count — e.g., set total to
data.count if it is not null/undefined, otherwise fall back to
data.results?.length or data.length — ensuring zero counts are preserved.
🧹 Nitpick comments (2)
frontend/src/components/widgets/card-grid-view/CardFieldComponents.jsx (1)

195-199: Consider renaming pipelineId prop for clarity.

This component is used for both pipelines and API deployments per the PR scope. Passing itemId as pipelineId works functionally but may be confusing for API deployment contexts. If StatusPillsComponent supports a more generic prop name (e.g., entityId or itemId), consider using that for consistency.

frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

53-53: Move require() to a top-level ES import.

Using require() inside a component body is an anti-pattern in modern React applications. It evaluates on each render, isn't tree-shakeable, and is inconsistent with the ES module imports used throughout this file.

♻️ Proposed fix

Add at the top of the file with other imports:

+import { fetchExecutionLogs } from "../log-modal/fetchExecutionLogs";

Then remove line 53:

-  const { fetchExecutionLogs } = require("../log-modal/fetchExecutionLogs");

- Fix array index keys in PipelineCardConfig.jsx and LoadingSkeleton.jsx
- Replace role="button" with native <button> elements for accessibility
- Fix text contrast ratios to meet WCAG AA (4.5:1 minimum)
- Fix unexpected negated conditions in pagination handlers
- Use globalThis instead of window for better portability
- Add proper button reset styles in CSS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/src/components/deployments/api-deployment/ApiDeployment.jsx (1)

163-175: Direct mutation of state before API confirmation.

Line 165 mutates record.is_active directly. Since record likely references an object in tableData, this mutates React state outside of setState, which can cause inconsistent UI and debugging difficulties. If the API call fails, the local state remains incorrectly toggled with no rollback.

Proposed fix with optimistic update and rollback
   const updateStatus = (record) => {
     setIsTableLoading(true);
-    record.is_active = !record?.is_active;
+    const newStatus = !record?.is_active;
+    const updatedRecord = { ...record, is_active: newStatus };
+    
+    // Optimistically update the UI
+    setTableData((prev) =>
+      prev.map((item) => (item.id === record.id ? updatedRecord : item))
+    );
+    
     apiDeploymentsApiService
-      .updateApiDeployment(record)
-      .then(() => {})
+      .updateApiDeployment(updatedRecord)
+      .then(() => {
+        // Success - state already updated
+      })
       .catch((err) => {
+        // Rollback on failure
+        setTableData((prev) =>
+          prev.map((item) => (item.id === record.id ? record : item))
+        );
         setAlertDetails(handleException(err));
       })
       .finally(() => {
         setIsTableLoading(false);
       });
   };
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

95-97: Search term not reset when type changes.

When the pipeline type changes, getPipelineList() is called with default parameters (empty search), but searchTerm state is not reset. This could cause the search input to show a stale value that doesn't match the displayed data.

🐛 Proposed fix
   useEffect(() => {
+    setSearchTerm("");
+    setPagination((prev) => ({ ...prev, current: 1 }));
     getPipelineList();
   }, [type]);
🤖 Fix all issues with AI agents
In `@frontend/src/components/deployments/api-deployment/ApiDeployment.jsx`:
- Around line 120-129: The pagination total logic incorrectly treats data.count
=== 0 as falsy and falls back to array length; update the code that sets
pagination (the setPagination call that computes total) to use a nullish check
(e.g., use data.count ?? (data.results ? data.results.length : data.length) or
explicitly check data.count !== undefined) so a legitimate zero total is
preserved; adjust the same expression that sets setTableData/setPagination to
rely on data.results when present and only fallback to lengths when count is
undefined.

In
`@frontend/src/components/pipelines-or-deployments/pipelines/PipelineCardConfig.jsx`:
- Around line 89-94: The current key computation inside the statuses.map mapping
(where key is set using run.execution_id || run.timestamp || config.label) can
produce duplicate React keys when multiple runs lack execution_id and timestamp;
update the mapping logic in the statuses.map callback (the function that calls
getStatusConfig) to append the array index as a final fallback so each key is
unique (e.g., use run.execution_id || run.timestamp ||
`${config.label}-${index}` or similar), ensuring you reference the index
parameter of the map callback when constructing the key.
🧹 Nitpick comments (5)
frontend/src/components/deployments/api-deployment/ApiDeployment.jsx (2)

290-307: useMemo has incomplete dependencies—handlers may become stale.

The memo depends only on [sessionDetails, location], but it captures updateStatus and all handle* callbacks which are recreated on every render. If any of these handlers reference state that changes (e.g., pagination in updateStatus's flow, or selectedRow in handleShare), the memoized config will hold stale references.

Consider either:

  1. Wrapping the handlers with useCallback and including them in deps, or
  2. Removing useMemo if re-creation on every render is acceptable.
Option 1: Stabilize handlers with useCallback
const handleEditDeployment = useCallback(() => {
  openAddModal(true);
}, []);

const handleShareDeployment = useCallback(() => {
  handleShare();
}, [handleShare]); // handleShare would also need useCallback

// ... similarly for other handlers

const apiDeploymentCardConfig = useMemo(
  () =>
    createApiDeploymentCardConfig({
      setSelectedRow,
      updateStatus,
      sessionDetails,
      location,
      onEdit: handleEditDeployment,
      onShare: handleShareDeployment,
      // ...
    }),
  [
    sessionDetails,
    location,
    updateStatus,
    handleEditDeployment,
    handleShareDeployment,
    // ... all handlers
  ]
);

91-93: filteredData appears redundant with server-side search.

The useEffect syncs filteredData to match tableData on every update, and handleSearch fetches from the server rather than filtering locally. Passing setSearchList={setFilteredData} suggests client-side filtering capability, but it's not being used here.

If client-side filtering is not needed, consider removing filteredData state and passing tableData directly to simplify the data flow.

Also applies to: 323-323, 330-330

frontend/src/components/pipelines-or-deployments/pipelines/PipelineCardConfig.jsx (1)

142-152: PropTypes shape is incomplete.

The component uses run.successful_files and run.failed_files (lines 92, 100, 103), but these are not documented in the PropTypes shape.

📝 Proposed fix
 StatusPills.propTypes = {
   statuses: PropTypes.arrayOf(
     PropTypes.shape({
       status: PropTypes.string,
       timestamp: PropTypes.string,
       execution_id: PropTypes.string,
+      successful_files: PropTypes.number,
+      failed_files: PropTypes.number,
     })
   ),
   executionType: PropTypes.string,
   pipelineId: PropTypes.string,
 };
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

53-53: Avoid require() inside functional components.

Using require() inside a React component is unconventional and may cause issues with tree-shaking and module bundling. It also runs on every render. Consider moving this to a top-level import.

♻️ Proposed fix

Move to top-level imports:

 import { SharePermission } from "../../widgets/share-permission/SharePermission";
 import { createPipelineCardConfig } from "./PipelineCardConfig.jsx";
+import { fetchExecutionLogs } from "../log-modal/fetchExecutionLogs";

 function Pipelines({ type }) {
   // ... state declarations ...
-  const { fetchExecutionLogs } = require("../log-modal/fetchExecutionLogs");
frontend/src/components/widgets/card-grid-view/CardGridView.css (1)

219-236: Consider adding prefers-reduced-motion support for accessibility.

Users who prefer reduced motion (due to vestibular disorders) should have animations disabled or minimized. This is a WCAG 2.1 AAA consideration but increasingly expected.

♻️ Suggested enhancement
 `@keyframes` cardExpandAnimation {
   from {
     opacity: 0;
     transform: translateY(-8px);
   }
   to {
     opacity: 1;
     transform: translateY(0);
   }
 }
+
+@media (prefers-reduced-motion: reduce) {
+  .card-expanded-content {
+    animation: none;
+  }
+  
+  .card-list-expanded {
+    transition: none;
+  }
+}

- Revert status badge colors to vibrant originals (pre-SonarCloud fix)
- Fix pipeline list sorting with nulls_last for last_run_time
- Add index fallback to React key in StatusPills to prevent duplicates
- Simplify enableDisablePipeline by removing unnecessary .then()
- Remove unused pagination deps from useMemo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 1

🤖 Fix all issues with AI agents
In `@frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx`:
- Around line 99-109: The effect only restores scrollToCardId; update the
useEffect handling location.state in Pipelines.jsx to also read page, pageSize,
and searchTerm from location.state and apply them before triggering the scroll:
call setPage(location.state.page), setPageSize(location.state.pageSize),
setSearchTerm(location.state.searchTerm) (or the component's equivalents) and
then re-fetch the list via the existing load/fetch function (e.g.,
fetchPipelines/loadPipelines) so the correct page/filter is loaded before
calling setScrollRestoreId; keep the same timeout/cleanup logic that clears
setScrollRestoreId after 500ms. Also ensure navigation code that opens a card
(the navigate(...) invoked from StatusPills or similar) includes page, pageSize
and searchTerm in state when pushing the route so the effect can restore full
list context.
🧹 Nitpick comments (3)
frontend/src/components/widgets/card-grid-view/CardGridView.css (2)

16-24: Consider specifying explicit transition properties.

Using transition: all can cause performance issues by transitioning unintended properties and may have unexpected side effects during future refactoring. Specifying exact properties is more predictable.

♻️ Suggested improvement
 .card-grid-item {
   height: 100%;
-  transition: all 0.2s ease;
+  transition: box-shadow 0.2s ease;
   border-radius: 8px;
 }
 .card-grid-item.card-list-mode {
   width: 100%;
-  transition: all 0.2s ease;
+  transition: box-shadow 0.2s ease;
 }

Also applies to: 286-289


645-648: Remove dead CSS class.

The comment indicates this class is no longer used. Consider removing it to keep the stylesheet clean.

🧹 Suggested cleanup
-/* Hide arrow - no longer used in vertical layout */
-.connector-flow-arrow {
-  display: none;
-}
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

2-3: Avoid require inside an ES module component.
If this file is compiled as ESM (e.g., Vite), require is undefined at runtime. Prefer a static import (or a lazy import() if you intended to defer loading).

♻️ Suggested change
-import { useEffect, useMemo, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
+import { fetchExecutionLogs } from "../log-modal/fetchExecutionLogs";
@@
-  const { fetchExecutionLogs } = require("../log-modal/fetchExecutionLogs");

Also applies to: 32-32, 53-53

@chandrasekharan-zipstack chandrasekharan-zipstack changed the title UN-3154 [FEAT] Add CardGridView for Pipelines and API Deployments UN-3154 [FEAT] Card based layout to list Pipelines and API Deployments Jan 31, 2026
- Pass listContext (page, pageSize, searchTerm) through StatusPills navigation
- Restore pagination and search state in useEffect before scrolling
- Add listContext prop to Last5RunsFieldRow and StatusPills components
- Update useMemo dependencies to include pagination state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx (1)

95-124: ⚠️ Potential issue | 🟠 Major

Prevent race between initial fetch and scroll-restore fetch.

When scrollToCardId exists, both the initial getPipelineList() (page 1) and the restore fetch run. If the page‑1 request resolves last, it overwrites the restored page and breaks scroll restoration. Gate the initial fetch when full list context is present.

✅ Suggested fix
-  useEffect(() => {
-    getPipelineList();
-  }, [type]);
+  useEffect(() => {
+    const hasListContext =
+      location.state?.page ||
+      location.state?.pageSize ||
+      location.state?.searchTerm !== undefined;
+    if (location.state?.scrollToCardId && hasListContext) return;
+    getPipelineList();
+  }, [
+    type,
+    location.state?.scrollToCardId,
+    location.state?.page,
+    location.state?.pageSize,
+    location.state?.searchTerm,
+  ]);
🤖 Fix all issues with AI agents
In `@frontend/src/components/widgets/card-grid-view/CardFieldComponents.jsx`:
- Around line 261-294: ApiEndpointSection currently renders apiEndpoint as a
clickable link which can be exploited if apiEndpoint contains unsafe schemes
like "javascript:" or "data:"; update ApiEndpointSection to validate the URL
scheme before rendering an <a> tag by parsing apiEndpoint (e.g., using URL or a
regex) and only allow "http" or "https" schemes, otherwise render the shortened
endpoint as plain text (still wrapped in Tooltip and copy button), and keep the
existing onClick stopPropagation and copyToClipboard behavior; adjust logic
around shortenApiEndpoint usage so it is used for both safe links and plain-text
rendering.
- Around line 50-101: The icon-only action buttons lack accessible names; update
the button elements (the ones with classNames "action-icon-btn edit-icon",
"action-icon-btn share-icon", "action-icon-btn delete-icon", and
"card-kebab-menu") to include explicit aria-label attributes (e.g.,
aria-label="Edit", aria-label="Share", aria-label="Delete", aria-label="More
actions") so screen readers get a proper name; keep existing onClick handlers
like handleEditAction, handleShareAction, the Popconfirm onConfirm that calls
setSelectedItem and onDelete, and the Dropdown using kebabMenuItems
unchanged—only add the aria-labels to the respective buttons.

Copy link
Copy Markdown
Contributor

@vishnuszipstack vishnuszipstack left a comment

Choose a reason for hiding this comment

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

@chandrasekharan-zipstack use antD components where ever possible. i see custom classes those can be replaced with antD components. for example make use of Space, Col, Row etc instead of div with class. Also try to use antD cards if possible instead of custom cards. Also add screenshots for FE changes.

- Replace native buttons with AntD Button type="text" in CardActionBox
- Replace native button with AntD Button for expand chevron in CardItem
- Use AntD Space for status badges container in StatusPills
- Use AntD Tag instead of native button/span for status badges
- Fix require() inside component body → static import in Pipelines.jsx
- Update CSS to support both native and AntD component selectors
- Add missing listContext prop type to StatusPills

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ApiDeployment: Use explicit null check instead of falsy check for data.count
- CardFieldComponents: Add URL scheme validation to prevent javascript: URLs
- PipelineCardConfig: Add keyboard accessibility to status badges
- CardGridView.css: Increase max-height from 200px to 500px for expanded content
- CardItem: Add setTimeout cleanup with clearTimeout to prevent memory leaks
- CardItem: Use isExpanded prop instead of expanded for grid mode support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fixed ESLint eqeqeq error: Changed != null to !== null && !== undefined check
- Added loading state to LogsModal to display spinner while fetching logs
- Updated fetchExecutionLogs to support optional setIsLoading callback
- Wired up isFetchingLogs state in both Pipelines and ApiDeployment components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix card grid toggle flashing by memoizing CardItem and using optimistic updates. Prevent edit list from disappearing by using inline updates instead of refetch. Restore scroll position on back navigation using location.key. Remove unnecessary success alert on pipeline update. Keep share button always visible for consistency with workflows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove background overrides that don't work due to AntD CSS-in-JS
specificity. Action icons will now show AntD's default hover state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add backend ordering by last_run_time for API deployments using
  Subquery annotation with nulls_last for "Never" entries
- Fix share modal by passing deployment directly to avoid stale state
- Add robust response handling for different API response structures
- Remove Tooltip wrappers from action buttons to fix shadow issues

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@athul-rs athul-rs left a comment

Choose a reason for hiding this comment

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

@chandrasekharan-zipstack lgtm overall for backend changes.
Please check if all the frontend updates are required and if there are any redundant code using. On primary glance looks too much changes added, I might be wrong.

Copy link
Copy Markdown
Contributor

@vishnuszipstack vishnuszipstack left a comment

Choose a reason for hiding this comment

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

antd Component Usage Review

This PR introduces a solid card-based layout system and correctly uses several antd components (Card, Row, Col, Pagination, Skeleton, Empty, Tag, Switch, Tooltip, Button, Dropdown, Typography, Image). However, there are significant areas where raw HTML elements and custom CSS are used where antd components would be more appropriate.

Summary of Issues

High Priority:

  1. Label-value rows use raw <div> + <span> instead of antd Descriptions, Flex, or Typography.Text (repeated ~10 times across CardFieldComponents.jsx, PipelineCardConfig.jsx, ApiDeploymentCardConfig.jsx)
  2. Status badge colors use ~80 lines of custom CSS to override antd Tag styling instead of using the built-in color prop (<Tag color="success">, <Tag color="error">, etc.)
  3. Raw <span> for text throughout footer items and field values instead of Typography.Text
  4. Raw <a> tag instead of Typography.Link for API endpoint links
  5. Raw <img> tag instead of antd Image or Avatar for workflow icon

Medium Priority:
6. Flex containers use raw <div> + custom CSS instead of antd Flex component (header rows, action containers, footer rows, pagination wrapper)
7. Action containers use <div className="card-list-actions"> instead of antd Space
8. Field/section layouts in CardItem.jsx use custom CSS classes instead of antd Space, Flex, Descriptions

Estimated Impact

The CSS file is 1,245 lines. By adopting the antd components noted above, roughly 400-500 lines of CSS could be eliminated, and 40-60 wrapper <div>/<span> elements across JSX files could be replaced with semantic antd components. This would improve consistency with the project's architecture (antd is the primary UI framework per CLAUDE.md) and reduce maintenance burden.

What Was Done Well

  • CardGridView.jsx correctly uses Row, Col, Empty, Pagination
  • LoadingSkeleton.jsx correctly uses Card, Row, Col, Skeleton
  • CardItem.jsx correctly uses antd Card as the base wrapper
  • Proper use of Switch, Button, Dropdown, Popconfirm, Typography.Title

…lines and ApiDeployment

Extract duplicated logic into 4 reusable hooks (useExecutionLogs,
usePaginatedList, useScrollRestoration, useShareModal) and wire them
into both components. Also fixes a bug where ApiDeployment lost
searchTerm when changing pages or deleting items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve merge conflicts keeping card-based layout approach:
- ApiDeployment.jsx: Keep card config + shared hooks, remove old table columns/actionItems
- Pipelines.jsx: Keep card config + shared hooks, remove old table dropdown
- DetailedLogs.jsx: Deduplicate imports
- ExecutionLogs.jsx: Add useLocation for scroll restoration
- fetchExecutionLogs.js: Keep setIsLoading parameter for hook usage
- Fix biome lint warnings in auto-merged files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address Vishnu's review comments on PR #1769:
- Field rows: div/span → Flex + Typography.Text + Space
- img → AntD Avatar for WorkflowIcon
- a/span → Typography.Link / Typography.Text in ApiEndpointSection
- Header wrappers → Flex in CardHeaderRow and CardItem
- Actions container → Space in CardItem
- CSS: display block on subtitle, endpoint link targets .ant-typography-link

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The empty module (`export default undefined`) made plugin imports
succeed silently, bypassing catch blocks and causing a login loop
in OSS mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sort imports, wrap single-line if/return in block statements,
add trailing commas, and fix CSS formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…plicate CSS

- Replaced raw HTML elements with Ant Design components (Flex, Space, Typography.Text, Avatar, Tag)
- Removed ~80 lines of custom CSS that duplicated AntD native functionality
- Fixed API endpoint Card padding/border specificity issues
- Fixed copy button padding in pipeline cards
- Addresses PR #1769 review feedback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Preserve green/red icon colors in status pill tooltips where
`color: inherit` was making them white on dark background.
Move unnecessary lazy imports to top-level across dashboard_metrics,
pipeline_v2 and workflow_manager modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d files

- Extract helpers to reduce function nesting depth (S2004)
- Replace nested ternary with nullish coalescing (S3358)
- Remove unused imports in DetailedLogs (S1128)
- Move PostHog try-catch into usePostHogEvents hook (S2486)
- Use globalThis over window (S7764)
- Fix non-native interactive element in CardItem (S6848)
- Use stable keys instead of array index in LoadingSkeleton (S6479)
- Improve contrast ratio for success status badge (S7924)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@vishnuszipstack vishnuszipstack left a comment

Choose a reason for hiding this comment

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

LGTM

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 2, 2026

Frontend Lint Report (Biome)

All checks passed! No linting or formatting issues found.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 2, 2026

Test Results

Summary
  • Runner Tests: 11 passed, 0 failed (11 total)
  • SDK1 Tests: 63 passed, 0 failed (63 total)

Runner Tests - Full Report
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_logs}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup\_skip}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_client\_init}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config\_without\_mount}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_run\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_for\_sidecar}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_sidecar\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{11}}$$ $$\textcolor{#23d18b}{\tt{11}}$$
SDK1 Tests - Full Report
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_success\_on\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_on\_connection\_error}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_non\_retryable\_http\_error}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retryable\_http\_errors}}$$ $$\textcolor{#23d18b}{\tt{3}}$$ $$\textcolor{#23d18b}{\tt{3}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_post\_method\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_logging}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_success\_on\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_retry\_on\_errors}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_wrapper\_methods\_retry}}$$ $$\textcolor{#23d18b}{\tt{4}}$$ $$\textcolor{#23d18b}{\tt{4}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_connection\_error\_is\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_timeout\_is\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_retryable\_status\_codes}}$$ $$\textcolor{#23d18b}{\tt{3}}$$ $$\textcolor{#23d18b}{\tt{3}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_non\_retryable\_status\_codes}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_without\_response}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_retryable\_errno}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_non\_retryable\_errno}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_other\_exception\_not\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_without\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_with\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap\_with\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_successful\_call\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_after\_transient\_failure}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_max\_retries\_exceeded}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_with\_custom\_predicate}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_no\_retry\_with\_predicate\_false}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_exception\_not\_in\_tuple\_not\_retried}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_default\_configuration}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_environment\_variable\_configuration}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_max\_retries}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_base\_delay}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_multiplier}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_jitter\_values}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_exceptions\_only}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_predicate\_only}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_both\_exceptions\_and\_predicate}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_exceptions\_match\_but\_predicate\_false}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_platform\_service\_call\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_prompt\_service\_call\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_platform\_service\_decorator\_retries\_on\_connection\_error}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_prompt\_service\_decorator\_retries\_on\_timeout}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_warning\_logged\_on\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_info\_logged\_on\_success\_after\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_exception\_logged\_on\_giving\_up}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{63}}$$ $$\textcolor{#23d18b}{\tt{63}}$$

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Mar 2, 2026

@chandrasekharan-zipstack chandrasekharan-zipstack merged commit eb4cb95 into main Mar 2, 2026
8 checks passed
@chandrasekharan-zipstack chandrasekharan-zipstack deleted the UN-3154-card-grid-view-pipelines-deployments branch March 2, 2026 08:34
athul-rs pushed a commit that referenced this pull request Mar 3, 2026
#1769)

* UN-3154 [FEAT] Add execution status tracking and list enhancements

- Add WorkflowExecution.get_last_run_statuses() method to fetch last N
  executions with PARTIAL_SUCCESS computed dynamically
- Add last_5_run_statuses, run_count, last_run_time to API deployment serializer
- Add last_5_run_statuses, next_run_time to pipeline serializer
- Add source/destination instance names to pipeline representation
- Enable pagination (CustomPagination) for both pipelines and deployments
- Add search filter by name for both endpoints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Add reusable CardGridView component

- Create CardGridView widget for displaying items in card/list layouts
- Add CardItem component with expand/collapse, status badges, actions
- Add LoadingSkeleton for loading states
- Support pagination, search, scroll restoration, and card configuration
- Add dateFormatter helper for consistent date formatting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor API Deployments to use CardGridView

- Add ApiDeploymentCardConfig for card layout configuration
- Refactor ApiDeployment to use CardGridView with pagination
- Update api-deployments-service with pagination and search support
- Refactor Body component to use CardGridView instead of Table
- Add search functionality to Header component
- Update Layout to pass card configuration and pagination props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor Pipelines to use CardGridView

- Add PipelineCardConfig with StatusPills component for execution status
- Display source/destination connector icons and instance names
- Show last 5 run statuses with clickable navigation to execution logs
- Add schedule display, next run time, and run count
- Support pagination and search functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Improve navigation with scroll restoration

- Update MenuLayout to forward scrollToCardId state on back navigation
- Pass scrollToCardId from workflow links in card configs
- Add back route with state to ExecutionLogs for scroll restoration
- Remove redundant back button from DetailedLogs header
- Add previousRoute and previousRouteState props to ToolNavBar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit review comments

- Remove unused variables: type, filteredData, copyUrl, handleStatusRefresh,
  getPipelineData, sessionDetails
- Wrap cronstrue.toString() in try-catch for invalid cron expressions
- Use current pagination when refreshing after pipeline enable/disable
- Use String() coercion for type-safe ID comparison in CardGridView
- Update PropTypes to accept string or number for IDs
- Improve shortenApiEndpoint to strip query/hash and handle edge cases
- Merge state properly in MenuLayout back navigation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarQube issues and reduce code duplication

- Fix HIGH severity: Remove orphaned setFilteredData references in Pipelines.jsx
- Fix LOW severity: Add keyboard listeners to clickable spans for accessibility
  - PipelineCardConfig.jsx: Add role, tabIndex, onKeyDown to status badge
  - CardItem.jsx: Add keyboard handlers to action spans and expand chevron
- Reduce code duplication between ApiDeploymentCardConfig and PipelineCardConfig
  - Extract shared components to CardFieldComponents.jsx:
    CardActionBox, OwnerFieldRow, LastRunFieldRow, Last5RunsFieldRow,
    WorkflowFieldRow, ApiEndpointSection, CardHeaderRow
  - Net reduction: -289 lines in existing files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud ReDoS security hotspot

Replace regex /\/+$/ with iterative string manipulation to strip
trailing slashes, avoiding potential ReDoS concerns flagged by
SonarCloud security scanner.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix function argument mismatch in clearFileMarkers

Update clearFileMarkers to accept optional workflowId parameter
to match the call site that passes pipeline.workflow_id.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarQube issues and improve code quality

- Remove unused useSessionStore import in DetailedLogs.jsx
- Add search term persistence across pagination in Pipelines.jsx
- Fix stale closure in pipelineCardConfig useMemo dependencies
- Add keyboard accessibility (role, tabIndex, onKeyDown) to action icons
- Guard against undefined orgName in WorkflowFieldRow link rendering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud code quality issues

- Fix array index keys in PipelineCardConfig.jsx and LoadingSkeleton.jsx
- Replace role="button" with native <button> elements for accessibility
- Fix text contrast ratios to meet WCAG AA (4.5:1 minimum)
- Fix unexpected negated conditions in pagination handlers
- Use globalThis instead of window for better portability
- Add proper button reset styles in CSS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Revert status pill colors and fix pipeline sorting

- Revert status badge colors to vibrant originals (pre-SonarCloud fix)
- Fix pipeline list sorting with nulls_last for last_run_time
- Add index fallback to React key in StatusPills to prevent duplicates
- Simplify enableDisablePipeline by removing unnecessary .then()
- Remove unused pagination deps from useMemo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Restore full list context on back navigation from logs

- Pass listContext (page, pageSize, searchTerm) through StatusPills navigation
- Restore pagination and search state in useEffect before scrolling
- Add listContext prop to Last5RunsFieldRow and StatusPills components
- Update useMemo dependencies to include pagination state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

- Replace native buttons with AntD Button type="text" in CardActionBox
- Replace native button with AntD Button for expand chevron in CardItem
- Use AntD Space for status badges container in StatusPills
- Use AntD Tag instead of native button/span for status badges
- Fix require() inside component body → static import in Pipelines.jsx
- Update CSS to support both native and AntD component selectors
- Add missing listContext prop type to StatusPills

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit PR review comments from #1769

- ApiDeployment: Use explicit null check instead of falsy check for data.count
- CardFieldComponents: Add URL scheme validation to prevent javascript: URLs
- PipelineCardConfig: Add keyboard accessibility to status badges
- CardGridView.css: Increase max-height from 200px to 500px for expanded content
- CardItem: Add setTimeout cleanup with clearTimeout to prevent memory leaks
- CardItem: Use isExpanded prop instead of expanded for grid mode support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] ESLint eqeqeq error and add loading state to logs modal

- Fixed ESLint eqeqeq error: Changed != null to !== null && !== undefined check
- Added loading state to LogsModal to display spinner while fetching logs
- Updated fetchExecutionLogs to support optional setIsLoading callback
- Wired up isFetchingLogs state in both Pipelines and ApiDeployment components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Resolve card grid rendering issues and UI glitches

Fix card grid toggle flashing by memoizing CardItem and using optimistic updates. Prevent edit list from disappearing by using inline updates instead of refetch. Restore scroll position on back navigation using location.key. Remove unnecessary success alert on pipeline update. Keep share button always visible for consistency with workflows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Remove ineffective CSS hover overrides for action icons

Remove background overrides that don't work due to AntD CSS-in-JS
specificity. Action icons will now show AntD's default hover state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Backend sorting and share modal improvements

- Add backend ordering by last_run_time for API deployments using
  Subquery annotation with nulls_last for "Never" entries
- Fix share modal by passing deployment directly to avoid stale state
- Add robust response handling for different API response structures
- Remove Tooltip wrappers from action buttons to fix shadow issues

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Extract shared hooks to reduce duplication in Pipelines and ApiDeployment

Extract duplicated logic into 4 reusable hooks (useExecutionLogs,
usePaginatedList, useScrollRestoration, useShareModal) and wire them
into both components. Also fixes a bug where ApiDeployment lost
searchTerm when changing pages or deleting items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* MISC [FIX] Suppress eslint import/no-unresolved for Webpack url import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

Address Vishnu's review comments on PR #1769:
- Field rows: div/span → Flex + Typography.Text + Space
- img → AntD Avatar for WorkflowIcon
- a/span → Typography.Link / Typography.Text in ApiEndpointSection
- Header wrappers → Flex in CardHeaderRow and CardItem
- Actions container → Space in CardItem
- CSS: display block on subtitle, endpoint link targets .ant-typography-link

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [FIX] Make Vite optional plugin emit throw instead of empty export

The empty module (`export default undefined`) made plugin imports
succeed silently, bypassing catch blocks and causing a login loop
in OSS mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [MISC] Fix biome v2 lint and format issues in card grid view files

Sort imports, wrap single-line if/return in block statements,
add trailing commas, and fix CSS formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Replace divs/spans with AntD components, remove duplicate CSS

- Replaced raw HTML elements with Ant Design components (Flex, Space, Typography.Text, Avatar, Tag)
- Removed ~80 lines of custom CSS that duplicated AntD native functionality
- Fixed API endpoint Card padding/border specificity issues
- Fixed copy button padding in pipeline cards
- Addresses PR #1769 review feedback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix tooltip icon colors, move lazy imports to top-level

Preserve green/red icon colors in status pill tooltips where
`color: inherit` was making them white on dark background.
Move unnecessary lazy imports to top-level across dashboard_metrics,
pipeline_v2 and workflow_manager modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarCloud code smells in card grid view and related files

- Extract helpers to reduce function nesting depth (S2004)
- Replace nested ternary with nullish coalescing (S3358)
- Remove unused imports in DetailedLogs (S1128)
- Move PostHog try-catch into usePostHogEvents hook (S2486)
- Use globalThis over window (S7764)
- Fix non-native interactive element in CardItem (S6848)
- Use stable keys instead of array index in LoadingSkeleton (S6479)
- Improve contrast ratio for success status badge (S7924)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
athul-rs pushed a commit that referenced this pull request Mar 4, 2026
#1769)

* UN-3154 [FEAT] Add execution status tracking and list enhancements

- Add WorkflowExecution.get_last_run_statuses() method to fetch last N
  executions with PARTIAL_SUCCESS computed dynamically
- Add last_5_run_statuses, run_count, last_run_time to API deployment serializer
- Add last_5_run_statuses, next_run_time to pipeline serializer
- Add source/destination instance names to pipeline representation
- Enable pagination (CustomPagination) for both pipelines and deployments
- Add search filter by name for both endpoints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Add reusable CardGridView component

- Create CardGridView widget for displaying items in card/list layouts
- Add CardItem component with expand/collapse, status badges, actions
- Add LoadingSkeleton for loading states
- Support pagination, search, scroll restoration, and card configuration
- Add dateFormatter helper for consistent date formatting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor API Deployments to use CardGridView

- Add ApiDeploymentCardConfig for card layout configuration
- Refactor ApiDeployment to use CardGridView with pagination
- Update api-deployments-service with pagination and search support
- Refactor Body component to use CardGridView instead of Table
- Add search functionality to Header component
- Update Layout to pass card configuration and pagination props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor Pipelines to use CardGridView

- Add PipelineCardConfig with StatusPills component for execution status
- Display source/destination connector icons and instance names
- Show last 5 run statuses with clickable navigation to execution logs
- Add schedule display, next run time, and run count
- Support pagination and search functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Improve navigation with scroll restoration

- Update MenuLayout to forward scrollToCardId state on back navigation
- Pass scrollToCardId from workflow links in card configs
- Add back route with state to ExecutionLogs for scroll restoration
- Remove redundant back button from DetailedLogs header
- Add previousRoute and previousRouteState props to ToolNavBar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit review comments

- Remove unused variables: type, filteredData, copyUrl, handleStatusRefresh,
  getPipelineData, sessionDetails
- Wrap cronstrue.toString() in try-catch for invalid cron expressions
- Use current pagination when refreshing after pipeline enable/disable
- Use String() coercion for type-safe ID comparison in CardGridView
- Update PropTypes to accept string or number for IDs
- Improve shortenApiEndpoint to strip query/hash and handle edge cases
- Merge state properly in MenuLayout back navigation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarQube issues and reduce code duplication

- Fix HIGH severity: Remove orphaned setFilteredData references in Pipelines.jsx
- Fix LOW severity: Add keyboard listeners to clickable spans for accessibility
  - PipelineCardConfig.jsx: Add role, tabIndex, onKeyDown to status badge
  - CardItem.jsx: Add keyboard handlers to action spans and expand chevron
- Reduce code duplication between ApiDeploymentCardConfig and PipelineCardConfig
  - Extract shared components to CardFieldComponents.jsx:
    CardActionBox, OwnerFieldRow, LastRunFieldRow, Last5RunsFieldRow,
    WorkflowFieldRow, ApiEndpointSection, CardHeaderRow
  - Net reduction: -289 lines in existing files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud ReDoS security hotspot

Replace regex /\/+$/ with iterative string manipulation to strip
trailing slashes, avoiding potential ReDoS concerns flagged by
SonarCloud security scanner.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix function argument mismatch in clearFileMarkers

Update clearFileMarkers to accept optional workflowId parameter
to match the call site that passes pipeline.workflow_id.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarQube issues and improve code quality

- Remove unused useSessionStore import in DetailedLogs.jsx
- Add search term persistence across pagination in Pipelines.jsx
- Fix stale closure in pipelineCardConfig useMemo dependencies
- Add keyboard accessibility (role, tabIndex, onKeyDown) to action icons
- Guard against undefined orgName in WorkflowFieldRow link rendering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud code quality issues

- Fix array index keys in PipelineCardConfig.jsx and LoadingSkeleton.jsx
- Replace role="button" with native <button> elements for accessibility
- Fix text contrast ratios to meet WCAG AA (4.5:1 minimum)
- Fix unexpected negated conditions in pagination handlers
- Use globalThis instead of window for better portability
- Add proper button reset styles in CSS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Revert status pill colors and fix pipeline sorting

- Revert status badge colors to vibrant originals (pre-SonarCloud fix)
- Fix pipeline list sorting with nulls_last for last_run_time
- Add index fallback to React key in StatusPills to prevent duplicates
- Simplify enableDisablePipeline by removing unnecessary .then()
- Remove unused pagination deps from useMemo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Restore full list context on back navigation from logs

- Pass listContext (page, pageSize, searchTerm) through StatusPills navigation
- Restore pagination and search state in useEffect before scrolling
- Add listContext prop to Last5RunsFieldRow and StatusPills components
- Update useMemo dependencies to include pagination state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

- Replace native buttons with AntD Button type="text" in CardActionBox
- Replace native button with AntD Button for expand chevron in CardItem
- Use AntD Space for status badges container in StatusPills
- Use AntD Tag instead of native button/span for status badges
- Fix require() inside component body → static import in Pipelines.jsx
- Update CSS to support both native and AntD component selectors
- Add missing listContext prop type to StatusPills

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit PR review comments from #1769

- ApiDeployment: Use explicit null check instead of falsy check for data.count
- CardFieldComponents: Add URL scheme validation to prevent javascript: URLs
- PipelineCardConfig: Add keyboard accessibility to status badges
- CardGridView.css: Increase max-height from 200px to 500px for expanded content
- CardItem: Add setTimeout cleanup with clearTimeout to prevent memory leaks
- CardItem: Use isExpanded prop instead of expanded for grid mode support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] ESLint eqeqeq error and add loading state to logs modal

- Fixed ESLint eqeqeq error: Changed != null to !== null && !== undefined check
- Added loading state to LogsModal to display spinner while fetching logs
- Updated fetchExecutionLogs to support optional setIsLoading callback
- Wired up isFetchingLogs state in both Pipelines and ApiDeployment components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Resolve card grid rendering issues and UI glitches

Fix card grid toggle flashing by memoizing CardItem and using optimistic updates. Prevent edit list from disappearing by using inline updates instead of refetch. Restore scroll position on back navigation using location.key. Remove unnecessary success alert on pipeline update. Keep share button always visible for consistency with workflows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Remove ineffective CSS hover overrides for action icons

Remove background overrides that don't work due to AntD CSS-in-JS
specificity. Action icons will now show AntD's default hover state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Backend sorting and share modal improvements

- Add backend ordering by last_run_time for API deployments using
  Subquery annotation with nulls_last for "Never" entries
- Fix share modal by passing deployment directly to avoid stale state
- Add robust response handling for different API response structures
- Remove Tooltip wrappers from action buttons to fix shadow issues

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Extract shared hooks to reduce duplication in Pipelines and ApiDeployment

Extract duplicated logic into 4 reusable hooks (useExecutionLogs,
usePaginatedList, useScrollRestoration, useShareModal) and wire them
into both components. Also fixes a bug where ApiDeployment lost
searchTerm when changing pages or deleting items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* MISC [FIX] Suppress eslint import/no-unresolved for Webpack url import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

Address Vishnu's review comments on PR #1769:
- Field rows: div/span → Flex + Typography.Text + Space
- img → AntD Avatar for WorkflowIcon
- a/span → Typography.Link / Typography.Text in ApiEndpointSection
- Header wrappers → Flex in CardHeaderRow and CardItem
- Actions container → Space in CardItem
- CSS: display block on subtitle, endpoint link targets .ant-typography-link

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [FIX] Make Vite optional plugin emit throw instead of empty export

The empty module (`export default undefined`) made plugin imports
succeed silently, bypassing catch blocks and causing a login loop
in OSS mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [MISC] Fix biome v2 lint and format issues in card grid view files

Sort imports, wrap single-line if/return in block statements,
add trailing commas, and fix CSS formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Replace divs/spans with AntD components, remove duplicate CSS

- Replaced raw HTML elements with Ant Design components (Flex, Space, Typography.Text, Avatar, Tag)
- Removed ~80 lines of custom CSS that duplicated AntD native functionality
- Fixed API endpoint Card padding/border specificity issues
- Fixed copy button padding in pipeline cards
- Addresses PR #1769 review feedback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix tooltip icon colors, move lazy imports to top-level

Preserve green/red icon colors in status pill tooltips where
`color: inherit` was making them white on dark background.
Move unnecessary lazy imports to top-level across dashboard_metrics,
pipeline_v2 and workflow_manager modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarCloud code smells in card grid view and related files

- Extract helpers to reduce function nesting depth (S2004)
- Replace nested ternary with nullish coalescing (S3358)
- Remove unused imports in DetailedLogs (S1128)
- Move PostHog try-catch into usePostHogEvents hook (S2486)
- Use globalThis over window (S7764)
- Fix non-native interactive element in CardItem (S6848)
- Use stable keys instead of array index in LoadingSkeleton (S6479)
- Improve contrast ratio for success status badge (S7924)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
athul-rs pushed a commit that referenced this pull request Mar 4, 2026
#1769)

* UN-3154 [FEAT] Add execution status tracking and list enhancements

- Add WorkflowExecution.get_last_run_statuses() method to fetch last N
  executions with PARTIAL_SUCCESS computed dynamically
- Add last_5_run_statuses, run_count, last_run_time to API deployment serializer
- Add last_5_run_statuses, next_run_time to pipeline serializer
- Add source/destination instance names to pipeline representation
- Enable pagination (CustomPagination) for both pipelines and deployments
- Add search filter by name for both endpoints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Add reusable CardGridView component

- Create CardGridView widget for displaying items in card/list layouts
- Add CardItem component with expand/collapse, status badges, actions
- Add LoadingSkeleton for loading states
- Support pagination, search, scroll restoration, and card configuration
- Add dateFormatter helper for consistent date formatting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor API Deployments to use CardGridView

- Add ApiDeploymentCardConfig for card layout configuration
- Refactor ApiDeployment to use CardGridView with pagination
- Update api-deployments-service with pagination and search support
- Refactor Body component to use CardGridView instead of Table
- Add search functionality to Header component
- Update Layout to pass card configuration and pagination props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor Pipelines to use CardGridView

- Add PipelineCardConfig with StatusPills component for execution status
- Display source/destination connector icons and instance names
- Show last 5 run statuses with clickable navigation to execution logs
- Add schedule display, next run time, and run count
- Support pagination and search functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Improve navigation with scroll restoration

- Update MenuLayout to forward scrollToCardId state on back navigation
- Pass scrollToCardId from workflow links in card configs
- Add back route with state to ExecutionLogs for scroll restoration
- Remove redundant back button from DetailedLogs header
- Add previousRoute and previousRouteState props to ToolNavBar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit review comments

- Remove unused variables: type, filteredData, copyUrl, handleStatusRefresh,
  getPipelineData, sessionDetails
- Wrap cronstrue.toString() in try-catch for invalid cron expressions
- Use current pagination when refreshing after pipeline enable/disable
- Use String() coercion for type-safe ID comparison in CardGridView
- Update PropTypes to accept string or number for IDs
- Improve shortenApiEndpoint to strip query/hash and handle edge cases
- Merge state properly in MenuLayout back navigation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarQube issues and reduce code duplication

- Fix HIGH severity: Remove orphaned setFilteredData references in Pipelines.jsx
- Fix LOW severity: Add keyboard listeners to clickable spans for accessibility
  - PipelineCardConfig.jsx: Add role, tabIndex, onKeyDown to status badge
  - CardItem.jsx: Add keyboard handlers to action spans and expand chevron
- Reduce code duplication between ApiDeploymentCardConfig and PipelineCardConfig
  - Extract shared components to CardFieldComponents.jsx:
    CardActionBox, OwnerFieldRow, LastRunFieldRow, Last5RunsFieldRow,
    WorkflowFieldRow, ApiEndpointSection, CardHeaderRow
  - Net reduction: -289 lines in existing files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud ReDoS security hotspot

Replace regex /\/+$/ with iterative string manipulation to strip
trailing slashes, avoiding potential ReDoS concerns flagged by
SonarCloud security scanner.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix function argument mismatch in clearFileMarkers

Update clearFileMarkers to accept optional workflowId parameter
to match the call site that passes pipeline.workflow_id.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarQube issues and improve code quality

- Remove unused useSessionStore import in DetailedLogs.jsx
- Add search term persistence across pagination in Pipelines.jsx
- Fix stale closure in pipelineCardConfig useMemo dependencies
- Add keyboard accessibility (role, tabIndex, onKeyDown) to action icons
- Guard against undefined orgName in WorkflowFieldRow link rendering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud code quality issues

- Fix array index keys in PipelineCardConfig.jsx and LoadingSkeleton.jsx
- Replace role="button" with native <button> elements for accessibility
- Fix text contrast ratios to meet WCAG AA (4.5:1 minimum)
- Fix unexpected negated conditions in pagination handlers
- Use globalThis instead of window for better portability
- Add proper button reset styles in CSS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Revert status pill colors and fix pipeline sorting

- Revert status badge colors to vibrant originals (pre-SonarCloud fix)
- Fix pipeline list sorting with nulls_last for last_run_time
- Add index fallback to React key in StatusPills to prevent duplicates
- Simplify enableDisablePipeline by removing unnecessary .then()
- Remove unused pagination deps from useMemo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Restore full list context on back navigation from logs

- Pass listContext (page, pageSize, searchTerm) through StatusPills navigation
- Restore pagination and search state in useEffect before scrolling
- Add listContext prop to Last5RunsFieldRow and StatusPills components
- Update useMemo dependencies to include pagination state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

- Replace native buttons with AntD Button type="text" in CardActionBox
- Replace native button with AntD Button for expand chevron in CardItem
- Use AntD Space for status badges container in StatusPills
- Use AntD Tag instead of native button/span for status badges
- Fix require() inside component body → static import in Pipelines.jsx
- Update CSS to support both native and AntD component selectors
- Add missing listContext prop type to StatusPills

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit PR review comments from #1769

- ApiDeployment: Use explicit null check instead of falsy check for data.count
- CardFieldComponents: Add URL scheme validation to prevent javascript: URLs
- PipelineCardConfig: Add keyboard accessibility to status badges
- CardGridView.css: Increase max-height from 200px to 500px for expanded content
- CardItem: Add setTimeout cleanup with clearTimeout to prevent memory leaks
- CardItem: Use isExpanded prop instead of expanded for grid mode support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] ESLint eqeqeq error and add loading state to logs modal

- Fixed ESLint eqeqeq error: Changed != null to !== null && !== undefined check
- Added loading state to LogsModal to display spinner while fetching logs
- Updated fetchExecutionLogs to support optional setIsLoading callback
- Wired up isFetchingLogs state in both Pipelines and ApiDeployment components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Resolve card grid rendering issues and UI glitches

Fix card grid toggle flashing by memoizing CardItem and using optimistic updates. Prevent edit list from disappearing by using inline updates instead of refetch. Restore scroll position on back navigation using location.key. Remove unnecessary success alert on pipeline update. Keep share button always visible for consistency with workflows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Remove ineffective CSS hover overrides for action icons

Remove background overrides that don't work due to AntD CSS-in-JS
specificity. Action icons will now show AntD's default hover state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Backend sorting and share modal improvements

- Add backend ordering by last_run_time for API deployments using
  Subquery annotation with nulls_last for "Never" entries
- Fix share modal by passing deployment directly to avoid stale state
- Add robust response handling for different API response structures
- Remove Tooltip wrappers from action buttons to fix shadow issues

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Extract shared hooks to reduce duplication in Pipelines and ApiDeployment

Extract duplicated logic into 4 reusable hooks (useExecutionLogs,
usePaginatedList, useScrollRestoration, useShareModal) and wire them
into both components. Also fixes a bug where ApiDeployment lost
searchTerm when changing pages or deleting items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* MISC [FIX] Suppress eslint import/no-unresolved for Webpack url import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

Address Vishnu's review comments on PR #1769:
- Field rows: div/span → Flex + Typography.Text + Space
- img → AntD Avatar for WorkflowIcon
- a/span → Typography.Link / Typography.Text in ApiEndpointSection
- Header wrappers → Flex in CardHeaderRow and CardItem
- Actions container → Space in CardItem
- CSS: display block on subtitle, endpoint link targets .ant-typography-link

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [FIX] Make Vite optional plugin emit throw instead of empty export

The empty module (`export default undefined`) made plugin imports
succeed silently, bypassing catch blocks and causing a login loop
in OSS mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [MISC] Fix biome v2 lint and format issues in card grid view files

Sort imports, wrap single-line if/return in block statements,
add trailing commas, and fix CSS formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Replace divs/spans with AntD components, remove duplicate CSS

- Replaced raw HTML elements with Ant Design components (Flex, Space, Typography.Text, Avatar, Tag)
- Removed ~80 lines of custom CSS that duplicated AntD native functionality
- Fixed API endpoint Card padding/border specificity issues
- Fixed copy button padding in pipeline cards
- Addresses PR #1769 review feedback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix tooltip icon colors, move lazy imports to top-level

Preserve green/red icon colors in status pill tooltips where
`color: inherit` was making them white on dark background.
Move unnecessary lazy imports to top-level across dashboard_metrics,
pipeline_v2 and workflow_manager modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarCloud code smells in card grid view and related files

- Extract helpers to reduce function nesting depth (S2004)
- Replace nested ternary with nullish coalescing (S3358)
- Remove unused imports in DetailedLogs (S1128)
- Move PostHog try-catch into usePostHogEvents hook (S2486)
- Use globalThis over window (S7764)
- Fix non-native interactive element in CardItem (S6848)
- Use stable keys instead of array index in LoadingSkeleton (S6479)
- Improve contrast ratio for success status badge (S7924)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Deepak-Kesavan pushed a commit that referenced this pull request Mar 4, 2026
* UN-1798: Fix dashboard metrics - HITL, pages processed, sidebar nav

- Fix HITL import path: manual_review_v2 → pluggable_apps.manual_review_v2
- Fix HITL model: ManualReviewEntity → HITLQueue with _base_manager
- Fix pages_processed: resolve org string identifier from UUID for PageUsage
- Add HITL metrics to live_series endpoint and summary
- Restore metrics dashboard sidebar nav removed by sidebar refactor (#1768)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FEAT] Card based layout to list Pipelines and API Deployments (#1769)

* UN-3154 [FEAT] Add execution status tracking and list enhancements

- Add WorkflowExecution.get_last_run_statuses() method to fetch last N
  executions with PARTIAL_SUCCESS computed dynamically
- Add last_5_run_statuses, run_count, last_run_time to API deployment serializer
- Add last_5_run_statuses, next_run_time to pipeline serializer
- Add source/destination instance names to pipeline representation
- Enable pagination (CustomPagination) for both pipelines and deployments
- Add search filter by name for both endpoints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Add reusable CardGridView component

- Create CardGridView widget for displaying items in card/list layouts
- Add CardItem component with expand/collapse, status badges, actions
- Add LoadingSkeleton for loading states
- Support pagination, search, scroll restoration, and card configuration
- Add dateFormatter helper for consistent date formatting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor API Deployments to use CardGridView

- Add ApiDeploymentCardConfig for card layout configuration
- Refactor ApiDeployment to use CardGridView with pagination
- Update api-deployments-service with pagination and search support
- Refactor Body component to use CardGridView instead of Table
- Add search functionality to Header component
- Update Layout to pass card configuration and pagination props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Refactor Pipelines to use CardGridView

- Add PipelineCardConfig with StatusPills component for execution status
- Display source/destination connector icons and instance names
- Show last 5 run statuses with clickable navigation to execution logs
- Add schedule display, next run time, and run count
- Support pagination and search functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FEAT] Improve navigation with scroll restoration

- Update MenuLayout to forward scrollToCardId state on back navigation
- Pass scrollToCardId from workflow links in card configs
- Add back route with state to ExecutionLogs for scroll restoration
- Remove redundant back button from DetailedLogs header
- Add previousRoute and previousRouteState props to ToolNavBar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit review comments

- Remove unused variables: type, filteredData, copyUrl, handleStatusRefresh,
  getPipelineData, sessionDetails
- Wrap cronstrue.toString() in try-catch for invalid cron expressions
- Use current pagination when refreshing after pipeline enable/disable
- Use String() coercion for type-safe ID comparison in CardGridView
- Update PropTypes to accept string or number for IDs
- Improve shortenApiEndpoint to strip query/hash and handle edge cases
- Merge state properly in MenuLayout back navigation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarQube issues and reduce code duplication

- Fix HIGH severity: Remove orphaned setFilteredData references in Pipelines.jsx
- Fix LOW severity: Add keyboard listeners to clickable spans for accessibility
  - PipelineCardConfig.jsx: Add role, tabIndex, onKeyDown to status badge
  - CardItem.jsx: Add keyboard handlers to action spans and expand chevron
- Reduce code duplication between ApiDeploymentCardConfig and PipelineCardConfig
  - Extract shared components to CardFieldComponents.jsx:
    CardActionBox, OwnerFieldRow, LastRunFieldRow, Last5RunsFieldRow,
    WorkflowFieldRow, ApiEndpointSection, CardHeaderRow
  - Net reduction: -289 lines in existing files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud ReDoS security hotspot

Replace regex /\/+$/ with iterative string manipulation to strip
trailing slashes, avoiding potential ReDoS concerns flagged by
SonarCloud security scanner.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix function argument mismatch in clearFileMarkers

Update clearFileMarkers to accept optional workflowId parameter
to match the call site that passes pipeline.workflow_id.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarQube issues and improve code quality

- Remove unused useSessionStore import in DetailedLogs.jsx
- Add search term persistence across pagination in Pipelines.jsx
- Fix stale closure in pipelineCardConfig useMemo dependencies
- Add keyboard accessibility (role, tabIndex, onKeyDown) to action icons
- Guard against undefined orgName in WorkflowFieldRow link rendering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address SonarCloud code quality issues

- Fix array index keys in PipelineCardConfig.jsx and LoadingSkeleton.jsx
- Replace role="button" with native <button> elements for accessibility
- Fix text contrast ratios to meet WCAG AA (4.5:1 minimum)
- Fix unexpected negated conditions in pagination handlers
- Use globalThis instead of window for better portability
- Add proper button reset styles in CSS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Revert status pill colors and fix pipeline sorting

- Revert status badge colors to vibrant originals (pre-SonarCloud fix)
- Fix pipeline list sorting with nulls_last for last_run_time
- Add index fallback to React key in StatusPills to prevent duplicates
- Simplify enableDisablePipeline by removing unnecessary .then()
- Remove unused pagination deps from useMemo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Restore full list context on back navigation from logs

- Pass listContext (page, pageSize, searchTerm) through StatusPills navigation
- Restore pagination and search state in useEffect before scrolling
- Add listContext prop to Last5RunsFieldRow and StatusPills components
- Update useMemo dependencies to include pagination state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

- Replace native buttons with AntD Button type="text" in CardActionBox
- Replace native button with AntD Button for expand chevron in CardItem
- Use AntD Space for status badges container in StatusPills
- Use AntD Tag instead of native button/span for status badges
- Fix require() inside component body → static import in Pipelines.jsx
- Update CSS to support both native and AntD component selectors
- Add missing listContext prop type to StatusPills

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Address CodeRabbit PR review comments from #1769

- ApiDeployment: Use explicit null check instead of falsy check for data.count
- CardFieldComponents: Add URL scheme validation to prevent javascript: URLs
- PipelineCardConfig: Add keyboard accessibility to status badges
- CardGridView.css: Increase max-height from 200px to 500px for expanded content
- CardItem: Add setTimeout cleanup with clearTimeout to prevent memory leaks
- CardItem: Use isExpanded prop instead of expanded for grid mode support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] ESLint eqeqeq error and add loading state to logs modal

- Fixed ESLint eqeqeq error: Changed != null to !== null && !== undefined check
- Added loading state to LogsModal to display spinner while fetching logs
- Updated fetchExecutionLogs to support optional setIsLoading callback
- Wired up isFetchingLogs state in both Pipelines and ApiDeployment components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Resolve card grid rendering issues and UI glitches

Fix card grid toggle flashing by memoizing CardItem and using optimistic updates. Prevent edit list from disappearing by using inline updates instead of refetch. Restore scroll position on back navigation using location.key. Remove unnecessary success alert on pipeline update. Keep share button always visible for consistency with workflows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Remove ineffective CSS hover overrides for action icons

Remove background overrides that don't work due to AntD CSS-in-JS
specificity. Action icons will now show AntD's default hover state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [FIX] Backend sorting and share modal improvements

- Add backend ordering by last_run_time for API deployments using
  Subquery annotation with nulls_last for "Never" entries
- Fix share modal by passing deployment directly to avoid stale state
- Add robust response handling for different API response structures
- Remove Tooltip wrappers from action buttons to fix shadow issues

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Extract shared hooks to reduce duplication in Pipelines and ApiDeployment

Extract duplicated logic into 4 reusable hooks (useExecutionLogs,
usePaginatedList, useScrollRestoration, useShareModal) and wire them
into both components. Also fixes a bug where ApiDeployment lost
searchTerm when changing pages or deleting items.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* MISC [FIX] Suppress eslint import/no-unresolved for Webpack url import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Replace native elements with AntD components per PR review

Address Vishnu's review comments on PR #1769:
- Field rows: div/span → Flex + Typography.Text + Space
- img → AntD Avatar for WorkflowIcon
- a/span → Typography.Link / Typography.Text in ApiEndpointSection
- Header wrappers → Flex in CardHeaderRow and CardItem
- Actions container → Space in CardItem
- CSS: display block on subtitle, endpoint link targets .ant-typography-link

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [FIX] Make Vite optional plugin emit throw instead of empty export

The empty module (`export default undefined`) made plugin imports
succeed silently, bypassing catch blocks and causing a login loop
in OSS mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [MISC] Fix biome v2 lint and format issues in card grid view files

Sort imports, wrap single-line if/return in block statements,
add trailing commas, and fix CSS formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [REFACTOR] Replace divs/spans with AntD components, remove duplicate CSS

- Replaced raw HTML elements with Ant Design components (Flex, Space, Typography.Text, Avatar, Tag)
- Removed ~80 lines of custom CSS that duplicated AntD native functionality
- Fixed API endpoint Card padding/border specificity issues
- Fixed copy button padding in pipeline cards
- Addresses PR #1769 review feedback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix tooltip icon colors, move lazy imports to top-level

Preserve green/red icon colors in status pill tooltips where
`color: inherit` was making them white on dark background.
Move unnecessary lazy imports to top-level across dashboard_metrics,
pipeline_v2 and workflow_manager modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-3154 [FIX] Fix SonarCloud code smells in card grid view and related files

- Extract helpers to reduce function nesting depth (S2004)
- Replace nested ternary with nullish coalescing (S3358)
- Remove unused imports in DetailedLogs (S1128)
- Move PostHog try-catch into usePostHogEvents hook (S2486)
- Use globalThis over window (S7764)
- Fix non-native interactive element in CardItem (S6848)
- Use stable keys instead of array index in LoadingSkeleton (S6479)
- Improve contrast ratio for success status badge (S7924)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* UN-1798: Merge subscription dashboard into metrics dashboard

- Move metrics dashboard from /metrics to /dashboard, replacing the old
  subscription-only dashboard page
- Add Subscription tab (cloud-only) showing plan info and daily page usage
- Add useSubscriptionUsage hook with overview data fallback
- Hide date range picker on subscription tab (not applicable)
- Add PlanBanner cloud-only guard (only show when subscription data exists)
- Make recent activity items clickable, navigating to execution logs
- Add back button in DetailedLogs to return to dashboard
- Remove old /metrics route and UnstractUsagePage import
- Consolidate sidebar to single "Dashboard" entry
- Improve cache key stability and add expired cache eviction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* UN-1798: Address review feedback - accessibility, labels, cache fix

- Remove duplicate _get_hitl_queue_model function from services.py
- Make recent activity rows keyboard accessible (tabIndex, role, onKeyDown)
- Fix HITL chart legend labels to use proper casing
- Fix formatMetricName to uppercase known acronyms (API, ETL, LLM, HITL)
- Show Subscription tab unconditionally when component is available
- Treat malformed cache entries as expired during eviction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-1798: Replace LLM Usage by Workflow with Usage by Deployment

The old "LLM Usage" tab queried the usage table grouped by workflow_id,
but most usage rows have NULL workflow_id making the tab empty. This
replaces it with per-deployment-type sub-tabs (API, ETL, Task, Workflow)
that join through workflow_execution to resolve deployment names.

Backend:
- Add get_deployment_usage() with 4 deployment type queries
- Add deployment_type query param to workflow-token-usage endpoint
- Enforce 30-day max date range for deployment usage queries
- Include execution stats, pages processed, and date range per deployment

Frontend:
- Replace useWorkflowTokenUsage with useDeploymentUsage hook
- Rewrite LLMUsageTable as DeploymentUsageTable with sub-tabs
- Add per-deployment-type localStorage caching
- Show 30-day limit note, hide 90-day preset on deployment tab

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* UN-1798: Show error state for failed deployment usage fetches

Surface backend/network errors in DeploymentUsageTable instead of
silently showing "No data" empty state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-1798: Fix Biome formatting in LLMUsageTable

Collapse multi-line import to single line per Biome formatter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-1798: Show empty state when dashboard has no data

Display a centered empty state with action buttons (Create Workflow,
Open Prompt Studio) when all data sources return empty, instead of
showing individual empty cards for each chart.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-1798: Align dashboard header and padding with other pages

- Remove icon from Dashboard header, use plain Typography (16px/600)
  matching ETL Pipelines, API Deployments, Prompt Studio pages
- Add horizontal padding to recent activity list items and usage table
  so content aligns with card headers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* UN-1798: Fix SonarQube code smells across dashboard

- Backend: extract 4 helper methods from get_deployment_usage to reduce
  cognitive complexity from 52 to ~10 per method
- MetricsChart: replace() → replaceAll() for ES2021 compliance
- MetricsDashboard: flip negated ternary condition
- metricsCache: remove unused catch parameter, add explanatory comment
- useMetricsData: specify react-hooks/exhaustive-deps in eslint-disable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Use ExecutionEntity enum instead of hardcoded deployment type strings

Replaces hardcoded "API", "ETL", "TASK", "WF" string literals with
ExecutionEntity enum values from workflow_manager.execution.enum,
as suggested in code review.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use default model managers instead of _base_manager for view-context queries

Replace _base_manager.filter(organization_id=...) with .objects which
auto-filters by the current user's organization via
DefaultOrganizationManagerMixin. This applies to _get_deployment_names
and get_deployment_usage which are only called from HTTP request
context where UserContext is available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Address PR review feedback for deployment usage endpoint

- Use ExecutionStatus.value instead of str() for enum comparisons
- Rename first_date/last_date to first_execution_at/last_execution_at
- Remove organization_id param from get_deployment_usage (org scoping
  handled by DefaultOrganizationManagerMixin via model managers)
- Move deployment_type validation and 30-day range cap into new
  DeploymentUsageQuerySerializer, using DRF ChoiceField for validation
- Remove manual error response construction in favor of DRF's
  raise_exception=True
- Update frontend table column to match renamed API field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Address PR review feedback: query optimization, serializer validation, UI polish

- Replace Python-dict materialization with DB-level Subquery+OuterRef for pages aggregation
- Add range_truncated flag to API response; frontend conditionally shows 30-day notice
- Move inline styles to CSS classes in LLMUsageTable
- Add range_truncated to DeploymentUsageQuerySerializer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix Sonar issues: reduce cognitive complexity, use replaceAll

- Refactor _build_exec_stats to reduce cognitive complexity from 18 to ~11
  using setdefault, dict-based status lookup, and min/max for timestamps
- Use String.replaceAll() instead of String.replace() in MetricsChart.jsx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix deployment usage: restore _get_usage_queryset() for Usage queries

Usage.objects uses DefaultOrganizationManagerMixin which auto-filters by
UserContext.get_organization(). This can return empty results when the
org context doesn't match. Restore _get_usage_queryset() which uses
_base_manager to bypass this filter, matching the pattern used by all
other Usage queries in the service.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix UUID/varchar type mismatch in pages query

The Subquery approach in _get_pages_by_deployment used
OuterRef("run_id") (varchar) to match WorkflowFileExecution.id (UUID),
causing a PostgreSQL type mismatch error. Revert to the working
two-query approach that explicitly converts IDs to strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
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.

3 participants