Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 1x 1x 19x 1x 8x 8x 8x 1x 6x 6x 6x 6x 6x 6x 1x 6x 6x 6x 6x 6x 6x 4x 4x 1x 78x 78x 78x 78x 78x 2x 2x 2x 2x 2x 2x 78x 78x 1x 78x 78x 78x 1x 8x 8x 8x 8x 8x 8x 8x 8x 8x | import { formatDateToISODateString } from '@/utils/date';
import { DailyCommentCounts } from '@/types/meal';
import { CommentListSummary } from '@/types/menuFeedback';
/**
* Per-day counts keyed by a VN day-key ("YYYY-MM-DD"). The dashboard header sums
* this map and the calendar dots days with unread > 0 — both independent of the
* paginated comment list. Seeded from `summaryByDay`, then individual days are
* overwritten by the realtime `dayCounts` stream.
*/
export type DailyCommentCountsMap = Record<string, DailyCommentCounts>;
/** VN day-key for either an ISO string or a Date (matches how the BE buckets days). */
export const dayKeyOf = (date: string | Date): string =>
formatDateToISODateString(date);
/**
* "YYYY-MM-DD" → a LOCAL-midnight Date, so react-day-picker matches it to the
* right calendar cell regardless of the browser timezone.
*/
export const isoDayToLocalDate = (key: string): Date => {
const [year, month, day] = key.split('-').map(Number);
return new Date(year ?? 1970, (month ?? 1) - 1, day ?? 1);
};
export const seedDayMap = (
rows: DailyCommentCounts[],
): DailyCommentCountsMap => {
const map: DailyCommentCountsMap = {};
for (const row of rows) map[dayKeyOf(row.date)] = row;
return map;
};
/**
* Last-write-wins by `computedAt` (server clock on both the seed and the stream),
* so a reordered or duplicate delivery can never overwrite a fresher day.
*/
export const applyDailyCommentCounts = (
map: DailyCommentCountsMap,
next: DailyCommentCounts,
): DailyCommentCountsMap => {
const key = dayKeyOf(next.date);
const current = map[key];
if (current && current.computedAt >= next.computedAt) return map;
return { ...map, [key]: next };
};
export const sumDayMap = (map: DailyCommentCountsMap): CommentListSummary => {
let total = 0;
let good = 0;
let bad = 0;
let unread = 0;
for (const key of Object.keys(map)) {
const day = map[key]!;
total += day.total;
good += day.good;
bad += day.bad;
unread += day.unread;
}
return { total, good, bad, unread };
};
export const unreadDaysFromMap = (map: DailyCommentCountsMap): Date[] =>
Object.entries(map)
.filter(([, day]) => day.unread > 0)
.map(([key]) => isoDayToLocalDate(key));
/** Determine which dashboard maps need updating based on realtime date. */
export const getAffectedMaps = (
date: string,
rangeFromDate: string,
rangeToDate: string,
monthFromDate: string,
monthToDate: string,
): { updateRange: boolean; updateMonth: boolean } => ({
updateRange: date >= rangeFromDate && date <= rangeToDate,
updateMonth: date >= monthFromDate && date <= monthToDate,
});
|