Deduplicate guest tasks list and restore header icon
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
Clock,
|
Clock,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
|
LifeBuoy,
|
||||||
UploadCloud,
|
UploadCloud,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
Check,
|
Check,
|
||||||
|
|||||||
22
resources/js/guest/lib/__tests__/taskUtils.test.ts
Normal file
22
resources/js/guest/lib/__tests__/taskUtils.test.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { dedupeTasksById } from '../taskUtils';
|
||||||
|
|
||||||
|
describe('dedupeTasksById', () => {
|
||||||
|
it('returns empty array for empty input', () => {
|
||||||
|
expect(dedupeTasksById([])).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the first occurrence and preserves order', () => {
|
||||||
|
const tasks = [
|
||||||
|
{ id: 1, title: 'A' },
|
||||||
|
{ id: 2, title: 'B' },
|
||||||
|
{ id: 1, title: 'A-dup' },
|
||||||
|
{ id: 3, title: 'C' },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(dedupeTasksById(tasks)).toEqual([
|
||||||
|
{ id: 1, title: 'A' },
|
||||||
|
{ id: 2, title: 'B' },
|
||||||
|
{ id: 3, title: 'C' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
18
resources/js/guest/lib/taskUtils.ts
Normal file
18
resources/js/guest/lib/taskUtils.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export type TaskIdentity = {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function dedupeTasksById<T extends TaskIdentity>(tasks: T[]): T[] {
|
||||||
|
const seen = new Set<number>();
|
||||||
|
const unique: T[] = [];
|
||||||
|
|
||||||
|
tasks.forEach((task) => {
|
||||||
|
if (seen.has(task.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(task.id);
|
||||||
|
unique.push(task);
|
||||||
|
});
|
||||||
|
|
||||||
|
return unique;
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ import { getDeviceId } from '../lib/device';
|
|||||||
import { FADE_SCALE, FADE_UP, STAGGER_FAST, getMotionContainerProps, getMotionItemProps, prefersReducedMotion } from '../lib/motion';
|
import { FADE_SCALE, FADE_UP, STAGGER_FAST, getMotionContainerProps, getMotionItemProps, prefersReducedMotion } from '../lib/motion';
|
||||||
import PullToRefresh from '../components/PullToRefresh';
|
import PullToRefresh from '../components/PullToRefresh';
|
||||||
import { triggerHaptic } from '../lib/haptics';
|
import { triggerHaptic } from '../lib/haptics';
|
||||||
|
import { dedupeTasksById } from '../lib/taskUtils';
|
||||||
|
|
||||||
interface Task {
|
interface Task {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -133,9 +134,10 @@ export default function TaskPickerPage() {
|
|||||||
? payload.tasks
|
? payload.tasks
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const entry = { data: taskList, etag: response.headers.get('ETag') };
|
const uniqueTasks = dedupeTasksById(taskList);
|
||||||
|
const entry = { data: uniqueTasks, etag: response.headers.get('ETag') };
|
||||||
tasksCacheRef.current.set(cacheKey, entry);
|
tasksCacheRef.current.set(cacheKey, entry);
|
||||||
setTasks(taskList);
|
setTasks(uniqueTasks);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load tasks', err);
|
console.error('Failed to load tasks', err);
|
||||||
setError(err instanceof Error ? err.message : 'Unbekannter Fehler');
|
setError(err instanceof Error ? err.message : 'Unbekannter Fehler');
|
||||||
|
|||||||
Reference in New Issue
Block a user