fix: Refactor date filtering logic for pagination in ActivityFeed and ActivityService
This commit is contained in:
@@ -67,10 +67,14 @@ export class ActivityService {
|
|||||||
const fetchLimit = effectiveLimit;
|
const fetchLimit = effectiveLimit;
|
||||||
|
|
||||||
// Default date range to last 30 days if not specified
|
// Default date range to last 30 days if not specified
|
||||||
|
// IMPORTANT: When cursor is provided (pagination), we should NOT apply the gte constraint
|
||||||
|
// to allow users to paginate back beyond the initial date range
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const defaultStartDate = new Date(now.getTime() - this.DEFAULT_DAYS_BACK * 24 * 60 * 60 * 1000);
|
const defaultStartDate = new Date(now.getTime() - this.DEFAULT_DAYS_BACK * 24 * 60 * 60 * 1000);
|
||||||
const dateFilter: Prisma.DateTimeFilter = {
|
const dateFilter: Prisma.DateTimeFilter = {
|
||||||
gte: startDate || defaultStartDate,
|
// Only apply start date filter on initial load (no cursor)
|
||||||
|
// This allows pagination to go back indefinitely
|
||||||
|
...(cursor ? {} : {gte: startDate || defaultStartDate}),
|
||||||
...(endDate ? {lte: endDate} : {}),
|
...(endDate ? {lte: endDate} : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -364,6 +368,14 @@ export class ActivityService {
|
|||||||
const where: Prisma.EmailWhereInput = {
|
const where: Prisma.EmailWhereInput = {
|
||||||
projectId,
|
projectId,
|
||||||
...(contactId ? {contactId} : {}),
|
...(contactId ? {contactId} : {}),
|
||||||
|
// Apply cursor-based pagination filter on createdAt
|
||||||
|
// This is critical for pagination to work correctly
|
||||||
|
createdAt: cursorTimestamp
|
||||||
|
? {
|
||||||
|
...dateFilter,
|
||||||
|
lt: cursorTimestamp,
|
||||||
|
}
|
||||||
|
: dateFilter,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build OR conditions to filter by the appropriate timestamp field for each activity type
|
// Build OR conditions to filter by the appropriate timestamp field for each activity type
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function ActivityFeed({typeFilter, dateRangeDays = 30, contactId}: Activi
|
|||||||
return date.toISOString();
|
return date.toISOString();
|
||||||
}, [dateRangeDays]);
|
}, [dateRangeDays]);
|
||||||
|
|
||||||
const fetchActivities = useCallback(
|
const fetchActivities = useCallback(
|
||||||
async (cursor?: string) => {
|
async (cursor?: string) => {
|
||||||
try {
|
try {
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
@@ -43,9 +43,14 @@ export function ActivityFeed({typeFilter, dateRangeDays = 30, contactId}: Activi
|
|||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
limit: '20', // Conservative limit to avoid overloading
|
limit: '20', // Conservative limit to avoid overloading
|
||||||
startDate: startDate,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Only apply startDate filter on initial load, not during pagination
|
||||||
|
// When cursor is present, we're paginating backwards and should not limit by startDate
|
||||||
|
if (!cursor) {
|
||||||
|
params.set('startDate', startDate);
|
||||||
|
}
|
||||||
|
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
params.set('cursor', cursor);
|
params.set('cursor', cursor);
|
||||||
}
|
}
|
||||||
@@ -74,9 +79,7 @@ export function ActivityFeed({typeFilter, dateRangeDays = 30, contactId}: Activi
|
|||||||
|
|
||||||
// For each new activity, reuse existing object if ID matches (preserves React component instances)
|
// For each new activity, reuse existing object if ID matches (preserves React component instances)
|
||||||
// Otherwise use new object. This maintains correct ordering while preserving component state.
|
// Otherwise use new object. This maintains correct ordering while preserving component state.
|
||||||
return result.data.map(newActivity =>
|
return result.data.map(newActivity => existingMap.get(newActivity.id) ?? newActivity);
|
||||||
existingMap.get(newActivity.id) ?? newActivity
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,9 +124,7 @@ export function ActivityFeed({typeFilter, dateRangeDays = 30, contactId}: Activi
|
|||||||
const existingMap = new Map(prev.map(activity => [activity.id, activity]));
|
const existingMap = new Map(prev.map(activity => [activity.id, activity]));
|
||||||
|
|
||||||
// For each new activity, reuse existing object if ID matches
|
// For each new activity, reuse existing object if ID matches
|
||||||
return result.activities.map(newActivity =>
|
return result.activities.map(newActivity => existingMap.get(newActivity.id) ?? newActivity);
|
||||||
existingMap.get(newActivity.id) ?? newActivity
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching upcoming activities:', err);
|
console.error('Error fetching upcoming activities:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user