Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
7251661
feat(ui-perf): If-None-Match → 304 short-circuit + client ETag cache
harshach May 10, 2026
aed45a1
feat(ui-perf): defer Queries-tab fetch until tab activated
harshach May 10, 2026
101a972
feat(ui-perf): lazy-mount below-fold widgets on landing page
harshach May 10, 2026
f6654fc
fix(ui-perf): address PR review feedback for P1 perceived-latency
harshach May 10, 2026
5ab5276
test(ui-perf): unblock tests against DeferredWidget on landing page
harshach May 10, 2026
3782a0c
test(ui): force single React copy for core-components in jest
harshach May 10, 2026
c515580
revert(ui-perf): drop P1.3 DeferredWidget — broke Playwright shards
harshach May 11, 2026
d825f3d
fix(ui-perf): drop Cache-Control on ETag responses — broke DataContra…
harshach May 17, 2026
4775d71
Merge branch 'main' into harshach/perceived-latency-p1
harshach May 17, 2026
45c9730
fix(ui-perf): invalidate in-memory ETag cache on any mutation response
harshach May 17, 2026
3f03ee1
Merge branch 'main' into harshach/perceived-latency-p1
harshach May 17, 2026
94e5062
fix(ui-perf): address PR review on ETag cache (clone-on-write + 304 r…
harshach May 18, 2026
a9efde0
fix(ui-perf): emit Cache-Control: no-store on ETag responses
harshach May 18, 2026
83085e4
fix(ui-perf): don't re-insert snapshot fallback into the ETag cache
harshach May 21, 2026
25d79b0
feat(ui-perf): defer Activity Feed activity-count fetch across all en…
harshach May 21, 2026
f09f040
feat(perf): cheap per-entity activity count via limit=0; re-eager Act…
harshach May 21, 2026
7f40077
feat(ui-perf): lazy-mount below-fold landing-page widgets (re-attempt)
harshach May 21, 2026
7577c56
perf(ui): dedup landing-page announcements fetch
harshach May 21, 2026
5de535d
feat(ui-perf): wire @tanstack/react-query at the app root
harshach May 21, 2026
a6b17a4
feat(ui-perf): migrate TableDetailsPageV1 to useQuery + optimistic fo…
harshach May 22, 2026
ff9cdbf
feat(ui-perf): prefetch Table details on hover from search result cards
harshach May 22, 2026
9a76d5c
feat(ui-perf): migrate DashboardDetailsPage to useQuery + optimistic …
harshach May 22, 2026
afbfd4b
feat(ui-perf): migrate PipelineDetailsPage to useQuery + optimistic f…
harshach May 22, 2026
6152d8f
feat(ui-perf): migrate TopicDetailsPage to useQuery + optimistic follow
harshach May 22, 2026
f5bac95
feat(ui-perf): prefetch Dashboard/Pipeline/Topic on hover too
harshach May 22, 2026
c9942c2
fix(ui-perf): break dashboardQuery/pipelineQuery → utils circular import
harshach May 22, 2026
5d26539
chore(ui): add preview-server proxy config so yarn preview works locally
harshach May 22, 2026
1c7db21
feat(perf): branch Cache-Control by path on the asset servlet
harshach May 22, 2026
1ed5c1f
feat(perf): emit strong ETag on the SPA shell and 304 on If-None-Match
harshach May 22, 2026
338a5e6
chore(ui): disable modulepreload polyfill for modern-browser targets
harshach May 22, 2026
6780722
feat(perf): inline a CSS-only loading shell in index.html
harshach May 22, 2026
7bb952a
feat(perf): make HTTP/2 listener available via SERVER_PROTOCOL=h2c
harshach May 22, 2026
325f494
feat(perf): cache-first for hashed assets in the existing service worker
harshach May 22, 2026
5e4d085
docs(perf): CDN deployment guide for per-customer isolated clusters
harshach May 22, 2026
8f6fb85
fix(perf): emit ETag based on CSP policy, not nonce attribute presence
harshach May 22, 2026
ab69e6e
docs(perf): CDN guide for per-customer release pinning, no extra data…
harshach May 22, 2026
c00a21e
Merge branch 'main' into harshach/perceived-latency-p1
harshach May 22, 2026
ca0d223
feat(ui-perf): split heavy specialist libs into their own chunks, tar…
harshach May 22, 2026
04262ac
docs(perf): bundle-size follow-up plan with measurements + per-PR ROI
harshach May 22, 2026
96e5f2f
feat(ui-perf): lazy-load FeedEditor (Quill) inside ActivityFeedEditor
harshach May 22, 2026
ce0a94b
chore(ui-perf): mark react-data-grid type-only imports as `import type`
harshach May 22, 2026
5efeaff
feat(ui-perf): per-scope vendor chunking for fine-grained cache inval…
harshach May 22, 2026
5813998
chore(ui): organize-imports order fix on ActivityFeedEditor
harshach May 22, 2026
916f198
test(ui): align entity-page jest mocks with deferred feed-count helpers
harshach May 22, 2026
04494c7
fix(ui-perf): render DeferredWidget eagerly under headless automation
harshach May 22, 2026
1131cb5
fix(ui-perf): re-bind Table state writers when cache key shifts on pe…
harshach May 22, 2026
d2e5fd0
fix(ui-perf): show ErrorPlaceHolder on Table 404; revert FeedEditor lazy
harshach May 22, 2026
ab45e76
chore(ui-checkstyle): prettier reflow on DirectoryDetails.test mock
harshach May 22, 2026
4d2532e
fix(sonar): exclude src/test/** from main-source indexing
harshach May 22, 2026
e0a0fa7
feat(ui-perf): variable Inter font + dark-mode splash restore
harshach May 22, 2026
77f1968
chore(ui): full Apache 2.0 header on inter-variable.css
harshach May 23, 2026
7bd1892
feat(ui-perf): idle route prefetch + lazy Tour + drop eager image pre…
harshach May 23, 2026
a76a1ea
feat(ui-perf): migrate 16 entity detail pages to React Query
harshach May 23, 2026
050d5f7
perf(ui): lazy-load TasksDAGView to split DAG chunk off entry path
harshach May 23, 2026
51288b8
fix(ui-perf): dedupe inflight permission fetches in PermissionProvider
harshach May 23, 2026
55be615
feat(ui-perf): bundle-size follow-up — 6 lazy-load PRs in one commit
harshach May 23, 2026
9834fad
chore(ui-checkstyle): prettier reflow on service-utility schema loaders
harshach May 23, 2026
9a1c80d
test(ui): await React.lazy() settle in AppTour test
harshach May 23, 2026
64901a4
fix(ui-perf): staleTime=0 (stale-while-revalidate) to restore Playwri…
harshach May 23, 2026
dd1aad6
chore(docs): drop bundle-size-followup.md — all PRs landed
harshach May 23, 2026
4cd8737
perf(ui-perf): lazy-fetch entity `extension` via Custom Properties tab
harshach May 23, 2026
1368c49
fix(ui-perf): observe LazyDataGrid mount before focusing first cell
harshach May 23, 2026
e44fd8a
fix(ui-perf): break Container column deep-link permission loop
harshach May 23, 2026
2297b0b
fix(ui-perf): walk up to parent container when column-FQN 404s
harshach May 23, 2026
98c9eb1
Revert "perf(ui-perf): lazy-fetch entity `extension` via Custom Prope…
harshach May 24, 2026
6bc3f9e
fix(ui-perf): keep RJSF form stable when service-form data prop updates
harshach May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,24 @@ public int count(List<UUID> domainIds, long afterTimestamp) {
return activityStreamDAO.countByDomains(domainJson, domainIdStrings, afterTimestamp);
}

/** Get count of activity events for a specific entity. */
public int countByEntity(String entityType, UUID entityId, long afterTimestamp) {
return activityStreamDAO.countByEntity(entityType, entityId.toString(), afterTimestamp);
}

/** Get count of activity events for a specific entity scoped to specific domains. */
public int countByEntity(
String entityType, UUID entityId, List<UUID> domainIds, long afterTimestamp) {
if (nullOrEmpty(domainIds)) {
return countByEntity(entityType, entityId, afterTimestamp);
}

List<String> domainIdStrings = domainIds.stream().map(UUID::toString).toList();
String domainJson = JsonUtils.pojoToJson(domainIdStrings);
return activityStreamDAO.countByEntityAndDomains(
entityType, entityId.toString(), domainJson, domainIdStrings, afterTimestamp);
}

/** Delete events older than the cutoff timestamp. */
public int deleteOlderThan(long cutoffTimestamp) {
return activityStreamDAO.deleteOlderThan(cutoffTimestamp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13252,6 +13252,42 @@ int countByDomains(
@BindList("domainIds") List<String> domainIds,
@Bind("after") long after);

@ConnectionAwareSqlQuery(
value =
"SELECT count(*) FROM activity_stream WHERE entityType = :entityType AND entityId = :entityId "
+ "AND timestamp >= :after",
connectionType = MYSQL)
@ConnectionAwareSqlQuery(
value =
"SELECT count(*) FROM activity_stream WHERE entitytype = :entityType AND entityid = :entityId "
+ "AND timestamp >= :after",
connectionType = POSTGRES)
int countByEntity(
@Bind("entityType") String entityType,
@Bind("entityId") String entityId,
@Bind("after") long after);

@ConnectionAwareSqlQuery(
value =
"SELECT count(*) FROM activity_stream WHERE entityType = :entityType AND entityId = :entityId "
+ "AND JSON_OVERLAPS(domains, :domainJson) "
+ "AND timestamp >= :after",
connectionType = MYSQL)
@ConnectionAwareSqlQuery(
value =
"SELECT count(*) FROM activity_stream WHERE entitytype = :entityType AND entityid = :entityId "
+ "AND EXISTS ("
+ "SELECT 1 FROM jsonb_array_elements_text(domains) AS domain_id "
+ "WHERE domain_id IN (<domainIds>)) "
+ "AND timestamp >= :after",
connectionType = POSTGRES)
int countByEntityAndDomains(
@Bind("entityType") String entityType,
@Bind("entityId") String entityId,
@Bind("domainJson") String domainJson,
@BindList("domainIds") List<String> domainIds,
@Bind("after") long after);

@SqlUpdate("DELETE FROM activity_stream WHERE timestamp < :cutoff")
int deleteOlderThan(@Bind("cutoff") long cutoffTimestamp);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,20 +175,27 @@ public ResultList<ActivityEvent> getEntityActivityById(
@Max(90)
@QueryParam("days")
int days,
@Parameter(description = "Maximum number of events to return")
@Parameter(
description =
"Maximum number of events to return. Pass 0 for a count-only response "
+ "(empty data array, accurate paging.total).")
@DefaultValue("50")
@Min(1)
@Min(0)
@Max(200)
@QueryParam("limit")
int limit) {

long afterTimestamp = Instant.now().minus(days, ChronoUnit.DAYS).toEpochMilli();
List<UUID> domainIds = getEffectiveDomainsByFqn(securityContext, domain);
int total =
activityStreamRepository.countByEntity(entityType, entityId, domainIds, afterTimestamp);
if (limit == 0) {
return new ResultList<>(List.of(), null, null, total);
}
Comment on lines 188 to +194
Comment on lines 188 to +194
List<ActivityEvent> events =
activityStreamRepository.listByEntity(
entityType, entityId, domainIds, afterTimestamp, limit);

return new ResultList<>(events, null, null, events.size());
return new ResultList<>(events, null, null, total);
Comment on lines +190 to +198
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Performance: COUNT query always runs even when limit > 0

In both getEntityActivityById and getEntityActivityByFqn, the countByEntity query now runs unconditionally (lines 190-191, 248-249), even when the caller passes a non-zero limit. Previously total was just events.size() — zero extra DB round-trips. Now every normal activity-list request pays an additional COUNT(*) query against activity_stream just to populate paging.total.

For the limit=0 (badge-only) path this is correct and efficient. But for the full-list path (Activity Feed tab open, default limit=50), the extra COUNT is wasted work — the caller already has the rows and the old events.size() was sufficient for paginating a capped result set.

On high-activity entities or under load this doubles the DB cost of the endpoint for no user-visible benefit on the full-list path.

Only run the COUNT query when limit=0; for normal requests use events.size() as before.:

long afterTimestamp = Instant.now().minus(days, ChronoUnit.DAYS).toEpochMilli();
List<UUID> domainIds = getEffectiveDomainsByFqn(securityContext, domain);
if (limit == 0) {
  int total =
      activityStreamRepository.countByEntity(entityType, entityId, domainIds, afterTimestamp);
  return new ResultList<>(List.of(), null, null, total);
}
List<ActivityEvent> events =
    activityStreamRepository.listByEntity(
        entityType, entityId, domainIds, afterTimestamp, limit);
return new ResultList<>(events, null, null, events.size());
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

Comment on lines 188 to +198
}
Comment on lines 188 to 199

@GET
Expand Down Expand Up @@ -219,9 +226,13 @@ public ResultList<ActivityEvent> getEntityActivityByFqn(
@Max(90)
@QueryParam("days")
int days,
@Parameter(description = "Maximum number of events to return")
@Parameter(
description =
"Maximum number of events to return. Pass 0 for a count-only response "
+ "(empty data array, accurate paging.total). Frontend tab-badge fetches "
+ "use this path so first paint isn't blocked on a 100-row list query.")
@DefaultValue("50")
@Min(1)
@Min(0)
@Max(200)
@QueryParam("limit")
int limit) {
Expand All @@ -234,11 +245,15 @@ public ResultList<ActivityEvent> getEntityActivityByFqn(
UUID entityId = entity.getId();
List<UUID> domainIds = getEffectiveDomainsByFqn(securityContext, domain);

int total =
activityStreamRepository.countByEntity(entityType, entityId, domainIds, afterTimestamp);
if (limit == 0) {
return new ResultList<>(List.of(), null, null, total);
}
Comment on lines +248 to +252
List<ActivityEvent> events =
activityStreamRepository.listByEntity(
entityType, entityId, domainIds, afterTimestamp, limit);

return new ResultList<>(events, null, null, events.size());
return new ResultList<>(events, null, null, total);
Comment on lines +248 to +256
Comment on lines +248 to +256
}

@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,87 @@
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import org.openmetadata.schema.EntityInterface;
import org.openmetadata.service.monitoring.RequestLatencyContext;
import org.openmetadata.service.util.EntityETag;

/**
* JAX-RS filter that automatically adds ETag headers to GET responses
* containing EntityInterface entities.
* JAX-RS filter that adds an {@code ETag} header to entity GET responses and short-circuits to
* {@code 304 Not Modified} when the client's {@code If-None-Match} matches the computed ETag.
*
* <p>The 304 path saves the response body bytes on the wire and the client-side render cost on
* revisits — the server still computes the entity body (we'd need a cheap version-stamp lookup
* to truly skip the work, see design doc), but the network and client savings are immediate.
*
* <p>{@code Cache-Control: no-store} is emitted alongside the ETag. Without an explicit
* Cache-Control, Chrome falls back to heuristic caching for ETag-bearing responses and reuses
* the cached body on a 304. That breaks any mutation path where the server returns 304 with
* stale-relative-to-the-client state — notably the relationship-only mutations
* ({@code addFollower}, {@code removeFollower}, {@code updateVote},
* {@code DataContractRepository.updateLatestResult}) that don't bump entity {@code version} or
* {@code updatedAt} and therefore leave the ETag unchanged. With {@code no-store} the browser
* never caches a body, so the only conditional-GET path is our explicit Axios interceptor,
* which already invalidates its cache on every mutation response. We keep emitting the ETag
* header so any future client (or our own interceptor) can opt in to conditional GETs.
*/
@Provider
public class ETagResponseFilter implements ContainerResponseFilter {

private static final String CACHE_CONTROL_VALUE = "no-store";

@Override
public void filter(
ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
try (var ignored = RequestLatencyContext.phase("etagGeneration")) {
if ("GET".equals(requestContext.getMethod())
&& responseContext.getStatus() == Response.Status.OK.getStatusCode()
&& responseContext.getEntity() instanceof EntityInterface entity) {
if (!"GET".equals(requestContext.getMethod())
|| responseContext.getStatus() != Response.Status.OK.getStatusCode()
|| !(responseContext.getEntity() instanceof EntityInterface entity)) {
return;
}

String etag = EntityETag.generateETag(entity);
if (etag == null) {
return;
}
responseContext.getHeaders().putSingle(HttpHeaders.ETAG, etag);
responseContext.getHeaders().putSingle(HttpHeaders.CACHE_CONTROL, CACHE_CONTROL_VALUE);
Comment thread
harshach marked this conversation as resolved.

String ifNoneMatch = requestContext.getHeaderString(HttpHeaders.IF_NONE_MATCH);
if (ifNoneMatch == null) {
return;
}
if (matchesAny(ifNoneMatch, etag)) {
// RFC 7232: 304 must NOT include a message body. Drop the entity so the
// serializer emits an empty body. Headers (including ETag) are preserved.
responseContext.setStatus(Response.Status.NOT_MODIFIED.getStatusCode());
responseContext.setEntity(null);
}
}
}

String etag = EntityETag.generateETag(entity);
responseContext.getHeaders().add("ETag", etag);
/**
* RFC 7232 §3.2: {@code If-None-Match} can be {@code *} (match any), a single ETag, or a
* comma-separated list. Weak comparison is used — we treat {@code "abc"} and {@code W/"abc"}
* as matching, which is the spec's recommendation for cache-validation use.
*/
private static boolean matchesAny(String ifNoneMatch, String currentEtag) {
String trimmed = ifNoneMatch.trim();
if ("*".equals(trimmed)) {
return true;
}
String currentBare = stripWeakPrefix(currentEtag);
for (String candidate : trimmed.split(",")) {
if (currentBare.equals(stripWeakPrefix(candidate.trim()))) {
return true;
}
}
return false;
}

private static String stripWeakPrefix(String etag) {
return etag.startsWith("W/") ? etag.substring(2) : etag;
}
}
10 changes: 10 additions & 0 deletions openmetadata-ui/src/main/resources/ui/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ module.exports = {
'<rootDir>/src/test/unit/mocks/reactColumnResize.mock.js',
'^.*/Lineage/Layout/ELKUtil/ELKUtil$':
'<rootDir>/src/test/unit/mocks/elkLayout.mock.js',
// Force every `require('react')` / `require('react-dom')` to resolve to the consumer's
// copy. The `openmetadata-ui-core-components` package has its own `node_modules/react`
// (for its own dev/test) — without these mappings the CJS bundle loaded from
// `dist/*.cjs.js` resolves React from the core-components tree, producing a second React
// instance with a null hooks dispatcher and the classic "Invalid hook call ... reading
// 'useContext'" TypeError.
'^react$': '<rootDir>/node_modules/react',
'^react-dom$': '<rootDir>/node_modules/react-dom',
'^react/(.*)$': '<rootDir>/node_modules/react/$1',
'^react-dom/(.*)$': '<rootDir>/node_modules/react-dom/$1',
},
transformIgnorePatterns: [
'node_modules/(?!(@azure/msal-react|react-dnd|react-dnd-html5-backend|dnd-core|@react-dnd/invariant|@react-dnd/asap|@react-dnd/shallowequal|@melloware/react-logviewer|@material/material-color-utilities|@openmetadata/ui-core-components|nanoid|@rjsf/core|@rjsf/utils|@rjsf/validator-ajv8|uuid|elkjs))',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ import { useFqn } from '../../../hooks/useFqn';
import { FeedCounts } from '../../../interface/feed.interface';
import { restoreApiEndPoint } from '../../../rest/apiEndpointsAPI';
import apiEndpointClassBase from '../../../utils/APIEndpoints/APIEndpointClassBase';
import { getFeedCounts } from '../../../utils/CommonUtils';
import {
fetchEntityActivityCountInto,
fetchEntityTaskCountsInto,
getFeedCounts,
} from '../../../utils/CommonUtils';
import {
checkIfExpandViewSupported,
getDetailsTabWithNewLabel,
Expand Down Expand Up @@ -179,6 +183,22 @@ const APIEndpointDetails: React.FC<APIEndpointDetailsProps> = ({
handleFeedCount
);

const fetchTaskCounts = useCallback(() => {
if (decodedApiEndpointFqn) {
fetchEntityTaskCountsInto(decodedApiEndpointFqn, setFeedCount);
}
}, [decodedApiEndpointFqn]);

const fetchActivityCount = useCallback(() => {
if (decodedApiEndpointFqn) {
fetchEntityActivityCountInto(
EntityType.API_ENDPOINT,
decodedApiEndpointFqn,
setFeedCount
);
}
}, [decodedApiEndpointFqn]);

const afterDeleteAction = useCallback(
(isSoftDelete?: boolean) => !isSoftDelete && navigate('/'),
[navigate]
Expand Down Expand Up @@ -209,7 +229,8 @@ const APIEndpointDetails: React.FC<APIEndpointDetailsProps> = ({
);

useEffect(() => {
getEntityFeedCount();
fetchTaskCounts();
fetchActivityCount();
}, [apiEndpointPermissions, decodedApiEndpointFqn]);

const tabs = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { withDomainFilter } from '../../../hoc/withDomainFilter';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation';
import axiosClient from '../../../rest';
import { clearEtagCache } from '../../../rest/etagInterceptor';
import {
fetchAuthenticationConfig,
fetchAuthorizerConfig,
Expand Down Expand Up @@ -218,6 +219,10 @@ export const AuthProvider = ({
// Clear tokens properly during logout
await clearOidcToken();

// Drop the ETag interceptor's response cache so a freshly-authenticated user can't
// pick up another principal's cached body via If-None-Match → 304 mid-session.
clearEtagCache();

setApplicationLoading(false);

// Clear the refresh flag (used after refresh is complete)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ import { useFqn } from '../../../hooks/useFqn';
import { FeedCounts } from '../../../interface/feed.interface';
import { restoreChart } from '../../../rest/chartsAPI';
import chartDetailsClassBase from '../../../utils/ChartDetailsClassBase';
import { getFeedCounts } from '../../../utils/CommonUtils';
import {
fetchEntityActivityCountInto,
fetchEntityTaskCountsInto,
getFeedCounts,
} from '../../../utils/CommonUtils';
import {
checkIfExpandViewSupported,
getDetailsTabWithNewLabel,
Expand Down Expand Up @@ -127,8 +131,25 @@ const ChartDetails = ({
const getEntityFeedCount = () =>
getFeedCounts(EntityType.CHART, decodedChartFQN, handleFeedCount);

const fetchTaskCounts = useCallback(() => {
if (decodedChartFQN) {
fetchEntityTaskCountsInto(decodedChartFQN, setFeedCount);
}
}, [decodedChartFQN]);

const fetchActivityCount = useCallback(() => {
if (decodedChartFQN) {
fetchEntityActivityCountInto(
EntityType.CHART,
decodedChartFQN,
setFeedCount
);
}
}, [decodedChartFQN]);

useEffect(() => {
getEntityFeedCount();
fetchTaskCounts();
fetchActivityCount();
}, [decodedChartFQN]);

const handleTabChange = (activeKey: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ import { useCustomPages } from '../../../hooks/useCustomPages';
import { useFqn } from '../../../hooks/useFqn';
import { FeedCounts } from '../../../interface/feed.interface';
import { restoreDashboard } from '../../../rest/dashboardAPI';
import { getFeedCounts } from '../../../utils/CommonUtils';
import {
fetchEntityActivityCountInto,
fetchEntityTaskCountsInto,
getFeedCounts,
} from '../../../utils/CommonUtils';
import {
checkIfExpandViewSupported,
getDetailsTabWithNewLabel,
Expand Down Expand Up @@ -139,8 +143,25 @@ const DashboardDetails = ({
const getEntityFeedCount = () =>
getFeedCounts(EntityType.DASHBOARD, decodedDashboardFQN, handleFeedCount);

const fetchTaskCounts = useCallback(() => {
if (decodedDashboardFQN) {
fetchEntityTaskCountsInto(decodedDashboardFQN, setFeedCount);
}
}, [decodedDashboardFQN]);

const fetchActivityCount = useCallback(() => {
if (decodedDashboardFQN) {
fetchEntityActivityCountInto(
EntityType.DASHBOARD,
decodedDashboardFQN,
setFeedCount
);
}
}, [decodedDashboardFQN]);

useEffect(() => {
getEntityFeedCount();
fetchTaskCounts();
fetchActivityCount();
}, [decodedDashboardFQN]);

const handleTabChange = (activeKey: string) => {
Expand Down
Loading
Loading