diff --git a/client/public/__manus__/version.json b/client/public/__manus__/version.json index 4bcdcc1..490154a 100644 --- a/client/public/__manus__/version.json +++ b/client/public/__manus__/version.json @@ -1,4 +1,4 @@ { - "version": "f8f8affc", - "timestamp": 1783434543359 + "version": "bb8a78b4", + "timestamp": 1783732106605 } \ No newline at end of file diff --git a/client/src/pages/AAPDashboard.tsx b/client/src/pages/AAPDashboard.tsx index 40e6ffd..6ca5c00 100644 --- a/client/src/pages/AAPDashboard.tsx +++ b/client/src/pages/AAPDashboard.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo } from "react"; +import { useState, useMemo, useEffect } from "react"; import { useLocalAuth } from "@/contexts/LocalAuthContext"; import { toast } from "sonner"; import { trpc } from "@/lib/trpc"; @@ -117,13 +117,23 @@ export default function AAPDashboard() { // Marquage lu/non lu const [readIds, setReadIds] = useState>(new Set()); + const readIdsQuery = trpc.aap.getReadIds.useQuery(undefined, { enabled: !!user }); + useEffect(() => { + if (readIdsQuery.data?.ids) { + setReadIds(new Set(readIdsQuery.data.ids)); + } + }, [readIdsQuery.data]); const markAsReadMutation = trpc.aap.markAsRead.useMutation({ onSuccess: (_, vars) => { setReadIds((prev) => { const next = new Set(prev); next.add(vars.articleId); return next; }); + utils.aap.getReadIds.invalidate(); }, }); const markAllAsReadMutation = trpc.aap.markAllAsRead.useMutation({ - onSuccess: () => { utils.aap.unreadCount.invalidate(); }, + onSuccess: () => { + utils.aap.unreadCount.invalidate(); + utils.aap.getReadIds.invalidate(); + }, }); const unreadCountQuery = trpc.aap.unreadCount.useQuery(); const unreadCount = unreadCountQuery.data?.count ?? 0; diff --git a/client/src/pages/VeilleDashboard.tsx b/client/src/pages/VeilleDashboard.tsx index d8d7473..5e60176 100644 --- a/client/src/pages/VeilleDashboard.tsx +++ b/client/src/pages/VeilleDashboard.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo } from "react"; +import { useState, useMemo, useEffect } from "react"; import { useLocalAuth } from "@/contexts/LocalAuthContext"; import { toast } from "sonner"; import { trpc } from "@/lib/trpc"; @@ -234,14 +234,22 @@ export default function VeilleDashboard() { // Marquage lu/non lu const [readIds, setReadIds] = useState>(new Set()); + const readIdsQuery = trpc.veille.getReadIds.useQuery(undefined, { enabled: !!user }); + useEffect(() => { + if (readIdsQuery.data?.ids) { + setReadIds(new Set(readIdsQuery.data.ids)); + } + }, [readIdsQuery.data]); const markAsReadMutation = trpc.veille.markAsRead.useMutation({ onSuccess: (_, vars) => { setReadIds((prev) => { const next = new Set(prev); next.add(vars.articleId); return next; }); + utils.veille.getReadIds.invalidate(); }, }); const markAllAsReadMutation = trpc.veille.markAllAsRead.useMutation({ onSuccess: () => { utils.veille.unreadCount.invalidate(); + utils.veille.getReadIds.invalidate(); }, }); const unreadCountQuery = trpc.veille.unreadCount.useQuery(); diff --git a/server/routers.ts b/server/routers.ts index d0d6492..24b954f 100644 --- a/server/routers.ts +++ b/server/routers.ts @@ -226,6 +226,15 @@ export const appRouter = router({ const read = readRows[0]?.cnt ?? 0; return { count: Math.max(0, total - read) }; }), + getReadIds: protectedProcedure.query(async ({ ctx }) => { + const db = await getDb(); + if (!db) return { ids: [] as number[] }; + const rows = await db + .select({ articleId: articleReads.articleId }) + .from(articleReads) + .where(and(eqDrizzle(articleReads.userId, ctx.user.id), eqDrizzle(articleReads.articleType, "veille"))); + return { ids: rows.map((r: { articleId: number }) => r.articleId) }; + }), }), // ─── AAPP ──────────────────────────────────────────────────────────────────── aap: router({ @@ -353,8 +362,17 @@ export const appRouter = router({ const read = readRows[0]?.cnt ?? 0; return { count: Math.max(0, total - read) }; }), + getReadIds: protectedProcedure.query(async ({ ctx }) => { + const db = await getDb(); + if (!db) return { ids: [] as number[] }; + const rows = await db + .select({ articleId: articleReads.articleId }) + .from(articleReads) + .where(and(eqDrizzle(articleReads.userId, ctx.user.id), eqDrizzle(articleReads.articleType, "aap"))); + return { ids: rows.map((r: { articleId: number }) => r.articleId) }; + }), }), - // ─── Importt ───────────────────────────────────────────────────────────────── + // ─── Import ───────────────────────────────────────────────────────────────── import: router({ run: adminProcedure .input(z.object({ type: z.enum(["veille", "aap", "all"]).default("all") }))