@aiquants/daily-report 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -21
- package/dist/client.d.mts +3 -10
- package/dist/client.d.ts +3 -10
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +4 -4
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +2 -2
- package/dist/server.mjs.map +1 -1
- package/dist/{sse-schema-DO9Qe8XF.d.ts → sse-schema-CK7cUnEo.d.ts} +1 -34
- package/dist/{sse-schema-DKUjXMW3.d.mts → sse-schema-yl5AaSsj.d.mts} +1 -34
- package/dist/{types-DrjeK3_G.d.mts → types-CVhwLhSN.d.mts} +0 -1
- package/dist/{types-DrjeK3_G.d.ts → types-CVhwLhSN.d.ts} +0 -1
- package/package.json +8 -8
- package/src/client/components/daily-report-detail-list.tsx +10 -20
- package/src/client/components/daily-report-list.tsx +1 -13
- package/src/client/config-context.tsx +0 -9
- package/src/client/contexts/daily-report-action-context.tsx +0 -1
- package/src/server/service.ts +0 -2
- package/src/shared/sse-schema.ts +0 -1
- package/src/shared/types.ts +0 -1
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/client/components/business-day-thumb-overlay.tsx","../src/client/components/daily-report-comment-item.tsx","../src/shared/comment-adapter.ts","../src/shared/text-utils.ts","../src/client/ui/cn.ts","../src/client/components/daily-report-comment-section.tsx","../src/client/components/daily-report-detail-list.tsx","../src/client/config-context.tsx","../src/client/contexts/daily-report-action-context.tsx","../src/shared/logger.ts","../src/client/hooks/use-daily-report.ts","../src/shared/business-date.ts","../src/client/hooks/use-daily-report-sse-connection.ts","../src/shared/sse-schema.ts","../src/client/hooks/use-daily-report-comments.ts","../src/client/hooks/use-dynamic-viewport-height.ts","../src/client/utils/constants.ts","../src/client/components/daily-report-edit-form.tsx","../src/client/ui/button.tsx","../src/client/ui/input.tsx","../src/client/ui/label.tsx","../src/client/ui/textarea.tsx","../src/client/components/unread-indicator.tsx","../src/client/components/daily-report-list.tsx","../src/client/ui/tabs.tsx","../src/client/components/daily-report-page.tsx","../src/client/components/daily-report-resolved-content.tsx","../src/client/ui/switch.tsx","../src/client/route-helpers.ts"],"sourcesContent":["/**\n * Client entry of @aiquants/daily-report: React components, hooks, and route helpers.\n * @aiquants/daily-report の client エントリ。React コンポーネント・フック・ルートヘルパーを公開。\n */\nexport * from \"./client/components/business-day-thumb-overlay\"\nexport * from \"./client/components/daily-report-comment-item\"\nexport * from \"./client/components/daily-report-comment-section\"\nexport * from \"./client/components/daily-report-detail-list\"\nexport * from \"./client/components/daily-report-edit-form\"\nexport * from \"./client/components/daily-report-list\"\nexport * from \"./client/components/daily-report-page\"\nexport * from \"./client/components/daily-report-resolved-content\"\nexport * from \"./client/components/unread-indicator\"\nexport * from \"./client/config-context\"\nexport * from \"./client/contexts/daily-report-action-context\"\nexport * from \"./client/hooks/use-daily-report\"\nexport * from \"./client/hooks/use-daily-report-comments\"\nexport * from \"./client/hooks/use-daily-report-sse-connection\"\nexport * from \"./client/hooks/use-dynamic-viewport-height\"\nexport * from \"./client/route-helpers\"\nexport * from \"./client/utils/constants\"\n","import type { ScrollBarThumbOverlayRenderProps } from \"@aiquants/virtualscroll\"\nimport type { CSSProperties } from \"react\"\n\n/**\n * Tooltip bubble that anchors to the scrollbar thumb while interacting.\n * スクロール操作中にスクロールバーのサムへ連動表示するツールチップのコンポーネント。\n */\nexport const BusinessDayThumbOverlay = ({ orientation, thumbCenter, label, isDragging, isTapScrollActive, isWheelScrollActive }: ScrollBarThumbOverlayRenderProps & { label: string; isWheelScrollActive: boolean }) => {\n if (!(label.trim() && (isDragging || isTapScrollActive || isWheelScrollActive))) return null\n const style: CSSProperties = orientation === \"vertical\" ? { top: thumbCenter, left: -16, transform: \"translate(-100%, -50%)\" } : { left: thumbCenter, top: -16, transform: \"translate(-50%, -100%)\" }\n\n return (\n <div className=\"pointer-events-none absolute flex items-center\" style={style}>\n <div className=\"rounded-full border border-slate-200 bg-white px-3 py-1 text-slate-700 text-xs shadow-md shadow-slate-400/30 dark:border-slate-700 dark:bg-slate-900/90 dark:text-slate-100\">\n <span className=\"whitespace-nowrap font-semibold text-slate-800 dark:text-slate-100\">{label.trim()}</span>\n </div>\n </div>\n )\n}\n","import { TrashIcon } from \"@heroicons/react/24/outline\"\nimport { useState } from \"react\"\nimport { resolveCommentColorClass, type UIComment } from \"../../shared/comment-adapter\"\nimport { fallbackText, formatToIsoDate } from \"../../shared/text-utils\"\nimport { cn } from \"../ui/cn\"\n\ntype DailyReportCommentItemProps = {\n comment: UIComment\n reportHubId: number\n businessDate: string\n onOptimisticDelete: (commentId: number) => void\n pendingDeleteId: number | string | null\n onPendingDeleteIdChange: (id: number | string | null) => void\n onTrashClick: (e: React.MouseEvent | React.TouchEvent, commentId: number | string) => void\n resolvedIdMap?: Map<number, number>\n}\n\n/**\n * Checks if the current comment is in a pending delete state, considering temporary ID resolution.\n * 現在のコメントが削除保留状態かどうかを判定する。一時IDの解決も考慮する。\n */\nconst checkIsPendingDelete = (commentId: number | string, pendingDeleteId: number | string | null, resolvedIdMap?: Map<number, number>): boolean => {\n if (pendingDeleteId === commentId) return true\n // Case: commentId is temp, pendingDeleteId is resolved real ID\n if (typeof commentId === \"number\" && commentId < 0 && resolvedIdMap?.get(commentId) === pendingDeleteId) return true\n // Case: pendingDeleteId is temp, commentId is resolved real ID\n if (typeof pendingDeleteId === \"number\" && pendingDeleteId < 0 && resolvedIdMap?.get(pendingDeleteId) === commentId) return true\n return false\n}\n\n/**\n * Renders a single comment item with delete functionality.\n * 削除機能付きの単一コメントアイテムを描画するコンポーネント。\n */\nexport const DailyReportCommentItem = ({ comment, reportHubId: _reportHubId, businessDate: _businessDate, onOptimisticDelete, pendingDeleteId, onPendingDeleteIdChange, onTrashClick, resolvedIdMap }: DailyReportCommentItemProps) => {\n const [isDeleted, setIsDeleted] = useState(false)\n\n const isPendingDelete = checkIsPendingDelete(comment.id, pendingDeleteId, resolvedIdMap)\n\n const handleConfirmDelete = (e: React.MouseEvent | React.TouchEvent) => {\n e.stopPropagation()\n e.preventDefault()\n\n onPendingDeleteIdChange(null)\n setIsDeleted(true)\n\n if (typeof comment.id === \"number\") {\n // Optimistic UI Update (API call is handled by parent hook)\n onOptimisticDelete(comment.id)\n }\n }\n\n return (\n <li data-comment-id={comment.id} className={cn(\"group relative rounded-xl border border-slate-200 bg-white p-3\", comment.isMine && \"border-blue-200 bg-blue-50\", isDeleted && \"hidden\", isPendingDelete ? \"z-50\" : \"z-0 hover:z-10\")}>\n <div className=\"mb-1 flex items-center justify-between\">\n <div className={cn(\"font-semibold text-xs\", resolveCommentColorClass(comment.color))}>{fallbackText(comment.authorName)}</div>\n <div className=\"flex items-center gap-2\">\n {comment.createdAt && <div className=\"text-[10px] text-slate-400\">{formatToIsoDate(comment.createdAt)}</div>}\n {comment.isMine && !comment.isLegacy && (\n <div className=\"relative\">\n <button type=\"button\" className={cn(\"transition-colors\", isPendingDelete ? \"text-red-500\" : \"text-slate-400 hover:text-red-500\")} title=\"削除\" aria-label=\"削除\" onClick={(e) => onTrashClick(e, comment.id)}>\n <TrashIcon className=\"h-4 w-4\" />\n </button>\n\n {isPendingDelete && (\n <button\n type=\"button\"\n data-delete-confirmation\n className=\"fade-in zoom-in-95 absolute top-full right-0 z-20 mt-1 animate-in whitespace-nowrap rounded-md bg-red-600 px-3 py-1.5 font-bold text-white text-xs shadow-lg duration-200 hover:bg-red-700 active:scale-95\"\n onClick={handleConfirmDelete}>\n 削除する\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n <div className=\"whitespace-pre-wrap text-slate-700 text-sm leading-relaxed dark:text-slate-200\">{comment.content}</div>\n </li>\n )\n}\n","/**\n * Adapters merging legacy JSON comments and relational comments for the UI.\n * レガシー JSON コメントとリレーショナルコメントを UI 向けに統合するアダプタ群。\n */\nimport type { DailyReportComment, DailyReportCommentItem } from \"./types\"\n\nexport type UIComment = {\n id: string | number\n authorName: string\n content: string\n createdAt?: string\n isMine: boolean\n color?: string\n isLegacy: boolean\n}\n\n/**\n * Merges legacy JSON comments and modern relational comments into a unified UI format.\n * レガシーな JSON コメントとモダンなリレーショナルコメントを統合された UI 形式にマージする。\n */\nexport const mergeComments = (legacyComments: DailyReportComment[], modernComments: DailyReportCommentItem[], currentUserId?: string | null): UIComment[] => {\n // 1. Legacy コメントの変換\n const legacyUIComments: UIComment[] = legacyComments.map((c, index) => ({\n id: `legacy-${index}`,\n authorName: c.name || \"Unknown\",\n content: c.text || \"\",\n isMine: false,\n color: c.color || undefined,\n isLegacy: true,\n }))\n\n // 2. Modern コメントの変換\n const modernUIComments: UIComment[] = modernComments.map((c) => ({\n id: c.id,\n authorName: c.userName,\n content: c.content,\n createdAt: c.createdAt,\n isMine: currentUserId ? c.userId === currentUserId : c.isMine,\n isLegacy: false,\n }))\n\n // 3. 結合 (Legacy を先に表示)\n return [...legacyUIComments, ...modernUIComments]\n}\n\n/**\n * Resolves comment color tokens into Tailwind text classes.\n * コメント色のキーワードを Tailwind のテキストクラスに変換する関数。\n */\nexport const resolveCommentColorClass = (color: string | null | undefined): string => {\n if (!color) {\n return \"text-gray-700\"\n }\n\n const normalized = color.toLowerCase()\n\n if (normalized.includes(\"red\") || normalized.includes(\"赤\")) {\n return \"text-red-600\"\n }\n\n if (normalized.includes(\"green\") || normalized.includes(\"緑\")) {\n return \"text-green-600\"\n }\n\n if (normalized.includes(\"blue\") || normalized.includes(\"青\")) {\n return \"text-blue-600\"\n }\n\n if (normalized.includes(\"yellow\") || normalized.includes(\"黄\")) {\n return \"text-yellow-600\"\n }\n\n if (normalized.includes(\"orange\") || normalized.includes(\"橙\")) {\n return \"text-orange-500\"\n }\n\n return \"text-gray-700\"\n}\n","/**\n * Shared text helpers for daily report view components.\n * 日報ビューコンポーネント向けの共通テキストヘルパー。\n */\n\n/**\n * Normalizes nullable text by trimming whitespace and applying fallback.\n * null 許容の文字列をトリムしつつフォールバック値を適用する関数。\n */\nexport const fallbackText = (value: string | null | undefined): string => {\n if (!value) {\n return \"-\"\n }\n const trimmed = value.trim()\n return trimmed === \"\" ? \"-\" : trimmed\n}\n\n/**\n * Formats a date string to ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).\n * 日付文字列を ISO 8601 形式 (YYYY-MM-DD または YYYY-MM-DDTHH:mm:ss) に整形する関数。\n */\nexport const formatToIsoDate = (value: unknown): string => {\n if (!value) {\n return \"-\"\n }\n if (typeof value !== \"string\") {\n // If value is not a string (e.g. Date object), try to convert it to string or handle it\n if (value instanceof Date) {\n return formatToIsoDate(value.toISOString())\n }\n return String(value)\n }\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return \"-\"\n }\n\n const date = new Date(trimmed)\n if (Number.isNaN(date.getTime())) {\n return trimmed\n }\n\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, \"0\")\n const day = String(date.getDate()).padStart(2, \"0\")\n\n const hasTime = trimmed.includes(\":\") || trimmed.includes(\"T\")\n\n if (hasTime) {\n const hours = String(date.getHours()).padStart(2, \"0\")\n const minutes = String(date.getMinutes()).padStart(2, \"0\")\n const seconds = String(date.getSeconds()).padStart(2, \"0\")\n return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`\n }\n\n return `${year}-${month}-${day}`\n}\n","/**\n * Tailwind class combiner (clsx + tailwind-merge).\n * Tailwind クラスを結合・重複解決するユーティリティ。\n */\nimport { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\n/**\n * Combines class values and resolves Tailwind conflicts.\n * クラス値を結合し Tailwind の衝突を解決する処理。\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { UIComment } from \"../../shared/comment-adapter\"\nimport { DailyReportCommentItem } from \"./daily-report-comment-item\"\n\n/**\n * Custom hook to manage the auto-reset behavior of the pending delete state.\n * 削除保留状態の自動リセット(タイマーおよび外部クリック)を管理するカスタムフック。\n */\nconst useAutoResetPendingDelete = (pendingDeleteId: number | string | null, onPendingDeleteIdChange: (id: number | string | null) => void) => {\n const timerRef = useRef<number | null>(null)\n\n /**\n * Effect to handle global clicks and reset pending delete state if clicked outside.\n * 外部クリックを監視し、削除確認ボタン以外がクリックされた場合に削除保留状態をリセットする副作用。\n *\n * Purpose: To cancel the delete confirmation mode when the user interacts with other parts of the UI.\n * Dependencies: [pendingDeleteId, onPendingDeleteIdChange] - Re-runs when the pending ID changes.\n * Cleanup: Removes event listeners for mousedown and touchstart.\n */\n useEffect(() => {\n if (pendingDeleteId === null) return\n\n const handleGlobalClick = (e: Event) => {\n const target = e.target as HTMLElement\n // Ignore clicks inside the delete confirmation button\n if (target.closest(\"[data-delete-confirmation]\")) return\n\n onPendingDeleteIdChange(null)\n }\n\n document.addEventListener(\"mousedown\", handleGlobalClick)\n document.addEventListener(\"touchstart\", handleGlobalClick)\n\n return () => {\n document.removeEventListener(\"mousedown\", handleGlobalClick)\n document.removeEventListener(\"touchstart\", handleGlobalClick)\n }\n }, [pendingDeleteId, onPendingDeleteIdChange])\n\n /**\n * Effect to clean up the timer on unmount.\n * アンマウント時にタイマーをクリーンアップする副作用。\n *\n * Purpose: To prevent memory leaks and state updates on unmounted components.\n * Dependencies: [] - Runs only on mount/unmount.\n * Cleanup: Clears the timeout if it exists.\n */\n useEffect(() => {\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current)\n }\n }, [])\n\n const handleTrashClick = useCallback(\n (e: React.MouseEvent | React.TouchEvent, commentId: number | string) => {\n e.stopPropagation()\n e.preventDefault()\n\n if (pendingDeleteId === commentId) return\n\n onPendingDeleteIdChange(commentId)\n\n if (timerRef.current) clearTimeout(timerRef.current)\n // Auto-reset after 5 seconds\n timerRef.current = window.setTimeout(() => {\n onPendingDeleteIdChange(null)\n timerRef.current = null\n }, 5000)\n },\n [pendingDeleteId, onPendingDeleteIdChange],\n )\n\n return { handleTrashClick }\n}\n\ntype DailyReportCommentListProps = {\n comments: UIComment[]\n reportHubId: number\n businessDate: string\n onDeleteComment?: (commentId: number) => void\n pendingDeleteId: number | string | null\n onPendingDeleteIdChange: (id: number | string | null) => void\n resolvedIdMap?: Map<number, number>\n}\n\n/**\n * Renders a list of daily report comments.\n * 日報コメントのリストを描画するコンポーネント。\n */\nexport const DailyReportCommentList = ({ comments, reportHubId, businessDate, onDeleteComment, pendingDeleteId, onPendingDeleteIdChange, resolvedIdMap }: DailyReportCommentListProps) => {\n const { handleTrashClick } = useAutoResetPendingDelete(pendingDeleteId, onPendingDeleteIdChange)\n\n if (comments.length === 0) return null\n\n return (\n <ul className=\"mb-3 space-y-2\">\n {comments.map((comment) => (\n <DailyReportCommentItem\n key={comment.id}\n comment={comment}\n reportHubId={reportHubId}\n businessDate={businessDate}\n onOptimisticDelete={onDeleteComment || (() => {})}\n pendingDeleteId={pendingDeleteId}\n onPendingDeleteIdChange={onPendingDeleteIdChange}\n onTrashClick={handleTrashClick}\n resolvedIdMap={resolvedIdMap}\n />\n ))}\n </ul>\n )\n}\n\ntype DailyReportCommentFormProps = {\n reportHubId: number\n businessDate: string\n onAddComment?: (content: string) => Promise<void> | void\n}\n\n/**\n * Renders a form to add a new comment to a daily report.\n * 日報に新しいコメントを追加するためのフォームを描画するコンポーネント。\n */\nexport const DailyReportCommentForm = ({ reportHubId, businessDate, onAddComment }: DailyReportCommentFormProps) => {\n const [isSubmitting, setIsSubmitting] = useState(false)\n const formRef = useRef<HTMLFormElement>(null)\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault()\n const formData = new FormData(e.currentTarget)\n const content = (formData.get(\"content\") as string)?.trim()\n\n if (!(content && onAddComment)) return\n\n setIsSubmitting(true)\n formRef.current?.reset()\n try {\n await onAddComment(content)\n } catch {\n if (formRef.current) {\n const input = formRef.current.elements.namedItem(\"content\") as HTMLInputElement\n if (input) input.value = content\n }\n } finally {\n setIsSubmitting(false)\n }\n }\n\n return (\n <form className=\"flex gap-2\" ref={formRef} onSubmit={handleSubmit}>\n <input type=\"hidden\" name=\"intent\" value=\"addComment\" />\n <input type=\"hidden\" name=\"reportHubId\" value={reportHubId} />\n <input type=\"hidden\" name=\"businessDate\" value={businessDate} />\n <input\n type=\"text\"\n name=\"content\"\n placeholder=\"コメントを追加...\"\n className=\"flex-1 rounded-lg border border-slate-300 bg-slate-50 px-3 py-2 text-sm focus:border-blue-500 focus:bg-white focus:outline-none dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200\"\n autoComplete=\"off\"\n />\n <button type=\"submit\" className=\"rounded-lg bg-blue-600 px-4 py-2 font-medium text-sm text-white hover:bg-blue-700 disabled:opacity-50\" disabled={isSubmitting}>\n 送信\n </button>\n </form>\n )\n}\n","/**\n * Virtual-scroll based detailed daily report view backed by fetcher-driven data hooks.\n * useFetcher ベースのデータフックで日報詳細を提供する仮想スクロールビュー。\n */\n\nimport { type ScrollBarTapCircleOptions, type ScrollBarThumbOverlayRenderProps, tapScrollCircleSampleVisual, VirtualScroll, type VirtualScrollHandle } from \"@aiquants/virtualscroll\"\n\n/**\n * Handle type augmented with E2E test helper methods.\n * E2E テスト用ヘルパーメソッドで拡張されたハンドル型。\n */\ntype DailyReportVirtualScrollHandle = VirtualScrollHandle & {\n findReportIndex: (id: number) => number\n getReportItem: (index: number) => DailyReportItem | undefined\n selectItem: (id: number) => void\n}\n\nimport { Check, Star, Trash2 } from \"lucide-react\"\nimport { type RefObject, useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { fallbackText, formatToIsoDate } from \"../../shared/text-utils\"\nimport type { DailyReportDetail, DailyReportItem } from \"../../shared/types\"\nimport { type DailyReportFieldLabels, type SourceTypeConfig, useDailyReportConfig } from \"../config-context\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { useDailyReportDetail, useDailyReportPrefetch } from \"../hooks/use-daily-report\"\nimport { useDailyReportComments } from \"../hooks/use-daily-report-comments\"\nimport { useDynamicViewportHeight } from \"../hooks/use-dynamic-viewport-height\"\nimport { cn } from \"../ui/cn\"\nimport { EMPTY_ITEM, FAST_SCROLL_INERTIA_OPTIONS, FAST_SCROLL_WHEEL_MULTIPLIER, PREFETCH_LOOKAHEAD, PREFETCH_MAX_DATES, VIRTUAL_SCROLL_OVERSCAN_COUNT } from \"../utils/constants\"\nimport { BusinessDayThumbOverlay } from \"./business-day-thumb-overlay\"\nimport { DailyReportCommentForm, DailyReportCommentList } from \"./daily-report-comment-section\"\nimport { DailyReportEditForm } from \"./daily-report-edit-form\"\nimport { UnreadIndicator } from \"./unread-indicator\"\n\n/**\n * Observes container height changes and reports measured values upstream.\n * コンテナの高さ変化を監視し測定結果を親へ通知するフック。\n */\nconst useReportItemHeightObserver = (ref: RefObject<HTMLDivElement | null>, itemId: number, onHeightMeasured: (id: number, height: number) => void) => {\n useEffect(() => {\n const element = ref.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver(() => {\n onHeightMeasured(itemId, element.offsetHeight)\n })\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [itemId, onHeightMeasured, ref])\n}\n\n/**\n * Wraps a daily report item row, observing its height and providing a stable test identifier.\n * 日報アイテム行をラップし、高さを監視するとともに安定したテスト識別子を提供するコンポーネント。\n */\nconst DailyReportDetailRow = ({ itemId, onHeightMeasured, children }: { itemId: number; onHeightMeasured: (id: number, height: number) => void; children: React.ReactNode }) => {\n const elementRef = useRef<HTMLDivElement | null>(null)\n useReportItemHeightObserver(elementRef, itemId, onHeightMeasured)\n return (\n <div ref={elementRef} className=\"px-2 pb-3\" data-daily-report-button={itemId}>\n {children}\n </div>\n )\n}\n\n/**\n * Formats interviewer labels with optional affiliation.\n * 面談者情報を所属付きのラベル文字列に整形する関数。\n */\nconst formatInterviewerLabel = (interviewer: DailyReportDetail[\"interviewers\"][number]): string => {\n if (interviewer.affiliation?.trim()) {\n return `${interviewer.name}(${interviewer.affiliation})`\n }\n return interviewer.name\n}\n\ntype DailyReportDetailListProps = {\n dailyReportItems: DailyReportItem[]\n userId?: string | null\n selectedItemId?: number | null\n onSelectItem?: (id: number | null) => void\n}\n\n/**\n * Daily report detail cards rendered with dynamic height virtual scroll.\n * 動的高さの仮想スクロールで日報詳細カードを描画するコンポーネント。\n */\nexport const DailyReportDetailList = ({ dailyReportItems, userId, selectedItemId, onSelectItem }: DailyReportDetailListProps) => {\n const [itemHeights, setItemHeights] = useState<Map<number, number>>(new Map())\n const scrollContainerRef = useRef<HTMLDivElement | null>(null)\n const virtualScrollRef = useRef<VirtualScrollHandle>(null)\n const { viewportHeight, updateViewportHeight } = useDynamicViewportHeight(scrollContainerRef, { enableMutationObserver: true })\n const prefetchDailyReportByDate = useDailyReportPrefetch()\n const [overlayLabel, setOverlayLabel] = useState<string>(\"\")\n const [isWheelScrollActive, setIsWheelScrollActive] = useState(false)\n const wheelResetTimerRef = useRef<number | null>(null)\n const scrollEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const dailyReportItemsRef = useRef(dailyReportItems)\n dailyReportItemsRef.current = dailyReportItems\n\n // 同一 selectedItemId かつ同一 index への重複スクロールを防止する ref\n const lastScrolledRef = useRef<{ id: number; index: number } | null>(null)\n\n useEffect(() => {\n if (selectedItemId != null && virtualScrollRef.current) {\n const index = dailyReportItems.findIndex((item) => item.reportHubId === selectedItemId)\n if (index !== -1) {\n // ID もインデックスも同じなら再スクロール不要\n if (lastScrolledRef.current?.id === selectedItemId && lastScrolledRef.current?.index === index) return\n virtualScrollRef.current.scrollToIndex(index, { align: \"top\" })\n lastScrolledRef.current = { id: selectedItemId, index }\n }\n } else {\n lastScrolledRef.current = null\n }\n }, [selectedItemId, dailyReportItems])\n\n // onSelectItem の最新参照を保持する ref (useEffect 依存配列を不要にする)\n const onSelectItemRef = useRef(onSelectItem)\n onSelectItemRef.current = onSelectItem\n\n useEffect(() => {\n if (virtualScrollRef.current) {\n const el = document.querySelector(\".test-daily-report-detail-list\")\n if (el) {\n const handle = virtualScrollRef.current\n const extendedHandle: DailyReportVirtualScrollHandle = Object.assign({}, handle, {\n findReportIndex: (id: number) => dailyReportItemsRef.current.findIndex((item) => item.reportHubId === id),\n getReportItem: (index: number) => dailyReportItemsRef.current[index],\n selectItem: (id: number) => onSelectItemRef.current?.(id),\n })\n ;(el as Element & { __virtualScroll: DailyReportVirtualScrollHandle }).__virtualScroll = extendedHandle\n }\n }\n })\n\n const itemCount = dailyReportItems.length\n\n const getItem = useCallback(\n (index: number): DailyReportItem => {\n if (index >= itemCount) return EMPTY_ITEM\n return dailyReportItems[index]\n },\n [dailyReportItems, itemCount],\n )\n\n const schedulePrefetchForRange = useCallback(\n (fromIndex: number, toIndex: number) => {\n if (itemCount === 0) return\n const boundedFrom = Math.max(0, fromIndex)\n const boundedTo = Math.min(itemCount - 1, toIndex)\n if (boundedFrom > boundedTo) return\n\n const businessDates = new Set<string>()\n for (let index = boundedFrom; index <= boundedTo; index++) {\n const candidate = getItem(index)\n if (candidate.businessDate) businessDates.add(candidate.businessDate)\n if (businessDates.size >= PREFETCH_MAX_DATES) break\n }\n businessDates.forEach((date) => {\n prefetchDailyReportByDate(date)\n })\n },\n [getItem, itemCount, prefetchDailyReportByDate],\n )\n\n /**\n * Handles visible range changes to update overlay label and schedule prefetching.\n * 表示範囲の変更を処理し、オーバーレイラベルの更新とプリフェッチのスケジュールを行う。\n */\n const handleRangeChange = useCallback(\n ({ renderingStartIndex, renderingEndIndex, visibleStartIndex }: { renderingStartIndex: number; renderingEndIndex: number; visibleStartIndex: number }) => {\n if (itemCount === 0) return\n const firstVisibleItem = getItem(visibleStartIndex)\n if (firstVisibleItem?.businessDate) {\n setOverlayLabel(formatToIsoDate(firstVisibleItem.businessDate))\n }\n schedulePrefetchForRange(renderingStartIndex, renderingEndIndex)\n schedulePrefetchForRange(renderingEndIndex + 1, renderingEndIndex + PREFETCH_LOOKAHEAD)\n schedulePrefetchForRange(renderingStartIndex - PREFETCH_LOOKAHEAD, renderingStartIndex - 1)\n },\n [getItem, itemCount, schedulePrefetchForRange],\n )\n\n const handleScroll = useCallback(() => {\n const container = scrollContainerRef.current\n if (!container) return\n\n if (!container.classList.contains(\"is-scrolling\")) {\n container.classList.add(\"is-scrolling\")\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n container.classList.remove(\"is-scrolling\")\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [])\n\n const renderThumbOverlay = useCallback((props: ScrollBarThumbOverlayRenderProps) => <BusinessDayThumbOverlay {...props} label={overlayLabel} isWheelScrollActive={isWheelScrollActive} />, [overlayLabel, isWheelScrollActive])\n\n /**\n * Sets up wheel event listener to toggle overlay visibility.\n * ホイールイベントリスナーを設定し、オーバーレイの表示状態を切り替える。\n */\n useEffect(() => {\n const el = scrollContainerRef.current\n if (!el) return\n const onWheel = () => {\n setIsWheelScrollActive(true)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n wheelResetTimerRef.current = window.setTimeout(() => {\n setIsWheelScrollActive(false)\n wheelResetTimerRef.current = null\n }, 240)\n }\n el.addEventListener(\"wheel\", onWheel, { passive: true })\n return () => {\n el.removeEventListener(\"wheel\", onWheel)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n }\n }, [])\n\n const updateItemHeight = useCallback((id: number, height: number) => {\n setItemHeights((prev) => {\n if (prev.get(id) === height) return prev\n const newMap = new Map(prev)\n newMap.set(id, height)\n return newMap\n })\n if (virtualScrollRef.current) {\n const index = dailyReportItemsRef.current.findIndex((item) => item.reportHubId === id)\n if (index !== -1) {\n virtualScrollRef.current.updateItemSize(index, height)\n }\n }\n }, [])\n\n const getItemHeight = useCallback(\n (index: number) => {\n const item = getItem(index)\n return itemHeights.get(item.reportHubId) || 350\n },\n [getItem, itemHeights],\n )\n\n const getItemKey = useCallback(\n (index: number) => {\n const item = getItem(index)\n return item.reportHubId\n },\n [getItem],\n )\n\n const tapScrollCircleOptions = useMemo<ScrollBarTapCircleOptions>(() => ({ enabled: true, renderVisual: tapScrollCircleSampleVisual }), [])\n\n /**\n * Updates viewport height when item count changes.\n * アイテム数変更時にビューポート高さを更新する。\n */\n useEffect(() => {\n if (itemCount >= 0) updateViewportHeight()\n }, [itemCount, updateViewportHeight])\n\n /**\n * Schedules initial prefetch when items are loaded.\n * アイテム読み込み時に初期プリフェッチをスケジュールする。\n */\n useEffect(() => {\n if (itemCount === 0) return\n schedulePrefetchForRange(0, Math.min(itemCount - 1, PREFETCH_LOOKAHEAD))\n }, [itemCount, schedulePrefetchForRange])\n\n const handleCommentAdded = useCallback((itemId: number) => {\n const index = dailyReportItemsRef.current.findIndex((item) => item.reportHubId === itemId)\n if (index >= 0) {\n setTimeout(() => virtualScrollRef.current?.scrollToIndex(index, { align: \"bottom\" }), 300)\n }\n }, [])\n\n const renderItem = useCallback((item: DailyReportItem) => <DailyReportItemContainer key={item.reportHubId} item={item} onHeightMeasured={updateItemHeight} onCommentAdded={handleCommentAdded} userId={userId} />, [updateItemHeight, handleCommentAdded, userId])\n\n return (\n <>\n <div ref={scrollContainerRef} className=\"flex min-h-0 flex-1 flex-col\">\n <VirtualScroll\n ref={virtualScrollRef}\n itemCount={itemCount}\n getItem={getItem}\n getItemKey={getItemKey}\n getItemHeight={getItemHeight}\n viewportSize={viewportHeight}\n overscanCount={VIRTUAL_SCROLL_OVERSCAN_COUNT}\n scrollBarOptions={{\n width: 8,\n tapScrollCircleOptions: tapScrollCircleOptions,\n renderThumbOverlay: renderThumbOverlay,\n }}\n behaviorOptions={{\n wheelSpeedMultiplier: FAST_SCROLL_WHEEL_MULTIPLIER,\n inertiaOptions: FAST_SCROLL_INERTIA_OPTIONS,\n }}\n onRangeChange={handleRangeChange}\n onScroll={handleScroll}\n className=\"t-detail-list test-daily-report-detail-list relative w-full\">\n {renderItem}\n </VirtualScroll>\n </div>\n {itemCount === 0 && (\n <div className=\"py-8 text-center\">\n <p className=\"text-slate-500\">データがありません</p>\n </div>\n )}\n </>\n )\n}\n\n/**\n * Loading skeleton for the virtual scroll detail entry.\n * 仮想スクロール詳細行の読み込み中スケルトン表示。\n */\nconst SkeletonReportItem = () => (\n <div className=\"rounded-2xl border border-slate-200 bg-white p-4 shadow-sm\">\n <div className=\"mb-4 flex flex-wrap gap-2\">\n <div className=\"h-6 w-20 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-24 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-24 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-28 rounded-full bg-slate-200\"></div>\n </div>\n <div className=\"mb-3 grid gap-3 md:grid-cols-[minmax(0,1fr)_220px]\">\n <div className=\"space-y-3\">\n <div className=\"h-5 w-40 rounded bg-slate-200\"></div>\n <div className=\"h-5 w-56 rounded bg-slate-200\"></div>\n <div className=\"h-24 rounded-lg bg-slate-200\"></div>\n </div>\n <div className=\"rounded-xl bg-slate-100 p-3\">\n <div className=\"mb-2 h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"mb-2 h-4 w-28 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-32 rounded bg-slate-200\"></div>\n </div>\n </div>\n <div className=\"space-y-3\">\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"flex flex-wrap gap-2\">\n <div className=\"h-6 w-16 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-16 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-16 rounded-full bg-slate-200\"></div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-16 rounded-lg bg-slate-200\"></div>\n <div className=\"h-16 rounded-lg bg-slate-200\"></div>\n </div>\n </div>\n </div>\n)\n\n/**\n * Chooses the appropriate daily report item representation based on fetch state.\n * フェッチ状態に応じて日報アイテムの表示コンポーネントを選択する関数。\n */\nconst DailyReportItemContainer = ({ item, onHeightMeasured, onCommentAdded, userId }: { item: DailyReportItem; onHeightMeasured: (id: number, height: number) => void; onCommentAdded?: (itemId: number) => void; userId?: string | null }) => {\n const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)\n const { removeStaleItem } = useDailyReportActionContext()\n const { draftLabelName } = useDailyReportConfig()\n const [isEditing, setIsEditing] = useState(false)\n const [hasBypassedAutoEdit, setHasBypassedAutoEdit] = useState(false)\n // 自動編集モード (ドラフト検出による自動遷移) かどうかを追跡\n const autoEditingRef = useRef(false)\n\n // フェッチが一度でも試行されたかを追跡する ref\n const hasEverFetched = useRef(false)\n if (isRefetching) hasEverFetched.current = true\n\n // フェッチ完了後に report が null → stale アイテムとしてリストから除去する安全弁\n // 外部リソース同期: useDailyReportDetail のフェッチ結果に反応する\n useEffect(() => {\n if (!(report || isLoading || isRefetching || error) && hasEverFetched.current && item.reportHubId > 0) {\n removeStaleItem(item.reportHubId)\n }\n }, [report, isLoading, isRefetching, error, item.reportHubId, removeStaleItem])\n\n const isDraft = report?.labels.some((l) => l.name === draftLabelName)\n const isMine = userId && report?.userId === userId\n\n // ドラフトレポートの自動編集モード制御\n // - ドラフト状態かつ自分のレポート: 自動的にエディタモード\n // - SSE でドラフト→公開に遷移: 自動編集モードの場合のみビューモードへ解除\n useEffect(() => {\n if (report && isMine && isDraft && !isEditing && !hasBypassedAutoEdit) {\n setIsEditing(true)\n autoEditingRef.current = true\n } else if (report && !isDraft && isEditing && autoEditingRef.current) {\n // SSE で下書き→公開に遷移: 自動編集モードの場合のみビューモードへ\n setIsEditing(false)\n autoEditingRef.current = false\n }\n }, [report, isMine, isDraft, isEditing, hasBypassedAutoEdit])\n\n return (\n <DailyReportDetailRow itemId={item.reportHubId} onHeightMeasured={onHeightMeasured}>\n {error ? (\n <DailyReportError itemId={item.reportHubId} />\n ) : !report ? (\n <SkeletonReportItem />\n ) : isEditing ? (\n <DailyReportEditForm\n report={report}\n onCancel={() => {\n setIsEditing(false)\n setHasBypassedAutoEdit(true)\n }}\n onPublishSuccess={() => {\n setIsEditing(false)\n }}\n />\n ) : (\n <DailyReportResolvedItem item={item} report={report} onCommentAdded={onCommentAdded} userId={userId} onDoubleClick={isMine ? () => setIsEditing(true) : undefined} />\n )}\n </DailyReportDetailRow>\n )\n}\n\n/**\n * Renders the header section of a daily report.\n * 日報のヘッダー部分を描画するコンポーネント。\n */\nconst DailyReportHeader = ({\n report,\n isRead,\n isStarred,\n isMine,\n onToggleRead,\n onToggleStar,\n onDelete,\n visitTimeDisplay,\n categoryLabel,\n fieldLabels,\n sourceTypeConfigs,\n}: {\n report: DailyReportDetail\n isRead: boolean\n isStarred: boolean\n isMine: boolean\n onToggleRead: () => void\n onToggleStar: () => void\n onDelete: () => void\n visitTimeDisplay: string\n categoryLabel: string\n fieldLabels: DailyReportFieldLabels\n sourceTypeConfigs?: Record<string, SourceTypeConfig>\n}) => (\n <div className=\"flex flex-wrap items-center gap-2 font-medium text-slate-600 text-xs\">\n {(() => {\n const sourceType = report.sourceType\n const sourceConfig = sourceType ? sourceTypeConfigs?.[sourceType] : undefined\n return sourceConfig ? <span className={cn(\"inline-flex items-center rounded px-2 py-0.5 font-semibold text-[10px] tracking-wide\", sourceConfig.badgeClassName)}>{sourceConfig.label}</span> : null\n })()}\n <button\n type=\"button\"\n onClick={onToggleStar}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 focus:outline-none dark:hover:bg-slate-700\"\n title={isStarred ? \"スターを外す\" : \"スターを付ける\"}\n data-detail-star-button={report.reportHubId}>\n {isStarred ? <Star className=\"h-3.5 w-3.5 fill-yellow-400 text-yellow-400\" /> : <Star className=\"h-3.5 w-3.5 text-slate-300 dark:text-slate-500\" />}\n </button>\n <button\n type=\"button\"\n onClick={onToggleRead}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 focus:outline-none dark:hover:bg-slate-700\"\n title={isRead ? \"未読にする\" : \"既読にする\"}\n data-detail-read-button={report.reportHubId}>\n {isRead ? <Check className=\"h-3.5 w-3.5 text-blue-500\" /> : <UnreadIndicator className=\"text-xs\" />}\n </button>\n {isMine && (\n <button type=\"button\" onClick={onDelete} data-detail-delete-button={report.reportHubId} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-125\" title=\"削除する\">\n <Trash2 className=\"h-4 w-4 text-slate-300 hover:text-red-500\" />\n </button>\n )}\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-blue-50 px-3 py-1 font-semibold text-blue-700 text-xs\">\n <span className=\"text-[10px] text-blue-500 uppercase tracking-wide\">{fieldLabels.id}</span>\n {report.reportHubId}\n </span>\n\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.businessDate}</span>\n {formatToIsoDate(report.date)}\n </span>\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.author}</span>\n {fallbackText(report.employeeName)}\n </span>\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.visitTime}</span>\n {visitTimeDisplay || \"-\"}\n </span>\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.updatedAt}</span>\n {formatToIsoDate(report.updatedAt)}\n </span>\n {categoryLabel && (\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-amber-100 px-3 py-1 text-amber-700\">\n <span className=\"text-[10px] text-amber-500 uppercase tracking-wide\">{fieldLabels.category}</span>\n {categoryLabel}\n </span>\n )}\n </div>\n)\n\n/**\n * Renders the side panel with detailed metadata.\n * 詳細なメタデータを表示するサイドパネルを描画するコンポーネント。\n */\nconst MetadataPanel = ({ report, visitTimeDisplay, categoryLabel, fieldLabels }: { report: DailyReportDetail; visitTimeDisplay: string; categoryLabel: string; fieldLabels: DailyReportFieldLabels }) => (\n <div className=\"flex flex-col gap-3 rounded-xl bg-slate-100 p-4 text-slate-600 text-xs\">\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.author}</span>\n <span className=\"text-slate-700 text-sm\">{fallbackText(report.author)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.createdAt}</span>\n <span className=\"text-slate-700 text-sm\">{formatToIsoDate(report.createdAt)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.updatedBy}</span>\n <span className=\"text-slate-700 text-sm\">{fallbackText(report.updatedBy)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.updatedAt}</span>\n <span className=\"text-slate-700 text-sm\">{formatToIsoDate(report.updatedAt)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.businessDate}</span>\n <span className=\"text-slate-700 text-sm\">{formatToIsoDate(report.date)}</span>\n </div>\n {visitTimeDisplay && (\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.visitTime}</span>\n <span className=\"text-slate-700 text-sm\">{visitTimeDisplay}</span>\n </div>\n )}\n {categoryLabel && (\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.categoryInfo}</span>\n <span className=\"text-slate-700 text-sm\">{categoryLabel}</span>\n </div>\n )}\n </div>\n)\n\n/**\n * Renders the resolved daily report detail card with metadata and content.\n * 解決済みの日報詳細カードをメタデータと内容込みで描画する関数。\n */\nconst DailyReportResolvedItem = ({ item, report, onCommentAdded, userId, onDoubleClick }: { item: DailyReportItem; report: DailyReportDetail; onCommentAdded?: (itemId: number) => void; userId?: string | null; onDoubleClick?: () => void }) => {\n const formContainerRef = useRef<HTMLDivElement>(null)\n // 表示中ユーザーはアクションコンテキストから取得する (root loader 非依存)\n const { toggleStar, toggleRead, pendingStarUpdates, pendingReadUpdates, deleteReport, user } = useDailyReportActionContext()\n const { uiComments, handleAddComment, handleDeleteComment, pendingDeleteId, setPendingDeleteId, resolvedIdMap } = useDailyReportComments(report, user, userId)\n const { fieldLabels, sourceTypeConfigs } = useDailyReportConfig()\n\n const handleAddCommentWrapper = async (content: string) => {\n await handleAddComment(content)\n onCommentAdded?.(item.reportHubId)\n }\n\n const isStarred = pendingStarUpdates.current.has(report.reportHubId) ? (pendingStarUpdates.current.get(report.reportHubId) ?? false) : report.isStarred\n const isRead = pendingReadUpdates.current.has(report.reportHubId) ? (pendingReadUpdates.current.get(report.reportHubId) ?? false) : report.isRead\n const isMine = !!(userId && report.userId === userId)\n\n const handleToggleRead = async () => {\n try {\n await toggleRead(report.reportHubId, !isRead, report.date ?? \"\")\n } catch {}\n }\n\n const handleToggleStar = async () => {\n try {\n await toggleStar(report.reportHubId, !isStarred, report.date ?? \"\")\n } catch {}\n }\n\n const handleDelete = async () => {\n if (!confirm(\"本当に削除しますか?\")) return\n await deleteReport(report.reportHubId, report.date ?? \"\")\n }\n\n const normalizedContent = useMemo(() => (report.content ? report.content.replace(/<br\\s*\\/??\\s*>/gi, \"\\n\").trim() : \"\"), [report.content])\n const visitTimeDisplay = useMemo(() => {\n const from = report.visitTimeFrom?.trim()\n const to = report.visitTimeTo?.trim()\n return from && to ? `${from} - ${to}` : from || to || \"\"\n }, [report.visitTimeFrom, report.visitTimeTo])\n const categoryLabel = useMemo(() => {\n const labels: string[] = []\n if (report.category?.trim()) labels.push(`${fieldLabels.category}: ${report.category}`)\n if (report.creationCategory?.trim()) labels.push(`${fieldLabels.creationCategory}: ${report.creationCategory}`)\n return labels.join(\" / \")\n }, [report.category, report.creationCategory, fieldLabels.category, fieldLabels.creationCategory])\n\n return (\n // biome-ignore lint/a11y/noStaticElementInteractions: Double click to edit feature\n <div\n onDoubleClick={onDoubleClick}\n className={`flex h-full w-full flex-col rounded-2xl border bg-white p-4 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:border-blue-300 hover:shadow-lg dark:border-slate-700 dark:bg-slate-900/70 ${!isRead ? \"border-l-4 border-l-blue-500\" : \"\"}`}>\n <div className=\"flex flex-col gap-4\">\n <DailyReportHeader\n report={report}\n isRead={isRead}\n isStarred={isStarred}\n isMine={isMine}\n onToggleRead={handleToggleRead}\n onToggleStar={handleToggleStar}\n onDelete={handleDelete}\n visitTimeDisplay={visitTimeDisplay}\n categoryLabel={categoryLabel}\n fieldLabels={fieldLabels}\n sourceTypeConfigs={sourceTypeConfigs}\n />\n <div className=\"grid gap-4 md:grid-cols-[minmax(0,1fr)_240px]\">\n <div className=\"flex flex-col gap-4\">\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.customer}</span>\n <span className=\"font-semibold text-base text-slate-900\">{fallbackText(report.customerName)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.subject}</span>\n <span className=\"text-slate-700 text-sm\">{fallbackText(report.subject)}</span>\n </div>\n </div>\n {normalizedContent && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.content}</span>\n <div className=\"whitespace-pre-wrap rounded-xl bg-slate-50 p-3 text-slate-700 text-sm leading-relaxed\">{normalizedContent}</div>\n </div>\n )}\n {report.interviewers.length > 0 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.interviewers}</span>\n <ul className=\"flex flex-wrap gap-2\">\n {report.interviewers.map((interviewer) => (\n <li key={`${interviewer.name}-${interviewer.affiliation || \"\"}`} className=\"rounded-full bg-slate-100 px-3 py-1 text-slate-600 text-xs\">\n {formatInterviewerLabel(interviewer)}\n </li>\n ))}\n </ul>\n </div>\n )}\n {uiComments.length > 0 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.comments}</span>\n <DailyReportCommentList\n comments={uiComments}\n reportHubId={report.reportHubId}\n businessDate={report.date ?? \"\"}\n onDeleteComment={handleDeleteComment}\n pendingDeleteId={pendingDeleteId}\n onPendingDeleteIdChange={setPendingDeleteId}\n resolvedIdMap={resolvedIdMap}\n />\n </div>\n )}\n <div className=\"mt-auto\" ref={formContainerRef}>\n <DailyReportCommentForm reportHubId={report.reportHubId} businessDate={report.date ?? \"\"} onAddComment={handleAddCommentWrapper} />\n </div>\n </div>\n <MetadataPanel report={report} visitTimeDisplay={visitTimeDisplay} categoryLabel={categoryLabel} fieldLabels={fieldLabels} />\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Displays an error banner when daily report detail retrieval fails.\n * 日報詳細の取得に失敗した際のエラーバナーを表示する関数。\n */\nconst DailyReportError = ({ itemId }: { itemId: number }) => (\n <div className=\"rounded-2xl border border-red-200 bg-red-50 p-4 text-red-700 text-sm\">\n <p>日報 {itemId.toLocaleString()} の読み込みに失敗しました。</p>\n </div>\n)\n","/**\n * Client-side DI configuration for the daily-report UI (paths, chrome slots, labels).\n * 日報 UI のクライアント側 DI 設定 (パス・クローム差し込み・ラベル名) を提供するコンテキスト。\n */\nimport { createContext, type ReactNode, useContext, useMemo } from \"react\"\n\n/** ヘッダー描画スロットに渡される引数。 */\nexport type DailyReportHeaderProps = {\n title: string\n annotation?: ReactNode\n}\n\n/**\n * Display labels for the report detail/list fields, sections, and side-pane tabs.\n * 日報の詳細/一覧カードのフィールド・セクション・タブ見出しの表示ラベル群。\n *\n * パッケージは中立な英語ラベルのみを既定として同梱し、ドメイン固有の表現 (言語・業務語彙) は\n * 消費アプリが `fieldLabels` で注入する。これによりパッケージ本体が業務ドメイン知識を持たない。\n */\nexport type DailyReportFieldLabels = {\n id: string\n businessDate: string\n author: string\n visitTime: string\n createdAt: string\n updatedAt: string\n updatedBy: string\n category: string\n categoryInfo: string\n creationCategory: string\n customer: string\n subject: string\n content: string\n interviewers: string\n comments: string\n tabArticle: string\n tabRelations: string\n relationsEmpty: string\n}\n\nexport type SourceTypeConfig = {\n label: string\n badgeClassName?: string\n}\n\n/** クライアント層の設定一式。 */\nexport type DailyReportClientConfig = {\n /** 画面タイトル (ヘッダーへ渡す)。 */\n title: string\n /** API ベースパス (例 `/daily_report/api`)。末尾スラッシュ無し。 */\n apiBasePath: string\n /** SSE エンドポイントパス (例 `/sse/daily_report/updates`)。 */\n ssePath: string\n /** 下書き状態を表すラベル名。UI の編集モード判定に使う。既定はプレースホルダーなので消費側で自アプリのラベル名へ上書きする。 */\n draftLabelName: string\n /** 詳細/一覧カードのフィールド見出しラベル。既定は中立英語。アプリが自ドメインの表記を注入する。 */\n fieldLabels: DailyReportFieldLabels\n /** 外部ソース区分ごとのバッジ表示設定。 */\n sourceTypeConfigs?: Record<string, SourceTypeConfig>\n /** ヘッダー描画スロット。アプリのヘッダーコンポーネントを差し込む。 */\n renderHeader: (props: DailyReportHeaderProps) => ReactNode\n /** 開発向けコントロール (SSE 購読トグル等) を表示するか。 */\n showDevControls: boolean\n}\n\n/**\n * Partial client-config override. `fieldLabels` may itself be partially overridden.\n * クライアント設定の部分上書き型。`fieldLabels` も部分上書き可 (プロバイダーが深くマージする)。\n */\nexport type DailyReportClientConfigInput = Partial<Omit<DailyReportClientConfig, \"fieldLabels\">> & {\n fieldLabels?: Partial<DailyReportFieldLabels>\n sourceTypeConfigs?: Record<string, SourceTypeConfig>\n}\n\n/** 中立英語のフィールドラベル既定 (ドメイン非依存)。 */\nexport const defaultDailyReportFieldLabels: DailyReportFieldLabels = {\n id: \"ID\",\n businessDate: \"Business date\",\n author: \"Author\",\n visitTime: \"Visit time\",\n createdAt: \"Created at\",\n updatedAt: \"Updated at\",\n updatedBy: \"Updated by\",\n category: \"Category\",\n categoryInfo: \"Category\",\n creationCategory: \"Creation category\",\n customer: \"Customer\",\n subject: \"Subject\",\n content: \"Content\",\n interviewers: \"Attendees\",\n comments: \"Comments\",\n tabArticle: \"Overview\",\n tabRelations: \"Related\",\n relationsEmpty: \"No related view configured yet.\",\n}\n\n/** 既定設定 (消費側は必要なフィールドのみ部分上書きする)。 */\nexport const defaultDailyReportClientConfig: DailyReportClientConfig = {\n title: \"Daily Reports\",\n apiBasePath: \"/daily_report/api\",\n ssePath: \"/sse/daily_report/updates\",\n // 中立プレースホルダー。実運用のラベル名 (アプリ固有) は Provider / config prop で上書きする。\n draftLabelName: \"draft\",\n fieldLabels: defaultDailyReportFieldLabels,\n sourceTypeConfigs: {},\n renderHeader: ({ title, annotation }) => (\n <header className=\"flex flex-wrap items-baseline justify-between gap-2 px-2 py-1\">\n <span className=\"text-nowrap text-slate-700 text-sm dark:text-slate-200\">{title}</span>\n {annotation ? <span className=\"text-slate-600 text-xs dark:text-slate-300\">{annotation}</span> : null}\n </header>\n ),\n showDevControls: false,\n}\n\nconst DailyReportConfigContext = createContext<DailyReportClientConfig>(defaultDailyReportClientConfig)\n\n/**\n * Provides a partial override of the daily-report client configuration.\n * 日報クライアント設定の部分上書きを提供するプロバイダー。\n */\nexport const DailyReportConfigProvider = ({ config, children }: { config?: DailyReportClientConfigInput; children: ReactNode }) => {\n // 部分設定を既定値へ浅くマージする (fieldLabels のみ 1 段深くマージして部分上書きを許す)\n const merged = useMemo<DailyReportClientConfig>(\n () => ({\n ...defaultDailyReportClientConfig,\n ...config,\n fieldLabels: { ...defaultDailyReportFieldLabels, ...(config?.fieldLabels ?? {}) },\n }),\n [config],\n )\n return <DailyReportConfigContext.Provider value={merged}>{children}</DailyReportConfigContext.Provider>\n}\n\n/**\n * Reads the daily-report client configuration (defaults when no provider).\n * 日報クライアント設定を読む (プロバイダー未設置時は既定値)。\n */\nexport const useDailyReportConfig = (): DailyReportClientConfig => useContext(DailyReportConfigContext)\n","/**\n * Action context providing optimistic CRUD + SSE synchronization for daily reports.\n * 日報の楽観的 CRUD と SSE 同期を提供するアクションコンテキスト。\n */\nimport { type Context, createContext, type ReactNode, useCallback, useContext, useEffect, useRef, useState } from \"react\"\nimport { createLogger, LogLevel } from \"../../shared/logger\"\nimport type { DailyReportSseMessage } from \"../../shared/sse-schema\"\nimport type { DailyReportCommentItem, DailyReportDetail, DailyReportItem, DailyReportUser } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\nimport { acquireMutationLock, applyDailyReportServerUpdates, deleteDailyReportCache, getCachedReport, registerRecentDeletion, releaseMutationLock, subscribeCacheReady, updateDailyReportCache, writeCache } from \"../hooks/use-daily-report\"\nimport { useDailyReportSseConnection } from \"../hooks/use-daily-report-sse-connection\"\n\nconst logger = createLogger(LogLevel.INFO, \"daily-report-action-context\")\n\ntype ActionResponse = {\n status: string\n reportHubId: number\n newComment?: DailyReportCommentItem\n updatedStatus?: { isStarred?: boolean; isRead?: boolean }\n deletedCommentId?: string\n updatedAt?: string // ISO string from server\n report?: DailyReportDetail // Full report for creation/updates\n clientTempId?: string // Validation token for optimistic updates\n [key: string]: unknown\n}\n\ntype DailyReportActionContextType = {\n /** 表示中ユーザー (楽観的コメントの作者名等に使用)。 */\n user?: DailyReportUser\n /** reportHubId -> Map<tempCommentId, content> (追加コメントの一時IDと内容) */\n pendingAddCommentIds: React.MutableRefObject<Map<number, Map<number, string>>>\n /** reportHubId -> Set<tempCommentId> (削除された一時ID) */\n pendingDeleteCommentIds: React.MutableRefObject<Map<number, Set<number>>>\n /** reportHubId -> boolean (スターの保留状態) */\n pendingStarUpdates: React.MutableRefObject<Map<number, boolean>>\n /** reportHubId -> boolean (既読の保留状態) */\n pendingReadUpdates: React.MutableRefObject<Map<number, boolean>>\n /** tempId -> realId (解決されたID) */\n resolvedIdMap: React.MutableRefObject<Map<number, number>>\n /** 状態変更通知用バージョン */\n version: number\n /** 日報リスト */\n items: DailyReportItem[]\n /** SSE購読が有効かどうか */\n isSseEnabled: boolean\n /** SSE購読の有効/無効を切り替える */\n toggleSse: () => void\n\n /**\n * コメントを追加します。\n * @param reportHubId 日報HubID\n * @param content コメント内容\n * @param businessDate 営業日\n */\n addComment: (reportHubId: number, content: string, businessDate: string) => Promise<void>\n /**\n * コメントを削除します。\n * @param reportHubId 日報HubID\n * @param commentId コメントID (一時IDまたは実ID)\n * @param businessDate 営業日\n */\n deleteComment: (reportHubId: number, commentId: number, businessDate: string) => Promise<void>\n /**\n * スターの状態を切り替えます。\n * @param reportHubId 日報HubID\n * @param isStarred 新しいスター状態\n * @param businessDate 営業日\n */\n toggleStar: (reportHubId: number, isStarred: boolean, businessDate: string) => Promise<void>\n /**\n * 既読の状態を切り替えます。\n * @param reportHubId 日報HubID\n * @param isRead 新しい既読状態\n * @param businessDate 営業日\n */\n toggleRead: (reportHubId: number, isRead: boolean, businessDate: string) => Promise<void>\n createReport: (businessDate: string) => Promise<number>\n updateReport: (reportHubId: number, data: { title?: string; content?: string }, businessDate: string) => Promise<void>\n publishReport: (reportHubId: number, businessDate: string) => Promise<void>\n deleteReport: (reportHubId: number, businessDate: string) => Promise<void>\n /** stale アイテムをリストから除去する安全弁 */\n removeStaleItem: (reportHubId: number) => void\n}\n\n// HMR / dual-bundle (ESM+CJS) でもコンテキスト identity を一意に保つため、\n// グローバルへ登録した単一インスタンスを常に使用する。\ndeclare global {\n var __DailyReportActionContext: Context<DailyReportActionContextType | null> | undefined\n}\n\nconst DailyReportActionContext: Context<DailyReportActionContextType | null> = globalThis.__DailyReportActionContext ?? createContext<DailyReportActionContextType | null>(null)\nglobalThis.__DailyReportActionContext = DailyReportActionContext\n\nexport const useDailyReportActionContext = () => {\n const context = useContext(DailyReportActionContext)\n if (!context) {\n throw new Error(\"useDailyReportActionContext must be used within a DailyReportActionProvider\")\n }\n return context\n}\n\nconst getUserDisplayName = (user: DailyReportUser | undefined): string => {\n if (!user) return \"Unknown\"\n if (user.displayName?.trim()) return user.displayName\n if (user.name) {\n const fullName = `${user.name.familyName ?? \"\"} ${user.name.givenName ?? \"\"}`.trim()\n if (fullName) return fullName\n }\n return user.emails?.[0]?.value ?? \"Unknown\"\n}\n\n/**\n * Provides a context for daily report actions including comments, stars, leads, and basic CRUD operations.\n * Also manages optimistic UI updates and real-time synchronization via SSE.\n * 日報のアクション(コメント追加・削除、スター、既読)を管理するプロバイダー。\n * 楽観的UI更新とAPI通信の抽象化を提供します。\n */\nexport const DailyReportActionProvider = ({ children, user, initialItems = [], userId }: { children: ReactNode; user?: DailyReportUser; initialItems?: DailyReportItem[]; userId?: string | null }) => {\n const { apiBasePath, draftLabelName } = useDailyReportConfig()\n const pendingAddCommentIds = useRef<Map<number, Map<number, string>>>(new Map())\n const pendingDeleteCommentIds = useRef<Map<number, Set<number>>>(new Map())\n const pendingStarUpdates = useRef<Map<number, boolean>>(new Map())\n const pendingReadUpdates = useRef<Map<number, boolean>>(new Map())\n const resolvedIdMap = useRef<Map<number, number>>(new Map())\n const [version, setVersion] = useState(0)\n const [items, setItems] = useState<DailyReportItem[]>(initialItems)\n const [isSseEnabled, setIsSseEnabled] = useState(!!userId)\n const pendingAddPromises = useRef<Map<number, Promise<number>>>(new Map())\n const manuallyTrackingIds = useRef<Set<number>>(new Set())\n /** 削除済み ID を記録し、マージロジックでの復活を防止する */\n const removedIds = useRef<Set<number>>(new Set())\n /** 自分が発行したアクションのID(SSEエコー無視用) */\n const processedClientTempIds = useRef<Set<string>>(new Set())\n\n /**\n * Queue for SSE messages that arrived before the target report was cached.\n * キャッシュ未取得時に到着した SSE メッセージの待機キュー。\n * key: reportHubId, value: { messages, unsubscribe, timer }\n */\n const pendingSseQueue = useRef<\n Map<\n number,\n {\n messages: Exclude<DailyReportSseMessage, { type: \"connected\" }>[]\n unsubscribe: () => void\n timer: ReturnType<typeof setTimeout>\n }\n >\n >(new Map())\n\n /**\n * Ref to always access the latest handleSseMessage for queue replay.\n * キューの再処理時に常に最新の handleSseMessage を参照するための ref。\n */\n const handleSseMessageRef = useRef<(payload: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => void>(() => {})\n\n /**\n * Enqueue an SSE message for a report that isn't cached yet.\n * Subscribes to cache-ready events and replays queued messages when the cache becomes available.\n *\n * キャッシュ未取得のレポートへの SSE メッセージをキューに追加する。\n * キャッシュ書き込みを監視し、書き込まれたらキューのメッセージを再処理する。\n */\n const enqueuePendingSseMessage = useCallback((reportHubId: number, message: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => {\n const queue = pendingSseQueue.current\n const existing = queue.get(reportHubId)\n\n if (existing) {\n // 既存キューにメッセージを追加するだけ\n existing.messages.push(message)\n return\n }\n\n // 新規キューを作成\n const entry = {\n messages: [message],\n unsubscribe: () => {},\n timer: setTimeout(() => {\n // 30秒経過してもキャッシュが来なければキューを破棄\n const e = queue.get(reportHubId)\n if (e) {\n e.unsubscribe()\n queue.delete(reportHubId)\n logger.warn(`[SSE Queue] Expired: reportHubId=${reportHubId}, dropped=${e.messages.length} messages`)\n }\n }, 30_000),\n }\n\n // キャッシュ書き込みを監視\n entry.unsubscribe = subscribeCacheReady(reportHubId, () => {\n const e = queue.get(reportHubId)\n if (!e) return\n\n // キャッシュが書き込まれたか確認\n if (!getCachedReport(reportHubId, { ignoreExpiry: true })) return\n\n // キューをクリーンアップ\n e.unsubscribe()\n clearTimeout(e.timer)\n queue.delete(reportHubId)\n\n // キューのメッセージを最新の handleSseMessage で再処理\n logger.info(`[SSE Queue] Replaying ${e.messages.length} messages for reportHubId=${reportHubId}`)\n for (const msg of e.messages) {\n handleSseMessageRef.current(msg)\n }\n })\n\n queue.set(reportHubId, entry)\n }, [])\n\n const addProcessedId = (id: string) => {\n processedClientTempIds.current.add(id)\n // Auto-remove after 30 seconds to prevent memory leaks\n // 30秒後に自動削除(メモリリーク防止)\n setTimeout(() => {\n processedClientTempIds.current.delete(id)\n }, 30000)\n }\n\n const isProcessedId = useCallback((id: string | undefined): boolean => {\n return !!id && processedClientTempIds.current.has(id)\n }, [])\n\n /**\n * Upserts a report item into the list while keeping businessDate-desc order.\n *\n * レポートアイテムをリストへ upsert し、businessDate 降順を維持します。\n */\n const upsertItem = useCallback((reportHubId: number, businessDate: string | null | undefined) => {\n setItems((prev) => {\n const nextBusinessDate = businessDate ?? prev.find((item) => item.reportHubId === reportHubId)?.businessDate ?? null\n const exists = prev.some((item) => item.reportHubId === reportHubId)\n const merged = exists ? prev.map((item) => (item.reportHubId === reportHubId ? { ...item, businessDate: nextBusinessDate } : item)) : [{ reportHubId, businessDate: nextBusinessDate }, ...prev]\n\n return merged.sort((a, b) => {\n const left = a.businessDate ?? \"\"\n const right = b.businessDate ?? \"\"\n return right.localeCompare(left)\n })\n })\n }, [])\n\n // 未登録ユーザーは SSE を有効化できない\n const toggleSse = () => {\n if (!userId) return\n setIsSseEnabled((prev) => !prev)\n }\n\n /**\n * Remove a stale item from the list as a safety net.\n * フェッチ後にレポートが見つからなかった場合の安全弁としてアイテムを除去する。\n */\n const removeStaleItem = useCallback((reportHubId: number) => {\n removedIds.current.add(reportHubId)\n setItems((prev) => prev.filter((item) => item.reportHubId !== reportHubId))\n }, [])\n\n /**\n * Merge initialItems with prevItems instead of replacing.\n * Items already in the list are kept even if temporarily absent from the API response.\n * Removal is handled exclusively by the report-delete SSE handler.\n *\n * initialItems と prevItems をマージする(全置換ではない)。\n * API レスポンスから一時的に欠落した ID も prevItems に残っていれば保持する。\n * 削除は report-delete SSE ハンドラーでのみ行う。\n */\n // initialItems と prevItems のマージ — 削除済み ID の復活を防止する\n // 外部リソース同期: React Router loader の initialItems 更新に反応する\n useEffect(() => {\n // initialItems に含まれない removedIds エントリは不要なのでクリーンアップ\n const initialIds = new Set(initialItems.map((item) => item.reportHubId))\n // Set のイテレーション中の delete は JS 仕様で安全 (visited は再訪問しない)\n for (const id of removedIds.current) {\n if (!initialIds.has(id)) {\n removedIds.current.delete(id)\n }\n }\n\n setItems((prevItems) => {\n // initialItems をベースにマップを構築(removedIds を除外)\n const mergedMap = new Map<number, DailyReportItem>()\n for (const item of initialItems) {\n if (!removedIds.current.has(item.reportHubId)) {\n mergedMap.set(item.reportHubId, item)\n }\n }\n\n // prevItems にのみ存在する ID を保持(削除は report-delete SSE で行う)\n for (const item of prevItems) {\n if (!(mergedMap.has(item.reportHubId) || removedIds.current.has(item.reportHubId))) {\n mergedMap.set(item.reportHubId, item)\n }\n }\n\n // businessDate 降順でソート\n return Array.from(mergedMap.values()).sort((a, b) => {\n const dateA = a.businessDate ? new Date(a.businessDate).getTime() : 0\n const dateB = b.businessDate ? new Date(b.businessDate).getTime() : 0\n return dateB - dateA\n })\n })\n }, [initialItems])\n\n const handleSseMessage = useCallback(\n (payload: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => {\n // Ignore echo of own actions\n // 自分が発行したアクションのエコーは無視する\n // clientTempId は connected 以外すべてのメッセージに存在する(型で保証済み)\n if (isProcessedId(payload.clientTempId)) {\n return\n }\n\n switch (payload.type) {\n case \"comment-add\": {\n const { comment, reportHubId } = payload\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n if (cached) {\n // Check for duplicates\n // 重複チェック\n if (cached.commentItems.some((c) => c.id === comment.id)) return\n\n // Check if this comment matches a pending optimistic comment\n let isMyComment = false\n const pendingMap = pendingAddCommentIds.current.get(reportHubId)\n if (pendingMap) {\n for (const [tempId, content] of pendingMap.entries()) {\n if (content === comment.content) {\n pendingMap.delete(tempId)\n resolvedIdMap.current.set(tempId, comment.id)\n isMyComment = true\n break\n }\n }\n }\n\n const newComment: DailyReportCommentItem = {\n ...comment,\n userName: comment.userName || (isMyComment ? getUserDisplayName(user) : \"Unknown\"),\n createdAt: comment.createdAt || new Date().toISOString(),\n isMine: isMyComment, // Use the detected ownership\n }\n\n const updatedReport = {\n ...cached,\n commentItems: [...cached.commentItems, newComment].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()),\n }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n // Update mutation time to prevent stale fetch overwrite\n // 古いフェッチによる上書きを防ぐために変更時刻を更新\n acquireMutationLock(reportHubId)\n releaseMutationLock(reportHubId)\n setVersion((v) => v + 1) // UI更新トリガー\n } else {\n // キャッシュ未取得 → フェッチ完了後にリプレイ\n enqueuePendingSseMessage(reportHubId, payload)\n }\n break\n }\n case \"comment-delete\": {\n const { commentId, reportHubId } = payload\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n if (cached) {\n const updatedReport = {\n ...cached,\n commentItems: cached.commentItems.filter((c) => c.id !== commentId),\n }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n // Update mutation time to prevent stale fetch overwrite\n // 古いフェッチによる上書きを防ぐために変更時刻を更新\n acquireMutationLock(reportHubId)\n releaseMutationLock(reportHubId)\n setVersion((v) => v + 1)\n } else {\n enqueuePendingSseMessage(reportHubId, payload)\n }\n break\n }\n case \"status-update\": {\n const { statusType, value, reportHubId } = payload\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n\n if (statusType === \"star\") {\n if (pendingStarUpdates.current.get(reportHubId) === value) {\n pendingStarUpdates.current.delete(reportHubId)\n }\n if (cached) {\n const updatedReport = { ...cached, isStarred: value }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n setVersion((v) => v + 1)\n } else {\n enqueuePendingSseMessage(reportHubId, payload)\n }\n } else if (statusType === \"read\") {\n if (pendingReadUpdates.current.get(reportHubId) === value) {\n pendingReadUpdates.current.delete(reportHubId)\n }\n if (cached) {\n const updatedReport = { ...cached, isRead: value }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n setVersion((v) => v + 1)\n } else {\n enqueuePendingSseMessage(reportHubId, payload)\n }\n }\n break\n }\n case \"report-delete\": {\n const { reportHubId } = payload\n manuallyTrackingIds.current.delete(reportHubId)\n // マージロジックでの復活を防止するために削除済み ID を記録\n removedIds.current.add(reportHubId)\n\n // 削除されたレポートへの待機中 SSE メッセージを破棄\n const pendingEntry = pendingSseQueue.current.get(reportHubId)\n if (pendingEntry) {\n pendingEntry.unsubscribe()\n clearTimeout(pendingEntry.timer)\n pendingSseQueue.current.delete(reportHubId)\n }\n\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n if (cached?.date) {\n deleteDailyReportCache(reportHubId, cached.date)\n setVersion((v) => v + 1)\n }\n setItems((prev) => prev.filter((item) => item.reportHubId !== reportHubId))\n break\n }\n case \"report-create\":\n case \"report-update\":\n case \"report-publish\": {\n // report はスキーマで必須なのでバリデーション通過後は必ず存在する(if ガード不要)\n const { report } = payload\n applyDailyReportServerUpdates(report.reportHubId, report)\n if (payload.type === \"report-create\" || payload.type === \"report-publish\") {\n manuallyTrackingIds.current.add(report.reportHubId)\n upsertItem(report.reportHubId, report.date)\n }\n setVersion((v) => v + 1)\n break\n }\n }\n },\n [isProcessedId, upsertItem, user, enqueuePendingSseMessage],\n )\n\n // 常に最新の handleSseMessage をキューのリプレイから参照できるようにする\n handleSseMessageRef.current = handleSseMessage\n\n /**\n * Cleanup all pending SSE queues on unmount.\n * アンマウント時に全キューをクリーンアップする。\n */\n useEffect(() => {\n return () => {\n for (const entry of pendingSseQueue.current.values()) {\n entry.unsubscribe()\n clearTimeout(entry.timer)\n }\n pendingSseQueue.current.clear()\n }\n }, [])\n\n useDailyReportSseConnection({ isSseEnabled, onMessage: handleSseMessage })\n\n /**\n * Common helper function to execute API actions.\n * APIアクションを実行する共通ヘルパー関数。\n */\n const executeAction = async <T,>(\n reportHubId: number,\n intent: string,\n businessDate: string,\n params: Record<string, string>,\n callbacks: {\n onOptimistic?: () => void\n onSuccess?: (data: ActionResponse) => Promise<T> | T\n onFailure?: () => void\n },\n options?: {\n keepLock?: boolean\n skipLock?: boolean\n },\n ): Promise<T> => {\n if (!options?.skipLock) acquireMutationLock(reportHubId)\n callbacks.onOptimistic?.()\n\n // Generate or use provided clientTempId\n const clientTempId = params.clientTempId || crypto.randomUUID()\n addProcessedId(clientTempId)\n\n const formData = new FormData()\n formData.append(\"intent\", intent)\n formData.append(\"reportHubId\", String(reportHubId))\n formData.append(\"businessDate\", businessDate)\n formData.append(\"operationTimestamp\", String(Date.now()))\n formData.append(\"clientTempId\", clientTempId)\n\n for (const [k, v] of Object.entries(params)) {\n if (k !== \"clientTempId\") {\n formData.append(k, v)\n }\n }\n\n try {\n const res = await fetch(`${apiBasePath}/action`, {\n method: \"POST\",\n body: formData,\n headers: { Accept: \"application/json\" },\n })\n if (!res.ok) {\n const error = new Error(`Failed to ${intent}`)\n Object.assign(error, { status: res.status })\n throw error\n }\n const data = await res.json()\n if (data.status !== \"OK\" || (intent !== \"create\" && String(data.reportHubId) !== String(reportHubId))) {\n throw new Error(`Invalid response for ${intent}`)\n }\n if (typeof data.clientTempId !== \"string\" || data.clientTempId !== clientTempId) {\n throw new Error(`Invalid clientTempId for ${intent}`)\n }\n return (await callbacks.onSuccess?.(data)) as T\n } catch (e) {\n logger.error(`${intent} failed:`, e)\n callbacks.onFailure?.()\n throw e\n } finally {\n if (!(options?.keepLock || options?.skipLock)) {\n releaseMutationLock(reportHubId)\n }\n }\n }\n\n const addComment = async (reportHubId: number, content: string, businessDate: string) => {\n const id = Number(reportHubId)\n const tempId = -1 * Date.now()\n const promise = executeAction(\n id,\n \"addComment\",\n businessDate,\n { content, clientTempId: String(tempId) },\n {\n onOptimistic: () => {\n if (!pendingAddCommentIds.current.has(id)) pendingAddCommentIds.current.set(id, new Map())\n pendingAddCommentIds.current.get(id)?.set(tempId, content)\n setVersion((v) => v + 1)\n },\n onSuccess: (data) => {\n if (!data.newComment) throw new Error(\"No new comment returned\")\n const realId = data.newComment.id\n resolvedIdMap.current.set(tempId, realId)\n pendingAddCommentIds.current.get(id)?.delete(tempId)\n\n const cachedReport = getCachedReport(id, { ignoreExpiry: true })\n if (cachedReport && !cachedReport.commentItems.some((c) => c.id === realId)) {\n updateDailyReportCache(id, {\n commentItems: [...cachedReport.commentItems, { ...data.newComment, id: realId, isMine: true, userName: getUserDisplayName(user) }],\n updatedAt: data.updatedAt,\n })\n }\n return realId\n },\n onFailure: () => {\n pendingAddCommentIds.current.get(id)?.delete(tempId)\n },\n },\n { skipLock: true },\n ).finally(() => pendingAddPromises.current.delete(tempId))\n\n pendingAddPromises.current.set(tempId, promise)\n await promise\n }\n\n const deleteComment = async (reportHubId: number, commentId: number, businessDate: string) => {\n const id = Number(reportHubId)\n // 1. Optimistic UI Update\n if (!pendingDeleteCommentIds.current.has(id)) pendingDeleteCommentIds.current.set(id, new Set())\n pendingDeleteCommentIds.current.get(id)?.add(commentId)\n setVersion((v) => v + 1)\n\n // 2. Resolve ID\n let realId = commentId\n if (commentId < 0) {\n const resolved = resolvedIdMap.current.get(commentId)\n if (resolved !== undefined) {\n realId = resolved\n } else {\n const promise = pendingAddPromises.current.get(commentId)\n if (!promise) return // 既に削除済みかエラー\n try {\n realId = await promise\n } catch {\n return // 追加失敗時は削除不要\n }\n }\n }\n\n // 3. Server Request\n await executeAction(\n id,\n \"deleteComment\",\n businessDate,\n { commentId: String(realId) },\n {\n onSuccess: (data) => {\n const deletedId = typeof data.deletedCommentId === \"string\" ? Number(data.deletedCommentId) : realId\n const cachedReport = getCachedReport(id, { ignoreExpiry: true })\n if (cachedReport) {\n updateDailyReportCache(id, {\n commentItems: cachedReport.commentItems.filter((c) => c.id !== deletedId),\n updatedAt: data.updatedAt,\n })\n registerRecentDeletion(deletedId)\n }\n pendingDeleteCommentIds.current.get(id)?.delete(commentId)\n },\n onFailure: () => {\n pendingDeleteCommentIds.current.get(id)?.delete(commentId)\n },\n },\n { skipLock: true },\n )\n }\n\n const toggleStar = async (reportHubId: number, isStarred: boolean, businessDate: string) => {\n const id = Number(reportHubId)\n await executeAction(\n id,\n \"toggleStar\",\n businessDate,\n { isStarred: String(isStarred) },\n {\n onOptimistic: () => {\n updateDailyReportCache(id, { isStarred })\n pendingStarUpdates.current.set(id, isStarred)\n setVersion((v) => v + 1)\n },\n onSuccess: (data) => {\n if (data.updatedStatus && typeof data.updatedStatus.isStarred === \"boolean\") {\n const cached = getCachedReport(id, { ignoreExpiry: true })\n if (cached) {\n updateDailyReportCache(id, { isStarred: data.updatedStatus.isStarred, updatedAt: data.updatedAt })\n setVersion((v) => v + 1)\n }\n }\n pendingStarUpdates.current.delete(id)\n },\n onFailure: () => {\n updateDailyReportCache(id, { isStarred: !isStarred })\n pendingStarUpdates.current.delete(id)\n },\n },\n { skipLock: true },\n )\n }\n\n const toggleReadImpl = async (reportHubId: number, isRead: boolean, businessDate: string) => {\n const id = Number(reportHubId)\n await executeAction(\n id,\n \"toggleRead\",\n businessDate,\n { isRead: String(isRead) },\n {\n onOptimistic: () => {\n updateDailyReportCache(id, { isRead })\n pendingReadUpdates.current.set(id, isRead)\n setVersion((v) => v + 1)\n },\n onSuccess: (data) => {\n if (data.updatedStatus && typeof data.updatedStatus.isRead === \"boolean\") {\n const cached = getCachedReport(id, { ignoreExpiry: true })\n if (cached) {\n updateDailyReportCache(id, { isRead: data.updatedStatus.isRead, updatedAt: data.updatedAt })\n setVersion((v) => v + 1)\n }\n }\n pendingReadUpdates.current.delete(id)\n },\n onFailure: () => {\n updateDailyReportCache(id, { isRead: !isRead })\n pendingReadUpdates.current.delete(id)\n },\n },\n { skipLock: true },\n )\n }\n // ref 経由で最新実装を参照し、安定した関数 identity を提供する\n // (SSE イベントによるコンテキスト再レンダリングで auto-read タイマーがリセットされるのを防止)\n const toggleReadRef = useRef(toggleReadImpl)\n toggleReadRef.current = toggleReadImpl\n const toggleRead = useCallback((reportHubId: number, isRead: boolean, businessDate: string) => toggleReadRef.current(reportHubId, isRead, businessDate), [])\n\n const createReport = async (businessDate: string) => {\n const tempId = -1 * Date.now()\n // Optimistic UI update\n const now = new Date().toISOString()\n const dummyReport: DailyReportDetail = {\n reportHubId: tempId,\n date: businessDate,\n createdAt: now,\n author: getUserDisplayName(user),\n userId: user?.id ?? \"unknown\",\n sourceType: \"Internal\",\n employeeName: getUserDisplayName(user),\n updatedBy: getUserDisplayName(user),\n updatedAt: now,\n category: null,\n creationCategory: null,\n visitTimeFrom: null,\n visitTimeTo: null,\n customerName: null,\n interviewers: [],\n subject: \"\",\n content: \"\",\n comments: [],\n isRead: true,\n isStarred: false,\n labels: [{ id: 0, name: draftLabelName, color: \"#9ca3af\" }],\n commentItems: [],\n }\n writeCache(dummyReport, Date.now() + 300000, false)\n\n setItems((prev) => [\n {\n reportHubId: tempId,\n businessDate: businessDate,\n },\n ...prev,\n ])\n\n try {\n const res = await executeAction<ActionResponse>(\n 0,\n \"create\",\n businessDate,\n { clientTempId: String(tempId) },\n {\n onSuccess: (data) => data,\n },\n )\n // Validate response mapping\n if (res.clientTempId && res.clientTempId !== String(tempId)) {\n logger.warn(`[createReport] ID mismatch. req=${tempId}, res=${res.clientTempId}`)\n // Mismatch implies this response is not for this specific request. Throwing here triggers revert.\n throw new Error(\"Optimistic ID mismatch\")\n }\n\n const newId = Number(res.reportHubId)\n\n // newId を一時的にロックし、createReport 以前に開始された背景 prefetch の\n // cacheMany による stale データ上書きを防止する\n acquireMutationLock(newId)\n\n // Seed cache with server response (checking staleness)\n if (res.report) {\n applyDailyReportServerUpdates(newId, res.report)\n setVersion((v) => v + 1)\n }\n\n setItems((prev) => {\n // If newId is already present (e.g. from SSE), just remove tempId to avoid duplication\n if (prev.some((item) => item.reportHubId === newId)) {\n return prev.filter((item) => item.reportHubId !== tempId)\n }\n return prev.map((item) => (item.reportHubId === tempId ? { ...item, reportHubId: newId } : item))\n })\n\n // ロック解放: lastMutations に newId が記録され、以降の cacheMany でも保護される\n setTimeout(() => releaseMutationLock(newId), 1000)\n\n return newId\n } catch (e) {\n logger.error(\"[createReport] Failed or mismatched\", e)\n // Revert optimistic update\n setItems((prev) => prev.filter((item) => item.reportHubId !== tempId))\n throw e\n }\n }\n\n const updateReport = async (reportHubId: number, data: { title?: string; content?: string }, businessDate: string) => {\n const id = Number(reportHubId)\n await executeAction(\n id,\n \"update\",\n businessDate,\n { ...(data.title && { title: data.title }), ...(data.content && { content: data.content }) },\n {\n onSuccess: () => {\n const cached = getCachedReport(id, { ignoreExpiry: true })\n if (cached) {\n updateDailyReportCache(id, {\n subject: data.title ?? cached.subject,\n content: data.content ?? cached.content,\n })\n setVersion((v) => v + 1)\n }\n },\n },\n )\n }\n\n const publishReport = async (reportHubId: number, businessDate: string) => {\n const id = Number(reportHubId)\n const cached = getCachedReport(id, { ignoreExpiry: true })\n const oldLabels = cached?.labels || []\n\n await executeAction(\n id,\n \"publish\",\n businessDate,\n {},\n {\n onOptimistic: () => {\n if (cached) {\n const newLabels = cached.labels.filter((l) => l.name !== draftLabelName)\n updateDailyReportCache(id, {\n labels: newLabels,\n })\n setVersion((v) => v + 1)\n }\n },\n onSuccess: (data) => {\n // サーバーからのレスポンスを正とする\n if (data.report) {\n applyDailyReportServerUpdates(id, data.report)\n setVersion((v) => v + 1)\n }\n },\n onFailure: () => {\n // ロールバック\n updateDailyReportCache(id, {\n labels: oldLabels,\n })\n setVersion((v) => v + 1)\n },\n },\n { skipLock: true },\n )\n }\n\n const deleteReport = async (reportHubId: number, businessDate: string) => {\n const id = Number(reportHubId)\n const targetItem = items.find((i) => i.reportHubId === id)\n\n // マージロジックでの復活を防止するために削除済み ID を記録\n removedIds.current.add(id)\n // Optimistic UI update\n setItems((prev) => prev.filter((item) => item.reportHubId !== id))\n\n try {\n await executeAction(\n id,\n \"delete\",\n businessDate,\n {},\n {\n onSuccess: (_data) => {\n deleteDailyReportCache(id, businessDate)\n // Note: Item already removed from list optimistically\n },\n },\n )\n } catch (e: unknown) {\n const error = e as { status?: number }\n // 既に削除済み(404)の場合は、成功とみなしてロールバックしない(キャッシュはクリアする)\n if (error.status === 404) {\n deleteDailyReportCache(id, businessDate)\n return\n }\n\n // それ以外のエラーの場合は、楽観的更新をロールバックする\n removedIds.current.delete(id)\n if (targetItem) {\n setItems((prev) => {\n if (prev.some((i) => i.reportHubId === id)) return prev\n return [targetItem, ...prev].sort((a, b) => (b.businessDate ?? \"\").localeCompare(a.businessDate ?? \"\"))\n })\n }\n }\n }\n\n return (\n <DailyReportActionContext.Provider\n value={{\n user,\n pendingAddCommentIds,\n pendingDeleteCommentIds,\n pendingStarUpdates,\n pendingReadUpdates,\n resolvedIdMap,\n version,\n items,\n isSseEnabled,\n toggleSse,\n addComment,\n deleteComment,\n toggleStar,\n toggleRead,\n createReport,\n updateReport,\n publishReport,\n deleteReport,\n removeStaleItem,\n }}>\n {children}\n </DailyReportActionContext.Provider>\n )\n}\n","/**\n * Minimal level-filtered logger used internally by the daily-report package.\n * daily-report パッケージ内部で使う最小のレベルフィルタ付きロガー。\n *\n * 消費アプリのロガー実装 (console 互換の debug/info/warn/error) を注入でき、\n * 未注入時は console にフォールバックする。\n */\n\n/** 出力フィルタリング用のログレベル。 */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/** Console 互換のロガーインターフェース。 */\nexport interface DailyReportLogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * Creates a prefixed, level-filtered logger delegating to the given implementation.\n * 指定実装へ委譲するプレフィックス付き・レベルフィルタ付きロガーを生成する処理。\n */\nexport const createLogger = (level: LogLevel, prefix: string, impl: DailyReportLogger = console): DailyReportLogger => {\n // 文字列メッセージはプレフィックスを連結、それ以外は先頭引数として付加する\n const format = (message: unknown): unknown[] => (typeof message === \"string\" ? [`${prefix} ${message}`] : [prefix, message])\n return {\n debug: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.DEBUG) impl.debug(...format(message), ...rest)\n },\n info: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.INFO) impl.info(...format(message), ...rest)\n },\n warn: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.WARN) impl.warn(...format(message), ...rest)\n },\n error: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.ERROR) impl.error(...format(message), ...rest)\n },\n }\n}\n","/**\n * Suspense-friendly daily report detail loader (fetch-driven).\n * fetch ベースの日報詳細ローダー。\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\"\nimport { normalizeBusinessDateKey } from \"../../shared/business-date\"\nimport { createLogger, LogLevel } from \"../../shared/logger\"\nimport type { DailyReportDetail } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\n\nconst logger = createLogger(LogLevel.INFO, \"use-daily-report\")\n\n// --- Configuration & Constants ---\n/**\n * Retrieve global configuration or default.\n * グローバル設定またはデフォルト値を取得する。\n */\nconst getGlobal = (k: string, d: number): number => {\n if (typeof window === \"undefined\") return d\n const v = (window as unknown as Record<string, unknown>)[k]\n return typeof v === \"number\" && !Number.isNaN(v) ? v : d\n}\nconst TTL = getGlobal(\"__DAILY_REPORT_CACHE_TTL__\", 300_000)\nconst REVIVE_MS = getGlobal(\"__DAILY_REPORT_CACHE_REVIVE_MS__\", 60_000)\nconst FETCH_DEBOUNCE_MS = 120\n\n// --- Cache Store (Centralized State) ---\ntype CacheEntry<T> = { data: T; expiresAt: number }\n\nconst DailyReportCache = {\n reports: new Map<number, CacheEntry<DailyReportDetail>>(),\n lists: new Map<string, CacheEntry<DailyReportDetail[]>>(),\n pendingLists: new Map<string, Promise<DailyReportDetail[]>>(),\n pendingReports: new Map<number, Promise<DailyReportDetail>>(),\n listeners: new Set<(id: number) => void>(),\n locks: new Set<number>(),\n lastMutations: new Map<number, number>(),\n recentDeletes: new Map<number, number>(),\n lastPrune: 0,\n\n /**\n * Prune expired entries from the cache.\n * キャッシュから期限切れのエントリを削除する。\n */\n prune() {\n const now = Date.now()\n // Prune at most once per minute\n if (now - this.lastPrune < 60_000) return\n this.lastPrune = now\n\n // SWR のために古いデータを保持する。TTL 超過後すぐには削除せず、十分に古い(例えば 1時間経過した)データのみを破棄する\n const GC_THRESHOLD = 60 * 60 * 1000 // 1 hour\n for (const [id, entry] of this.reports) {\n if (now - entry.expiresAt > GC_THRESHOLD) this.reports.delete(id)\n }\n for (const [key, entry] of this.lists) {\n if (now - entry.expiresAt > GC_THRESHOLD) this.lists.delete(key)\n }\n },\n\n /**\n * Check if the cache entry is still valid.\n * キャッシュエントリがまだ有効かを確認する。\n */\n isValid: (ex: number) => ex > Date.now(),\n /**\n * Notify all listeners of a report update.\n * レポートの更新をすべてのリスナーに通知する。\n */\n notify: (id: number) => {\n DailyReportCache.listeners.forEach((l) => {\n l(id)\n })\n },\n\n /**\n * Retrieve a cached report by ID.\n * ID でキャッシュされたレポートを取得する。\n */\n getReport(id: number, ignoreExpiry = false) {\n const e = this.reports.get(id)\n return e && (ignoreExpiry || this.isValid(e.expiresAt)) ? e.data : null\n },\n\n /**\n * Retrieve a cached list of reports by business date key.\n * 営業日キーでレポートのキャッシュリストを取得する。\n */\n getList(dateKey: string | null, ignoreExpiry = false) {\n const e = dateKey && this.lists.get(dateKey)\n return e && (ignoreExpiry || this.isValid(e.expiresAt)) ? e.data : null\n },\n\n /**\n * Set a report in the cache and optionally sync with the list cache.\n * キャッシュにレポートを設定し、必要に応じてリストキャッシュと同期する。\n */\n set(report: DailyReportDetail, expiresAt: number, syncList = true) {\n this.reports.set(report.reportHubId, { data: report, expiresAt })\n if (syncList) {\n const dateKey = normalizeBusinessDateKey(report.date)\n const listEntry = dateKey ? this.lists.get(dateKey) : null\n if (listEntry && dateKey) {\n const exists = listEntry.data.some((r) => r.reportHubId === report.reportHubId)\n const data = exists ? listEntry.data.map((r) => (r.reportHubId === report.reportHubId ? report : r)) : [report, ...listEntry.data]\n this.lists.set(dateKey, { ...listEntry, data })\n }\n }\n this.notify(report.reportHubId)\n },\n\n /**\n * Update a cached report with partial data.\n * 部分的なデータでキャッシュされたレポートを更新する。\n */\n update(id: number, updates: Partial<DailyReportDetail>, strict = true) {\n const entry = this.reports.get(id)\n if (!entry?.data) return\n\n if (strict && updates.updatedAt && entry.data.updatedAt) {\n if (new Date(updates.updatedAt).getTime() < new Date(entry.data.updatedAt).getTime()) {\n logger.warn(`[DailyReportCache] Stale update skipped id=${id}`)\n return\n }\n }\n this.set({ ...entry.data, ...updates } as DailyReportDetail, Math.max(entry.expiresAt, Date.now() + REVIVE_MS))\n },\n\n /**\n * Delete a report from the cache and update the corresponding list.\n * キャッシュからレポートを削除し、対応するリストを更新する。\n */\n delete(id: number, date: string) {\n this.reports.delete(id)\n const dateKey = normalizeBusinessDateKey(date)\n const list = dateKey && this.lists.get(dateKey)\n if (list && dateKey) {\n this.lists.set(dateKey, { ...list, data: list.data.filter((r) => r.reportHubId !== id) })\n }\n this.notify(id)\n },\n\n /**\n * Merge server report with cached report to handle optimistic updates and zombies.\n * サーバーレポートとキャッシュレポートをマージして、楽観的更新とゾンビコメントを処理する。\n */\n merge(serverReport: DailyReportDetail, cachedReport?: DailyReportDetail | null) {\n let report = serverReport\n // Filter zombies\n if (report.commentItems.some((c) => this.recentDeletes.has(c.id))) {\n report = { ...report, commentItems: report.commentItems.filter((c) => !this.recentDeletes.has(c.id)) }\n }\n // Restore missing \"my comments\"\n if (cachedReport) {\n const serverIds = new Set(report.commentItems.map((c) => c.id))\n const missing = cachedReport.commentItems.filter((c) => !serverIds.has(c.id) && c.isMine && Date.now() - new Date(c.createdAt).getTime() < 10000)\n if (missing.length > 0) {\n report = {\n ...report,\n commentItems: [...report.commentItems, ...missing].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()),\n }\n }\n }\n return report\n },\n\n /**\n * Hydrate the cache with fetched reports, handling comment merges and locks.\n * フェッチされたレポートでキャッシュをハイドレートし、コメントのマージとロックを処理する。\n */\n cacheMany(reports: DailyReportDetail[], dateKey: string | null, fetchStart = 0) {\n if (!reports.length) return []\n this.prune()\n const now = Date.now(),\n expiresAt = now + TTL\n for (const [id, t] of this.recentDeletes) if (now - t > 10000) this.recentDeletes.delete(id)\n\n const processed = reports.map((r) => {\n // Original logic: If locked, return server data directly (don't update cache, don't merge)\n // ロックされている場合はサーバーデータをそのまま返す(キャッシュ更新せず、マージもしない)\n if (this.locks.has(r.reportHubId)) return r\n\n // Original logic: If recently mutated locally, prefer cached data\n // 最近ローカルで変更された場合は、キャッシュデータを優先する\n const lastMut = this.lastMutations.get(r.reportHubId)\n if (lastMut && fetchStart > 0 && lastMut > fetchStart) {\n return this.reports.get(r.reportHubId)?.data ?? r\n }\n\n const cached = this.reports.get(r.reportHubId)?.data\n const merged = this.merge(r, cached)\n this.set(merged, expiresAt, false)\n return merged\n })\n if (dateKey) this.lists.set(dateKey, { data: processed, expiresAt })\n return processed\n },\n}\n\n// --- Public API Exports ---\n/**\n * Update the cache for a single report.\n * 単一レポートのキャッシュを更新する。\n */\nexport const writeCache = (r: DailyReportDetail, exp: number, sync = true) => DailyReportCache.set(r, exp, sync)\n/**\n * Get a cached report by ID.\n * ID でキャッシュされたレポートを取得する。\n */\nexport const getCachedReport = (id: number, opts?: { ignoreExpiry?: boolean }) => DailyReportCache.getReport(id, opts?.ignoreExpiry)\n/**\n * Update specific fields of a cached report.\n * キャッシュされたレポートの特定のフィールドを更新する。\n */\nexport const updateDailyReportCache = (id: number, u: Partial<DailyReportDetail>) => DailyReportCache.update(id, u)\n/**\n * Delete a report from the cache.\n * キャッシュからレポートを削除する。\n */\nexport const deleteDailyReportCache = (id: number, d: string) => DailyReportCache.delete(id, d)\n\nexport const acquireMutationLock = (id: number) => {\n DailyReportCache.locks.add(id)\n DailyReportCache.lastMutations.set(id, Date.now())\n}\nexport const releaseMutationLock = (id: number) => {\n DailyReportCache.locks.delete(id)\n DailyReportCache.lastMutations.set(id, Date.now())\n}\nexport const registerRecentDeletion = (cId: number) => DailyReportCache.recentDeletes.set(cId, Date.now())\n\n/**\n * Subscribe to cache-ready events for a specific report.\n * Returns an unsubscribe function.\n *\n * 特定レポートのキャッシュ書き込みを監視する。\n * 解除関数を返す。\n */\nexport const subscribeCacheReady = (reportHubId: number, callback: () => void): (() => void) => {\n const listener = (id: number) => {\n if (id === reportHubId) {\n callback()\n }\n }\n DailyReportCache.listeners.add(listener)\n return () => {\n DailyReportCache.listeners.delete(listener)\n }\n}\n\nexport const applyDailyReportServerUpdates = (_reportHubIdd: number, report: DailyReportDetail) => {\n DailyReportCache.prune()\n DailyReportCache.set(report, Date.now() + TTL)\n}\n\n// --- Data Loading ---\n/**\n * Fetch a single report by ID.\n * ID で単一のレポートを取得する。\n */\nconst fetchReportById = async (apiBasePath: string, id: number) => {\n // 1. Cache Check\n const cached = DailyReportCache.getReport(id)\n if (cached) return cached\n\n // 2. Prevent Duplicate Requests\n const pending = DailyReportCache.pendingReports.get(id)\n if (pending) return pending\n\n // 3. Fetch\n const start = Date.now()\n const p = fetch(`${apiBasePath}/report?reportHubId=${id}`, { cache: \"no-cache\" })\n .then((res) => (res.ok ? res.json() : Promise.reject(`HTTP ${res.status}`)))\n .then((data) => {\n if (data.error) throw new Error(data.error.message)\n const report = data.report as DailyReportDetail\n // 単一レポートの取得時にキャッシュを一括更新する\n return DailyReportCache.cacheMany([report], null, start)[0]\n })\n .finally(() => DailyReportCache.pendingReports.delete(id))\n DailyReportCache.pendingReports.set(id, p)\n return p\n}\n\n/**\n * Fetch reports by date key.\n * 日付キーでレポートを取得する。\n */\nconst fetchReportsByDate = async (apiBasePath: string, dateKey: string) => {\n const list = DailyReportCache.getList(dateKey)\n if (list) return list\n\n const pending = DailyReportCache.pendingLists.get(dateKey)\n if (pending) return pending\n\n const start = Date.now()\n const p = fetch(`${apiBasePath}/business-date?businessDate=${encodeURIComponent(dateKey)}`, { cache: \"no-cache\" })\n .then((res) => (res.ok ? res.json() : Promise.reject(`HTTP ${res.status}`)))\n .then((data) => {\n if (data.error) throw new Error(data.error.message)\n return DailyReportCache.cacheMany(data.reports ?? [], dateKey, start)\n })\n .finally(() => DailyReportCache.pendingLists.delete(dateKey))\n DailyReportCache.pendingLists.set(dateKey, p)\n return p\n}\n\n// --- Hooks ---\nexport type DailyReportDetailResource = { report: DailyReportDetail | null; error: Error | null; isLoading: boolean; isRefetching: boolean }\n\n/**\n * Provides a fetcher-driven daily report detail reader with business-date aware caching.\n * 営業日キャッシュ対応の日報詳細取得フック。\n */\nexport const useDailyReportDetail = (reportHubId: number, businessDate: string | null): DailyReportDetailResource => {\n const { apiBasePath } = useDailyReportConfig()\n const [report, setReport] = useState<DailyReportDetail | null>(() => DailyReportCache.getReport(reportHubId))\n const [error, setError] = useState<Error | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [isRefetching, setIsRefetching] = useState(false)\n const reqRef = useRef({ id: reportHubId, token: 0 })\n\n // 1. Cache Subscription\n // キャッシュの購読: グローバルな DailyReportCache が更新されたときにローカルの状態を反応的に更新する。\n // 依存関係: [reportHubId] - 対象のレポート ID が変更されたときのみ再購読する。\n // クリーンアップ: メモリリークを防ぐためにリスナーを解除する。\n useEffect(() => {\n const onUpdate = (uid: number) => {\n if (uid === reportHubId) {\n const r = DailyReportCache.getReport(reportHubId, true)\n setReport(r)\n }\n }\n DailyReportCache.listeners.add(onUpdate)\n return () => {\n DailyReportCache.listeners.delete(onUpdate)\n }\n }, [reportHubId])\n\n // 2. Data Loading & Stale Check\n // データのロードと有効期限切れチェック: データのフェッチサイクル、 stale-while-revalidate ロジック、リクエストのデバウンスを管理する。\n // 依存関係: [reportHubId, businessDate, apiBasePath]\n useEffect(() => {\n const dateKey = normalizeBusinessDateKey(businessDate)\n const token = (reqRef.current.token || 0) + 1\n reqRef.current = { id: reportHubId, token }\n\n if (reportHubId <= 0) {\n setReport(null)\n setError(reportHubId === 0 ? new Error(\"Invalid ID\") : null)\n setIsLoading(false)\n setIsRefetching(false)\n return\n }\n\n // Cache Check\n let cached = DailyReportCache.getReport(reportHubId, true)\n let isStale = false\n if (cached) {\n isStale = !DailyReportCache.isValid(DailyReportCache.reports.get(reportHubId)?.expiresAt ?? 0)\n } else if (dateKey) {\n const list = DailyReportCache.getList(dateKey, true)\n const found = list?.find((r) => r.reportHubId === reportHubId)\n if (found) {\n cached = found\n isStale = !DailyReportCache.isValid(DailyReportCache.lists.get(dateKey)?.expiresAt ?? 0)\n }\n }\n setReport(cached)\n\n // If cached and valid, we are done\n if (cached && !isStale) {\n setError(null)\n setIsLoading(false)\n setIsRefetching(false)\n return\n }\n\n setIsLoading(!cached)\n setIsRefetching(true)\n\n const timer = setTimeout(async () => {\n if (reqRef.current.token !== token) return\n try {\n let r: DailyReportDetail | null = null\n if (dateKey) {\n const res = await fetchReportsByDate(apiBasePath, dateKey)\n r = res.find((item) => item.reportHubId === reportHubId) ?? null\n } else {\n r = await fetchReportById(apiBasePath, reportHubId)\n }\n\n if (reqRef.current.token === token) {\n setReport(r)\n setError(null)\n }\n } catch (e) {\n if (reqRef.current.token === token) setError(e instanceof Error ? e : new Error(\"Load failed\"))\n } finally {\n if (reqRef.current.token === token) {\n setIsLoading(false)\n setIsRefetching(false)\n }\n }\n }, FETCH_DEBOUNCE_MS)\n\n return () => clearTimeout(timer)\n }, [reportHubId, businessDate, apiBasePath])\n\n return { report, error, isLoading, isRefetching }\n}\n\n/**\n * Provides a business-date aware prefetch helper.\n * 営業日キャッシュを活用した日報プリフェッチ補助フック。\n */\nexport const useDailyReportPrefetch = () => {\n const { apiBasePath } = useDailyReportConfig()\n return useCallback(\n (date: string | null) => {\n const k = normalizeBusinessDateKey(date)\n if (k && !DailyReportCache.getList(k)) fetchReportsByDate(apiBasePath, k).catch((e) => logger.warn(\"Prefetch error\", k, e))\n },\n [apiBasePath],\n )\n}\n","/**\n * Business date normalization and formatting helper functions.\n * 営業日の正規化とフォーマットを支援するユーティリティ群。\n */\n\ntype BusinessDateInput = string | Date | null | undefined\n\nconst ISO_DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst SLASH_DATE_PATTERN = /^\\d{4}\\/\\d{2}\\/\\d{2}$/\n\nconst zeroPad = (value: number): string => {\n return value < 10 ? `0${value}` : `${value}`\n}\n\nconst normalizeFromDate = (value: Date): string | null => {\n if (Number.isNaN(value.getTime())) {\n return null\n }\n\n const year = value.getFullYear()\n const month = zeroPad(value.getMonth() + 1)\n const day = zeroPad(value.getDate())\n return `${year}-${month}-${day}`\n}\n\n/**\n * Normalizes business date input into YYYY-MM-DD format string.\n * 営業日を YYYY-MM-DD 形式の文字列へ正規化する。\n */\nexport const normalizeBusinessDateKey = (value: BusinessDateInput): string | null => {\n if (value === null || value === undefined) {\n return null\n }\n\n if (value instanceof Date) {\n return normalizeFromDate(value)\n }\n\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return null\n }\n\n if (ISO_DATE_PATTERN.test(trimmed)) {\n return trimmed\n }\n\n if (SLASH_DATE_PATTERN.test(trimmed)) {\n return trimmed.replaceAll(\"/\", \"-\")\n }\n\n const parsed = new Date(trimmed)\n return normalizeFromDate(parsed)\n}\n\n/**\n * Converts normalized business date into display format YYYY-MM-DD.\n * 正規化済みの営業日を YYYY-MM-DD 形式の表示文字列へ変換する。\n */\nexport const formatBusinessDateDisplay = (value: BusinessDateInput): string | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n return normalized\n}\n\n/**\n * Parses normalized business date into Date object.\n * 正規化した営業日を Date オブジェクトへ変換する。\n */\nexport const parseBusinessDateKeyToDate = (value: BusinessDateInput): Date | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n\n const [year, month, day] = normalized.split(\"-\").map((part) => Number.parseInt(part, 10))\n if ([year, month, day].some((component) => Number.isNaN(component))) {\n return null\n }\n\n return new Date(year, month - 1, day)\n}\n","/**\n * SSE connection lifecycle hook for daily-report real-time updates.\n * 日報リアルタイム更新の SSE 接続ライフサイクルフック。\n */\nimport { useEffect } from \"react\"\nimport { createLogger, LogLevel } from \"../../shared/logger\"\nimport { type DailyReportSseMessage, dailyReportSseMessageSchema } from \"../../shared/sse-schema\"\nimport { useDailyReportConfig } from \"../config-context\"\n\nconst logger = createLogger(LogLevel.INFO, \"daily-report-sse-connection\")\n\ntype UseDailyReportSseConnectionOptions = {\n isSseEnabled: boolean\n onMessage: (payload: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => void\n}\n\n/**\n * Manages Daily Report SSE connection lifecycle with reconnect handling.\n * Handles open, message parse/dispatch, error logging, retry, and cleanup.\n *\n * 日報 SSE 接続のライフサイクルを管理する Hook。\n * 接続開始、メッセージ解析と配送、エラー時再接続、クリーンアップを管理します。\n */\nexport const useDailyReportSseConnection = ({ isSseEnabled, onMessage }: UseDailyReportSseConnectionOptions) => {\n const { ssePath } = useDailyReportConfig()\n\n /**\n * Maintains SSE connection while enabled and retries on error.\n * isSseEnabled が true の間は接続を維持し、エラー時は再接続します。\n */\n useEffect(() => {\n if (!isSseEnabled) return\n\n let eventSource: EventSource | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let reconnectAttempt = 0\n let lastReceivedEventId: string | undefined\n\n const connect = () => {\n eventSource?.close()\n eventSource = new EventSource(`${ssePath}${lastReceivedEventId ? `?lastEventId=${lastReceivedEventId}` : \"\"}`)\n\n eventSource.onopen = () => {\n reconnectAttempt = 0\n logger.info(\"SSE Connected\")\n }\n\n eventSource.onmessage = (event) => {\n if (event.lastEventId) {\n lastReceivedEventId = event.lastEventId\n }\n try {\n const raw = JSON.parse(event.data)\n const result = dailyReportSseMessageSchema.safeParse(raw)\n if (!result.success) {\n logger.error(\"SSE Validation Error\", result.error.format())\n return\n }\n if (result.data.type === \"connected\") {\n logger.info(\"SSE Subscribe Ready\")\n return\n }\n onMessage(result.data)\n } catch (e) {\n logger.error(\"SSE Parse Error\", e)\n }\n }\n\n eventSource.onerror = (e) => {\n eventSource?.close()\n eventSource = null\n reconnectAttempt++\n const delayMs = Math.min(2000 * 2 ** (reconnectAttempt - 1), 30000)\n logger.error(`SSE Error. Retrying in ${delayMs}ms (attempt ${reconnectAttempt})...`, e)\n reconnectTimer = setTimeout(connect, delayMs)\n }\n }\n\n connect()\n\n return () => {\n if (eventSource) eventSource.close()\n if (reconnectTimer) clearTimeout(reconnectTimer)\n }\n }, [isSseEnabled, onMessage, ssePath])\n}\n","/**\n * Zod schemas for daily-report SSE messages (client/server shared contract).\n * 日報 SSE メッセージの zod スキーマ (client / server 共有契約)。\n */\nimport { z } from \"zod\"\nimport type { DailyReportCommentItem as DeclaredComment, DailyReportDetail as DeclaredDetail } from \"./types\"\n\n// --- サブスキーマ(types.ts の型と構造的に一致させる) ---\n\nexport const dailyReportInterviewerSchema = z.object({\n name: z.string(),\n affiliation: z.string().nullable(),\n})\n\nexport const dailyReportCommentSchema = z.object({\n name: z.string().nullable(),\n text: z.string().nullable(),\n color: z.string().nullable(),\n})\n\nexport const dailyReportLabelDefSchema = z.object({\n id: z.number(),\n name: z.string(),\n color: z.string().nullable(),\n})\n\nexport const dailyReportCommentItemSchema = z.object({\n id: z.number(),\n userId: z.string(),\n userName: z.string(),\n content: z.string(),\n createdAt: z.string(),\n isMine: z.boolean(),\n})\n\nexport const dailyReportDetailSchema = z.object({\n reportHubId: z.number(),\n date: z.string().nullable(),\n createdAt: z.string().nullable(),\n author: z.string(),\n userId: z.string(),\n sourceType: z.string().nullable(),\n employeeName: z.string().nullable(),\n updatedBy: z.string().nullable(),\n updatedAt: z.string().nullable(),\n category: z.string().nullable(),\n creationCategory: z.string().nullable(),\n visitTimeFrom: z.string().nullable(),\n visitTimeTo: z.string().nullable(),\n customerName: z.string().nullable(),\n interviewers: z.array(dailyReportInterviewerSchema),\n subject: z.string().nullable(),\n content: z.string().nullable(),\n comments: z.array(dailyReportCommentSchema),\n isRead: z.boolean(),\n isStarred: z.boolean(),\n labels: z.array(dailyReportLabelDefSchema),\n commentItems: z.array(dailyReportCommentItemSchema),\n})\n\n// --- SSE メッセージスキーマ(8種) ---\n\nexport const connectedMessageSchema = z.object({\n type: z.literal(\"connected\"),\n})\n\nexport const statusUpdateMessageSchema = z.object({\n type: z.literal(\"status-update\"),\n reportHubId: z.number(),\n statusType: z.enum([\"star\", \"read\"]),\n value: z.boolean(),\n clientTempId: z.string(),\n // recipientRawUserId はサーバーサイドフィルタリング専用。\n // SSE ルートが raw JSON から取得してフィルタ後に除去する。\n // クライアントには到達しないが、サーバー側の publish で .parse() を通すため定義が必要。\n recipientRawUserId: z.number().optional(),\n})\n\nexport const commentAddMessageSchema = z.object({\n type: z.literal(\"comment-add\"),\n reportHubId: z.number(),\n comment: dailyReportCommentItemSchema,\n clientTempId: z.string(),\n})\n\nexport const commentDeleteMessageSchema = z.object({\n type: z.literal(\"comment-delete\"),\n reportHubId: z.number(),\n commentId: z.number(),\n clientTempId: z.string(),\n})\n\nexport const reportCreateMessageSchema = z.object({\n type: z.literal(\"report-create\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportUpdateMessageSchema = z.object({\n type: z.literal(\"report-update\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportPublishMessageSchema = z.object({\n type: z.literal(\"report-publish\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n})\n\nexport const reportDeleteMessageSchema = z.object({\n type: z.literal(\"report-delete\"),\n reportHubId: z.number(),\n clientTempId: z.string(),\n})\n\n// --- Discriminated Union ---\n\nexport const dailyReportSseMessageSchema = z.discriminatedUnion(\"type\", [\n connectedMessageSchema,\n statusUpdateMessageSchema,\n commentAddMessageSchema,\n commentDeleteMessageSchema,\n reportCreateMessageSchema,\n reportUpdateMessageSchema,\n reportPublishMessageSchema,\n reportDeleteMessageSchema,\n])\n\n// --- 型エクスポート ---\n\nexport type DailyReportSseMessage = z.infer<typeof dailyReportSseMessageSchema>\n\n// --- コンパイル時の型互換チェック ---\n// Zod推論型が既存の型定義に代入可能であることを保証する。\n// ここでエラーが出たらスキーマと types.ts がずれている。\n\ntype _AssertDetailCompat = z.infer<typeof dailyReportDetailSchema> extends DeclaredDetail ? true : never\ntype _AssertCommentCompat = z.infer<typeof dailyReportCommentItemSchema> extends DeclaredComment ? true : never\nconst _detailCheck: _AssertDetailCompat = true\nconst _commentCheck: _AssertCommentCompat = true\nvoid _detailCheck\nvoid _commentCheck\n","/**\n * Comment management hook merging server data with optimistic local updates.\n * サーバーデータとローカルの楽観的更新を統合して日報コメントを管理するフック。\n */\nimport { useCallback, useMemo, useRef, useState } from \"react\"\nimport { mergeComments, type UIComment } from \"../../shared/comment-adapter\"\nimport type { DailyReportDetail } from \"../../shared/types\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\n\ntype User = { displayName?: string; name?: { familyName?: string; givenName?: string }; emails?: { value: string }[] }\n\n/**\n * Manages daily report comments, integrating server data with optimistic local updates.\n * サーバーデータとローカルの楽観的更新を統合して日報コメントを管理するフック。\n */\nexport const useDailyReportComments = (report: DailyReportDetail | null, user: User | null | undefined, userId?: string | null) => {\n const { pendingAddCommentIds, pendingDeleteCommentIds, resolvedIdMap, version, addComment, deleteComment } = useDailyReportActionContext()\n const [pendingDeleteId, setPendingDeleteId] = useState<number | string | null>(null)\n\n const handleDeleteComment = useCallback((cId: number) => report && deleteComment(report.reportHubId, cId, report.date ?? \"\"), [report, deleteComment])\n const handleAddComment = useCallback((content: string) => (report && user ? addComment(report.reportHubId, content, report.date ?? \"\") : Promise.resolve()), [report, user, addComment])\n\n const prevUiCommentsRef = useRef<UIComment[]>([])\n\n // UI用コメントリストの生成: サーバーデータ、レガシーコメント、楽観的更新(追加・削除)をマージして表示用データを構築する。\n // 依存関係: [report, user, userId, pendingDeleteCommentIds, pendingAddCommentIds, resolvedIdMap, version]\n // クリーンアップ: なし\n // biome-ignore lint/correctness/useExhaustiveDependencies: version は ref (pending*) 変更時の再計算トリガーとして意図的に依存させる\n const uiComments = useMemo(() => {\n if (!report) return []\n\n // 1. Base: Merge legacy and new comments\n // 基本データ: レガシーコメントと新しい形式のコメントをマージ。\n let comments = mergeComments(report.comments, report.commentItems, userId)\n\n // 2. Filter: Remove pending deletions\n // フィルタリング: 削除保留中のコメントを除外。\n const deletedIds = pendingDeleteCommentIds.current.get(report.reportHubId)\n if (deletedIds?.size) comments = comments.filter((c) => !deletedIds.has(c.id as number))\n\n // 3. Add: Append pending optimistic additions\n // 追加: 楽観的に追加されたコメントを末尾に追加。\n const addedComments = pendingAddCommentIds.current.get(report.reportHubId)\n if (addedComments?.size && user) {\n const existingIds = new Set(comments.map((c) => c.id))\n const authorName = user.displayName?.trim() || (user.name ? `${user.name.familyName ?? \"\"} ${user.name.givenName ?? \"\"}`.trim() : \"\") || user.emails?.[0]?.value || \"Unknown\"\n\n for (const [tempId, content] of addedComments) {\n if (deletedIds?.has(tempId)) continue\n if (existingIds.has(tempId)) continue\n\n // Skip if real ID resolved and already present\n // 実IDが解決済みで既にリストに存在する場合はスキップ。\n const realId = resolvedIdMap.current.get(tempId)\n if (realId && existingIds.has(realId)) continue\n\n // Skip if content duplicated (server data arrived before ID resolution)\n // 内容が重複している場合(ID解決前にサーバーデータが到達した場合)はスキップ。\n if (comments.some((c) => c.isMine && c.content === content && typeof c.id === \"number\" && c.id > 0)) continue\n\n comments.push({ id: tempId, authorName, content, createdAt: new Date().toISOString(), isMine: true, isLegacy: false })\n }\n }\n\n // Stable reference check\n // 参照の安定性チェック: 内容に変更がなければ前回の参照を返すことで不要な再レンダリングを防ぐ。\n if (JSON.stringify(comments) === JSON.stringify(prevUiCommentsRef.current)) return prevUiCommentsRef.current\n prevUiCommentsRef.current = comments\n return comments\n }, [report, user, userId, pendingDeleteCommentIds, pendingAddCommentIds, resolvedIdMap, version])\n\n return { uiComments, handleAddComment, handleDeleteComment, pendingDeleteId, setPendingDeleteId, resolvedIdMap: resolvedIdMap.current }\n}\n","/**\n * Shared hook for calculating viewport height of virtual scroll containers.\n * 仮想スクロールコンテナのビューポート高さを計測する共通フック。\n */\nimport { type RefObject, useCallback, useEffect, useState } from \"react\"\n\ntype UseDynamicViewportHeightOptions = {\n enableMutationObserver?: boolean\n bottomGap?: number\n minFallbackHeight?: number\n}\n\ntype UseDynamicViewportHeightResult = {\n viewportHeight: number\n updateViewportHeight: () => void\n}\n\n/**\n * Computes and updates the viewport height for scroll containers while reacting to layout changes.\n * レイアウト変化に追従しながらスクロールコンテナのビューポート高さを算出して更新する関数。\n */\nexport const useDynamicViewportHeight = (containerRef: RefObject<HTMLDivElement | null>, options?: UseDynamicViewportHeightOptions): UseDynamicViewportHeightResult => {\n const { enableMutationObserver = false, bottomGap = 4, minFallbackHeight = 240 } = options ?? {}\n const [viewportHeight, setViewportHeight] = useState(600)\n\n const updateViewportHeight = useCallback(() => {\n if (typeof window === \"undefined\") {\n return\n }\n\n const target = containerRef.current\n if (!target) {\n return\n }\n\n // 要素が非表示の場合は計測しない (display: none など)\n if (target.offsetParent === null && window.getComputedStyle(target).position !== \"fixed\") {\n return\n }\n\n const toNumber = (value: string) => Number.parseFloat(value) || 0\n const { top: containerTop } = target.getBoundingClientRect()\n const parentRect = target.parentElement?.getBoundingClientRect()\n const rootStyle = window.getComputedStyle(document.documentElement)\n const footerHeight = toNumber(rootStyle.getPropertyValue(\"--footer-height\"))\n const headerHeight = toNumber(rootStyle.getPropertyValue(\"--header-height\"))\n const scrollbarHeight = toNumber(rootStyle.getPropertyValue(\"--scrollbar-height\"))\n const containerStyle = window.getComputedStyle(target)\n const marginBottom = toNumber(containerStyle.marginBottom)\n const paddingBottom = toNumber(containerStyle.paddingBottom)\n const viewportBottom = window.innerHeight - footerHeight - scrollbarHeight - bottomGap\n const effectiveBottom = parentRect ? Math.min(parentRect.bottom, viewportBottom) : viewportBottom\n const layoutTop = parentRect ? Math.max(parentRect.top, headerHeight) : headerHeight\n const effectiveTop = Math.max(containerTop, layoutTop)\n const availableHeight = effectiveBottom - effectiveTop - marginBottom - paddingBottom\n const safeAvailableHeight = Number.isFinite(availableHeight) ? Math.floor(availableHeight) : 0\n const clampedHeight = safeAvailableHeight > 0 ? safeAvailableHeight : minFallbackHeight\n\n if (clampedHeight > 0 && Number.isFinite(clampedHeight)) {\n setViewportHeight((previous) => (Math.abs(previous - clampedHeight) > 1 ? clampedHeight : previous))\n }\n }, [bottomGap, containerRef, minFallbackHeight])\n\n useEffect(() => {\n if (typeof window === \"undefined\") {\n return\n }\n\n let frameId: number | null = null\n let timeoutId: number | null = null\n let resizeObserver: ResizeObserver | null = null\n let mutationObserver: MutationObserver | null = null\n const scheduleUpdate = () => {\n if (frameId !== null) {\n window.cancelAnimationFrame(frameId)\n }\n frameId = window.requestAnimationFrame(() => {\n updateViewportHeight()\n })\n }\n\n // 初期表示直後とわずかな遅延後に計測して安定させる\n scheduleUpdate()\n timeoutId = window.setTimeout(() => {\n scheduleUpdate()\n }, 150)\n\n const containerElement = containerRef.current\n if (typeof ResizeObserver !== \"undefined\" && containerElement) {\n resizeObserver = new ResizeObserver(() => {\n // コンテナと親要素のリサイズ変化に追従する\n scheduleUpdate()\n })\n resizeObserver.observe(containerElement)\n if (containerElement.parentElement) {\n resizeObserver.observe(containerElement.parentElement)\n }\n }\n\n if (enableMutationObserver && typeof MutationObserver !== \"undefined\") {\n mutationObserver = new MutationObserver(() => {\n // ルートのスタイル変更にも追従する\n scheduleUpdate()\n })\n mutationObserver.observe(document.documentElement, { attributes: true, attributeFilter: [\"style\"] })\n }\n\n window.addEventListener(\"resize\", scheduleUpdate)\n\n return () => {\n if (frameId !== null) {\n window.cancelAnimationFrame(frameId)\n }\n if (timeoutId !== null) {\n window.clearTimeout(timeoutId)\n }\n window.removeEventListener(\"resize\", scheduleUpdate)\n resizeObserver?.disconnect()\n mutationObserver?.disconnect()\n }\n }, [enableMutationObserver, updateViewportHeight, containerRef])\n\n return {\n viewportHeight,\n updateViewportHeight,\n }\n}\n","import { type ScrollBarTapCircleOptions, type ScrollPaneInertiaOptions, tapScrollCircleSampleVisual } from \"@aiquants/virtualscroll\"\nimport type { DailyReportItem } from \"../../shared/types\"\nimport type { DailyReportDetailResource } from \"../hooks/use-daily-report\"\n\n/**\n * Shared constants for Daily Report data fetching parameters.\n * 日報データ取得で利用する共通パラメーター。\n */\nexport const DAILY_REPORT_FETCH_DEBOUNCE_MS = 180\n\nexport const DEFAULT_ITEM_HEIGHT = 160\nexport const ITEM_VERTICAL_PADDING = 6\nexport const ITEM_CONTAINER_HEIGHT = DEFAULT_ITEM_HEIGHT - ITEM_VERTICAL_PADDING\nexport const FAST_SCROLL_WHEEL_MULTIPLIER = 0.8\nexport const FAST_SCROLL_INERTIA_OPTIONS: ScrollPaneInertiaOptions = { maxVelocity: 18, deceleration: 0.0018, startVelocityThreshold: 0.03 }\nexport const PREFETCH_LOOKAHEAD = 20\nexport const PREFETCH_MAX_DATES = 6\nexport const EMPTY_ITEM: DailyReportItem = { reportHubId: -1, businessDate: null }\nexport const EMPTY_DETAIL: DailyReportDetailResource = { report: null, error: null, isLoading: false, isRefetching: false }\nexport const WHEEL_RESET_TIMEOUT_MS = 240\nexport const VIRTUAL_SCROLL_OVERSCAN_COUNT = 15\nexport const SCROLL_BAR_WIDTH = 8\nexport const MAX_PREVIEW_LINES = 3\nexport const TAP_SCROLL_CIRCLE_OPTIONS: ScrollBarTapCircleOptions = {\n enabled: true,\n renderVisual: tapScrollCircleSampleVisual,\n maxSpeedCurve: { exponentialSteepness: 7.5, exponentialScale: 14, easedOffset: 0.18 },\n maxSpeedMultiplier: 18,\n}\n","import { useEffect, useRef, useState } from \"react\"\nimport type { DailyReportDetail } from \"../../shared/types\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { Button } from \"../ui/button\"\nimport { Input } from \"../ui/input\"\nimport { Label } from \"../ui/label\"\nimport { Textarea } from \"../ui/textarea\"\n\ntype DailyReportEditFormProps = {\n report: DailyReportDetail\n onCancel?: () => void\n onPublishSuccess?: () => void\n}\n\nexport const DailyReportEditForm = ({ report, onCancel, onPublishSuccess }: DailyReportEditFormProps) => {\n const { updateReport, publishReport } = useDailyReportActionContext()\n const [title, setTitle] = useState(report.subject ?? \"\")\n const [content, setContent] = useState(report.content ?? \"\")\n const [isSaving, setIsSaving] = useState(false)\n\n // 同一レポート編集中に SSE/キャッシュ更新でフォームがリセットされるのを防止するための ID 追跡\n const syncedReportIdRef = useRef(report.reportHubId)\n\n // レポート切り替え時のみフォーム状態を同期する (同一レポートのキャッシュ更新ではリセットしない)\n useEffect(() => {\n if (report.reportHubId !== syncedReportIdRef.current) {\n setTitle(report.subject ?? \"\")\n setContent(report.content ?? \"\")\n syncedReportIdRef.current = report.reportHubId\n }\n }, [report.reportHubId, report.subject, report.content])\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n await updateReport(report.reportHubId, { title, content }, report.date ?? \"\")\n } finally {\n setIsSaving(false)\n }\n }\n\n const handlePublish = async () => {\n setIsSaving(true)\n try {\n // Ensure latest changes are saved before publishing\n await updateReport(report.reportHubId, { title, content }, report.date ?? \"\")\n await publishReport(report.reportHubId, report.date ?? \"\")\n onPublishSuccess?.()\n } finally {\n setIsSaving(false)\n }\n }\n\n return (\n <div className=\"space-y-6 p-1\">\n <div className=\"space-y-2\">\n <Label htmlFor={`report-title-${report.reportHubId}`}>タイトル</Label>\n <Input id={`report-title-${report.reportHubId}`} value={title} onChange={(e) => setTitle(e.target.value)} placeholder=\"日報のタイトルを入力\" disabled={isSaving} />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor={`report-content-${report.reportHubId}`}>内容</Label>\n <Textarea id={`report-content-${report.reportHubId}`} value={content} onChange={(e) => setContent(e.target.value)} placeholder=\"日報の内容を入力\" className=\"min-h-50\" disabled={isSaving} />\n </div>\n\n <div className=\"flex justify-end gap-2\">\n {onCancel && (\n <Button variant=\"outline\" onClick={onCancel} disabled={isSaving}>\n キャンセル\n </Button>\n )}\n <Button variant=\"secondary\" onClick={handleSave} disabled={isSaving}>\n 保存\n </Button>\n <Button onClick={handlePublish} disabled={isSaving}>\n 公開\n </Button>\n </div>\n </div>\n )\n}\n","import { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport * as React from \"react\"\nimport { cn } from \"./cn\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n)\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />\n})\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n","import type * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n )\n}\n\nexport { Input }\n","import * as LabelPrimitive from \"@radix-ui/react-label\"\nimport type * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nfunction Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\"flex select-none items-center gap-2 font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50\", className)}\n {...props}\n />\n )\n}\n\nexport { Label }\n","import type * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex min-h-15 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n )\n}\n\nexport { Textarea }\n","import { cn } from \"../ui/cn\"\n\ntype UnreadIndicatorProps = {\n className?: string\n}\n\n/**\n * A simple blue dot indicator used to represent unread status.\n * 未読状態を表すためのシンプルな青いドットインジケーター。\n */\nexport const UnreadIndicator = ({ className }: UnreadIndicatorProps) => {\n return <span className={cn(\"text-blue-500\", className)}>●</span>\n}\n","/**\n * Virtual-scrolling summary list for daily reports driven by fetcher-based detail hooks.\n * useFetcher ベースの詳細取得フックで日報サマリーを仮想スクロール表示。\n */\nimport { SwipeCloseGuardContext, useSwipeGuardState, useSwipeToDismissOverlay, useTapClick, useTransitionDisplay } from \"@aiquants/swipe-overlay\"\nimport { type ScrollBarThumbOverlayRenderProps, VirtualScroll, type VirtualScrollHandle } from \"@aiquants/virtualscroll\"\nimport { Check, Star, Trash2 } from \"lucide-react\"\nimport { type CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { fallbackText, formatToIsoDate } from \"../../shared/text-utils\"\nimport type { DailyReportDetail, DailyReportItem } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { useDailyReportDetail } from \"../hooks/use-daily-report\"\nimport { useDailyReportComments } from \"../hooks/use-daily-report-comments\"\nimport { useDynamicViewportHeight } from \"../hooks/use-dynamic-viewport-height\"\nimport { cn } from \"../ui/cn\"\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\"\nimport {\n DEFAULT_ITEM_HEIGHT,\n EMPTY_DETAIL,\n EMPTY_ITEM,\n FAST_SCROLL_INERTIA_OPTIONS,\n FAST_SCROLL_WHEEL_MULTIPLIER,\n ITEM_CONTAINER_HEIGHT,\n ITEM_VERTICAL_PADDING,\n MAX_PREVIEW_LINES,\n SCROLL_BAR_WIDTH,\n TAP_SCROLL_CIRCLE_OPTIONS,\n VIRTUAL_SCROLL_OVERSCAN_COUNT,\n WHEEL_RESET_TIMEOUT_MS,\n} from \"../utils/constants\"\nimport { BusinessDayThumbOverlay } from \"./business-day-thumb-overlay\"\nimport { DailyReportCommentForm, DailyReportCommentList } from \"./daily-report-comment-section\"\nimport { DailyReportEditForm } from \"./daily-report-edit-form\"\nimport { UnreadIndicator } from \"./unread-indicator\"\n\ntype TestableVirtualScrollHandle = VirtualScrollHandle & {\n findReportIndex: (id: number) => number\n getReportIds: (limit?: number) => number[]\n}\n\ntype DailyReportListProps = {\n dailyReportItems: DailyReportItem[]\n initialSelectedBusinessDate?: string | null\n initialSelectedReportHubId?: number | null\n autoMarkRead?: boolean\n selectedReportHubId: number | null\n onSelectItem: (reportHubId: number | null) => void\n userId?: string | null\n}\n\n/**\n * Manages mobile overlay state, history navigation, and scroll locking.\n * モバイルオーバーレイの状態、履歴ナビゲーション、スクロールロックを管理するフック。\n */\nconst useMobileOverlayManager = (isMobileLayout: boolean, selectedReportHubId: number | null, dailyReportItems: DailyReportItem[], onSelectItem: (reportHubId: number | null) => void) => {\n const [isMobileDetailOpen, setIsMobileDetailOpen] = useState(false)\n const [isOverlayMounted, setIsOverlayMounted] = useState(false)\n const overlayHistoryKeyRef = useRef<string | null>(null)\n const lastClosedItemIdRef = useRef<number | null>(null)\n const bodyOverflowRef = useRef<string | null>(null)\n\n const selectedItem = useMemo(() => (selectedReportHubId === null ? null : (dailyReportItems.find((e) => e.reportHubId === selectedReportHubId) ?? null)), [dailyReportItems, selectedReportHubId])\n const shouldShowOverlay = isMobileLayout && isMobileDetailOpen && selectedItem !== null\n\n /**\n * Manages the mounting state of the mobile overlay for animation purposes.\n * アニメーション用にモバイルオーバーレイのマウント状態を管理する副作用。\n *\n * Purpose: To delay unmounting until the close animation completes.\n * Dependencies: [shouldShowOverlay]\n * Cleanup: Clears the timeout.\n */\n useEffect(() => {\n if (shouldShowOverlay) {\n setIsOverlayMounted(true)\n } else {\n const timer = setTimeout(() => setIsOverlayMounted(false), 300)\n return () => clearTimeout(timer)\n }\n }, [shouldShowOverlay])\n\n /**\n * Manages mobile history state and body scroll locking.\n * モバイル履歴状態と Body スクロールロックを管理する副作用。\n *\n * Purpose: To support browser back button for closing overlay and prevent background scrolling.\n * Dependencies: [isMobileLayout, isMobileDetailOpen, selectedReportHubId]\n * Cleanup: Restores body overflow style.\n */\n useEffect(() => {\n if (typeof window === \"undefined\") return\n\n // History Management\n if (isMobileLayout && isMobileDetailOpen && selectedReportHubId !== null && !overlayHistoryKeyRef.current) {\n const key = `overlay-${Date.now()}`\n window.history.pushState({ ...window.history.state, dailyReportOverlay: key }, \"\", window.location.href)\n overlayHistoryKeyRef.current = key\n } else if (!(isMobileLayout && isMobileDetailOpen) && overlayHistoryKeyRef.current) {\n overlayHistoryKeyRef.current = null\n }\n\n // Scroll Lock\n if (isMobileDetailOpen) {\n bodyOverflowRef.current = document.body.style.overflow\n document.body.style.overflow = \"hidden\"\n } else if (bodyOverflowRef.current !== null) {\n document.body.style.overflow = bodyOverflowRef.current\n bodyOverflowRef.current = null\n }\n\n return () => {\n if (bodyOverflowRef.current !== null) document.body.style.overflow = bodyOverflowRef.current\n }\n }, [isMobileLayout, isMobileDetailOpen, selectedReportHubId])\n\n /**\n * Handles browser back navigation (popstate).\n * ブラウザの戻る操作 (popstate) を処理する副作用。\n *\n * Purpose: To close the mobile overlay when the user presses the back button.\n * Dependencies: [selectedReportHubId]\n * Cleanup: Removes popstate listener.\n */\n useEffect(() => {\n if (typeof window === \"undefined\") return\n const onPopState = () => {\n if (overlayHistoryKeyRef.current) {\n lastClosedItemIdRef.current = selectedReportHubId\n overlayHistoryKeyRef.current = null\n setIsMobileDetailOpen(false)\n onSelectItem(null)\n }\n }\n window.addEventListener(\"popstate\", onPopState)\n return () => window.removeEventListener(\"popstate\", onPopState)\n }, [selectedReportHubId, onSelectItem])\n\n const handleOverlayClose = useCallback(() => {\n if (!isMobileLayout) return\n lastClosedItemIdRef.current = selectedReportHubId\n setIsMobileDetailOpen(false)\n onSelectItem(null)\n if (overlayHistoryKeyRef.current && typeof window !== \"undefined\") {\n overlayHistoryKeyRef.current = null\n window.history.back()\n }\n }, [isMobileLayout, selectedReportHubId, onSelectItem])\n\n return {\n isMobileDetailOpen,\n setIsMobileDetailOpen,\n isOverlayMounted,\n shouldShowOverlay,\n handleOverlayClose,\n lastClosedItemIdRef,\n selectedItem,\n }\n}\n\n/**\n * Manages auto-read logic and read status toggling.\n * 自動既読ロジックと既読状態の切り替えを管理するフック。\n */\nconst useAutoRead = (report: DailyReportDetail | null, isLoading: boolean, autoMarkRead: boolean, isActive: boolean, toggleRead: (reportHubId: number, isRead: boolean, date: string) => Promise<void>) => {\n const ignoreAutoReadForReportHubId = useRef<number | null>(null)\n const lastReportHubIdRef = useRef<number | undefined>(undefined)\n\n if (lastReportHubIdRef.current !== report?.reportHubId) {\n lastReportHubIdRef.current = report?.reportHubId\n ignoreAutoReadForReportHubId.current = null\n }\n\n /**\n * Resets suppression flag when inactive.\n * 非アクティブ時に抑制フラグをリセットする副作用。\n *\n * Purpose: To re-enable auto-read when the report is reopened.\n * Dependencies: [isActive]\n * Cleanup: None.\n */\n useEffect(() => {\n if (!isActive) ignoreAutoReadForReportHubId.current = null\n }, [isActive])\n\n /**\n * Handles the read status toggle logic.\n * 既読状態の切り替え処理を行うコールバック。\n *\n * Purpose: To toggle read status and manage auto-read suppression.\n * Dependencies: [report, toggleRead]\n */\n const handleToggleRead = useCallback(\n async (isAuto: boolean = false) => {\n if (!report) return\n if (!isAuto) ignoreAutoReadForReportHubId.current = report.reportHubId\n\n const nextValue = !report.isRead\n try {\n await toggleRead(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n },\n [report, toggleRead],\n )\n\n /**\n * Automatically marks the report as read after a delay.\n * 一定時間後にレポートを自動的に既読にする副作用。\n *\n * Purpose: To mark reports as read when viewed for a sufficient duration.\n * Dependencies: [report, isLoading, autoMarkRead, handleToggleRead]\n * Cleanup: Clears the timeout.\n */\n useEffect(() => {\n if (autoMarkRead && report && !report.isRead && !isLoading) {\n if (ignoreAutoReadForReportHubId.current === report.reportHubId) return\n const timer = setTimeout(() => handleToggleRead(true), 500)\n return () => clearTimeout(timer)\n }\n }, [report, isLoading, autoMarkRead, handleToggleRead])\n\n return { handleToggleRead }\n}\n\n/**\n * Daily report list cards with key metadata and preview content via virtual scrolling.\n * 主なメタデータと内容プレビューを仮想スクロール表示する日報リスト。\n */\nexport const DailyReportList = ({ dailyReportItems, initialSelectedBusinessDate = null, initialSelectedReportHubId = null, autoMarkRead = false, selectedReportHubId, onSelectItem, userId }: DailyReportListProps) => {\n const scrollContainerRef = useRef<HTMLDivElement | null>(null)\n const virtualScrollRef = useRef<VirtualScrollHandle>(null)\n const { viewportHeight, updateViewportHeight } = useDynamicViewportHeight(scrollContainerRef)\n const [overlayLabel, setOverlayLabel] = useState<string>(\"\")\n const [isWheelScrollActive, setIsWheelScrollActive] = useState(false)\n const [isMobileLayout, setIsMobileLayout] = useState(false)\n const wheelResetTimerRef = useRef<number | null>(null)\n const appliedInitialSignatureRef = useRef<string | null>(null)\n const itemCount = dailyReportItems.length\n const scrollEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const { isMobileDetailOpen, setIsMobileDetailOpen, isOverlayMounted, shouldShowOverlay, handleOverlayClose, lastClosedItemIdRef, selectedItem } = useMobileOverlayManager(isMobileLayout, selectedReportHubId, dailyReportItems, onSelectItem)\n\n const columnHeightStyle = useMemo<CSSProperties | undefined>(() => (Number.isFinite(viewportHeight) && viewportHeight > 0 ? { height: viewportHeight } : undefined), [viewportHeight])\n\n /**\n * Manages global event listeners (Media Query, Wheel, Resize).\n * メディアクエリ、ホイール、リサイズなどのグローバルイベントリスナーを管理する副作用。\n *\n * Purpose: To handle responsive layout changes and scroll interactions.\n * Dependencies: [updateViewportHeight]\n * Cleanup: Removes all event listeners and clears timers.\n */\n useEffect(() => {\n if (typeof window === \"undefined\") return\n\n const mq = window.matchMedia(\"(max-width: 639px)\")\n const onMediaChange = (e: MediaQueryListEvent) => setIsMobileLayout(e.matches)\n setIsMobileLayout(mq.matches)\n mq.addEventListener(\"change\", onMediaChange)\n\n const onResize = () => requestAnimationFrame(updateViewportHeight)\n window.addEventListener(\"resize\", onResize)\n updateViewportHeight()\n\n const el = scrollContainerRef.current\n const onWheel = () => {\n setIsWheelScrollActive(true)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n wheelResetTimerRef.current = window.setTimeout(() => {\n setIsWheelScrollActive(false)\n wheelResetTimerRef.current = null\n }, WHEEL_RESET_TIMEOUT_MS)\n }\n el?.addEventListener(\"wheel\", onWheel, { passive: true })\n\n return () => {\n mq.removeEventListener(\"change\", onMediaChange)\n window.removeEventListener(\"resize\", onResize)\n el?.removeEventListener(\"wheel\", onWheel)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n }\n }, [updateViewportHeight])\n\n /**\n * Synchronizes initial selection from props when data changes.\n * データ変更時にプロパティからの初期選択状態を同期する副作用。\n *\n * Purpose: To respect deep-linked selection (ID or Date) when items are loaded.\n * Dependencies: [dailyReportItems, initialSelectedBusinessDate, initialSelectedReportHubId, selectedReportHubId, itemCount, onSelectItem]\n * Cleanup: None.\n */\n useEffect(() => {\n if (!itemCount) {\n appliedInitialSignatureRef.current = null\n if (selectedReportHubId !== null) onSelectItem(null)\n return\n }\n\n const signature = `${initialSelectedReportHubId ?? \"\"}|${initialSelectedBusinessDate ?? \"\"}`\n const hasInitial = (initialSelectedReportHubId ?? null) !== null || (initialSelectedBusinessDate ?? null) !== null\n\n if (!hasInitial) appliedInitialSignatureRef.current = null\n\n if (hasInitial && appliedInitialSignatureRef.current !== signature) {\n const match = (initialSelectedReportHubId && dailyReportItems.find((i) => i.reportHubId === initialSelectedReportHubId)) || (initialSelectedBusinessDate && dailyReportItems.find((i) => i.businessDate === initialSelectedBusinessDate))\n if (match) {\n appliedInitialSignatureRef.current = signature\n if (selectedReportHubId !== match.reportHubId) onSelectItem(match.reportHubId)\n return\n }\n }\n }, [dailyReportItems, initialSelectedBusinessDate, initialSelectedReportHubId, selectedReportHubId, itemCount, onSelectItem])\n\n /**\n * Restores focus after closing overlay (Desktop only).\n * オーバーレイを閉じた後のフォーカス復帰 (デスクトップのみ) を行う副作用。\n *\n * Purpose: To maintain keyboard focus context for accessibility.\n * Dependencies: [isMobileDetailOpen, isMobileLayout, dailyReportItems]\n * Cleanup: None.\n */\n useEffect(() => {\n if (isMobileLayout || isMobileDetailOpen || lastClosedItemIdRef.current === null) return\n const targetId = lastClosedItemIdRef.current\n lastClosedItemIdRef.current = null\n\n const index = dailyReportItems.findIndex((item) => item.reportHubId === targetId)\n if (index >= 0) {\n requestAnimationFrame(() => virtualScrollRef.current?.focusItemAtIndex(index))\n }\n }, [isMobileDetailOpen, isMobileLayout, dailyReportItems, lastClosedItemIdRef])\n\n /**\n * Syncs overlay label with the first item's date.\n * オーバーレイラベルを先頭アイテムの日付と同期する副作用。\n *\n * Purpose: To provide a default label for the scroll thumb overlay.\n * Dependencies: [dailyReportItems, itemCount]\n * Cleanup: None.\n */\n useEffect(() => {\n if (itemCount > 0)\n setOverlayLabel((prev) => {\n const next = formatToIsoDate(dailyReportItems[0]?.businessDate)\n return prev === next ? prev : next\n })\n }, [dailyReportItems, itemCount])\n\n const dailyReportItemsRef = useRef(dailyReportItems)\n dailyReportItemsRef.current = dailyReportItems\n\n useEffect(() => {\n if (virtualScrollRef.current) {\n const el = document.querySelector(\".test-daily-report-list\") as (Element & { __virtualScroll?: TestableVirtualScrollHandle }) | null\n if (el) {\n el.__virtualScroll = {\n ...virtualScrollRef.current,\n findReportIndex: (id: number) => dailyReportItemsRef.current.findIndex((item) => item.reportHubId === id),\n getReportIds: (limit = 20) => dailyReportItemsRef.current.slice(0, limit).map((item) => item.reportHubId),\n }\n }\n }\n })\n\n const handleItemSelect = useCallback(\n (id: number) => {\n if (id <= 0) return\n onSelectItem(id)\n if (isMobileLayout) setIsMobileDetailOpen(true)\n },\n [isMobileLayout, onSelectItem, setIsMobileDetailOpen],\n )\n\n const getItem = useCallback((index: number) => (index < itemCount ? dailyReportItems[index] : EMPTY_ITEM), [dailyReportItems, itemCount])\n const getItemHeight = useCallback(() => DEFAULT_ITEM_HEIGHT, [])\n\n const handleRangeChange = useCallback(\n (range: { visibleStartIndex: number }) => {\n const item = getItem(range.visibleStartIndex)\n if (item?.reportHubId > 0)\n setOverlayLabel((prev) => {\n const next = formatToIsoDate(item.businessDate)\n return prev === next ? prev : next\n })\n },\n [getItem],\n )\n\n const handleItemFocus = useCallback(\n (index: number) => {\n const item = getItem(index)\n if (item && item.reportHubId > 0) {\n if (item.reportHubId === selectedReportHubId) return\n onSelectItem(item.reportHubId)\n }\n },\n [getItem, onSelectItem, selectedReportHubId],\n )\n\n const handleScroll = useCallback(() => {\n const container = scrollContainerRef.current\n if (!container) return\n\n if (!container.classList.contains(\"is-scrolling\")) {\n container.classList.add(\"is-scrolling\")\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n container.classList.remove(\"is-scrolling\")\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [])\n\n const renderThumbOverlay = useCallback((props: ScrollBarThumbOverlayRenderProps) => <BusinessDayThumbOverlay {...props} label={overlayLabel} isWheelScrollActive={isWheelScrollActive} />, [overlayLabel, isWheelScrollActive])\n\n return (\n <>\n <div className=\"flex h-full min-h-0 gap-4\">\n <div ref={scrollContainerRef} className=\"flex min-h-0 w-full sm:w-90\" style={columnHeightStyle}>\n <VirtualScroll\n ref={virtualScrollRef}\n itemCount={itemCount}\n getItem={getItem}\n getItemHeight={getItemHeight}\n viewportSize={viewportHeight}\n overscanCount={VIRTUAL_SCROLL_OVERSCAN_COUNT}\n scrollBarOptions={{\n width: SCROLL_BAR_WIDTH,\n tapScrollCircleOptions: TAP_SCROLL_CIRCLE_OPTIONS,\n renderThumbOverlay: renderThumbOverlay,\n }}\n behaviorOptions={{\n inertiaOptions: FAST_SCROLL_INERTIA_OPTIONS,\n wheelSpeedMultiplier: FAST_SCROLL_WHEEL_MULTIPLIER,\n }}\n onRangeChange={handleRangeChange}\n onScroll={handleScroll}\n onItemFocus={handleItemFocus}\n contentInsets={{ top: ITEM_VERTICAL_PADDING, bottom: ITEM_VERTICAL_PADDING }}\n className=\"test-daily-report-list relative w-full\">\n {(item) => <ListReportItem key={item.reportHubId} item={item} isActive={item.reportHubId === selectedReportHubId} onSelect={handleItemSelect} />}\n </VirtualScroll>\n </div>\n <DailyReportSidePane dailyReportItems={dailyReportItems} selectedItem={selectedItem} heightStyle={columnHeightStyle} autoMarkRead={!isMobileLayout && autoMarkRead} isActive={selectedReportHubId !== null} userId={userId} />\n </div>\n {itemCount === 0 && (\n <div className=\"py-8 text-center\">\n <p className=\"text-slate-500\">データがありません</p>\n </div>\n )}\n {isOverlayMounted && <DailyReportMobileOverlay isOpen={shouldShowOverlay} selectedItem={selectedItem} onClose={handleOverlayClose} autoMarkRead={isMobileLayout && autoMarkRead} userId={userId} />}\n </>\n )\n}\n\n/**\n * Loading skeleton representation for daily report list items.\n * 日報リストアイテム用読み込み中スケルトン表示。\n */\nconst ListSkeletonItem = () => (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n <div className=\"flex h-full animate-pulse flex-col justify-between rounded-2xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-700 dark:bg-slate-900/70\">\n <div className=\"mb-3 flex flex-wrap items-center gap-3\">\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-slate-200\"></div>\n <div className=\"h-4 w-11/12 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-5/6 rounded bg-slate-200\"></div>\n </div>\n </div>\n </div>\n)\n\ntype DailyReportSidePaneTab = \"article\" | \"relations\"\n\n/**\n * Side pane that exposes quick tabs for the selected report summary, editing, and correlation views.\n * 選択した日報の概要表示・編集・相関ビューを切り替え表示するサイドペイン。\n */\nconst DailyReportSidePane = ({\n dailyReportItems,\n selectedItem,\n heightStyle,\n autoMarkRead = false,\n isActive = true,\n userId,\n}: {\n dailyReportItems?: DailyReportItem[] // Optional for mobile overlay usage\n selectedItem: DailyReportItem | null\n heightStyle?: CSSProperties\n autoMarkRead?: boolean\n isActive?: boolean\n userId?: string | null\n}) => {\n const [activeTab, setActiveTab] = useState<DailyReportSidePaneTab>(\"article\")\n const [isEditing, setIsEditing] = useState(false)\n const { draftLabelName, fieldLabels, sourceTypeConfigs } = useDailyReportConfig()\n const { report, error, isLoading } = useDailyReportDetail(selectedItem?.reportHubId ?? -1, selectedItem?.businessDate ?? null)\n\n const scrollContainerRef = useRef<HTMLDivElement>(null)\n const lastReportIdRef = useRef<number | null>(null)\n // 自動編集モード (ドラフト検出による自動遷移) かどうかを追跡\n const autoEditRef = useRef(false)\n\n // レポート選択時のエディタモード自動制御\n // - 新しいレポート選択: ドラフトならエディタモード、公開済みならビューモード\n // - 同一レポートの SSE 更新: ドラフト→公開遷移時に自動編集のみ解除\n useEffect(() => {\n if (!report) return\n\n const isDraft = report.labels.some((l) => l.name === draftLabelName)\n\n if (report.reportHubId !== lastReportIdRef.current) {\n // 選択レポートが変更された\n lastReportIdRef.current = report.reportHubId\n if (isDraft) {\n setIsEditing(true)\n autoEditRef.current = true\n } else {\n setIsEditing(false)\n autoEditRef.current = false\n }\n } else if (!isDraft && isEditing && autoEditRef.current) {\n // SSE で同一レポートが下書き→公開に遷移: 自動編集モードのみ解除\n // ユーザーがダブルクリックで手動編集中の場合は解除しない\n setIsEditing(false)\n autoEditRef.current = false\n }\n }, [report, isEditing, draftLabelName])\n\n const isMine = !!(userId && report?.userId === userId)\n\n const scrollToBottom = useCallback(() => {\n setTimeout(() => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.scrollTo({ top: scrollContainerRef.current.scrollHeight, behavior: \"smooth\" })\n }\n }, 100)\n }, [])\n\n if (dailyReportItems && (dailyReportItems.length === 0 || !selectedItem)) {\n return (\n <aside className=\"hidden min-h-0 min-w-0 flex-1 gap-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:block dark:border-slate-700 dark:bg-slate-900/70\" style={heightStyle}>\n <div className=\"flex flex-1 items-center justify-center rounded-xl border border-slate-300 border-dashed bg-slate-50 p-4 text-center text-slate-500 text-sm dark:border-slate-600 dark:bg-slate-900/40 dark:text-slate-300\">\n <p>左側の一覧から日報を選択してください。</p>\n </div>\n </aside>\n )\n }\n\n const headerDate = formatToIsoDate(report?.date ?? selectedItem?.businessDate)\n const headerCreator = report?.employeeName || report?.author ? fallbackText(report.employeeName ?? report.author) : \"\"\n const showHeaderSkeleton = isLoading || !report\n\n const content = (\n <>\n <div className=\"space-y-1\">\n <div className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.businessDate}</div>\n <div className=\"text-slate-900 text-sm dark:text-slate-100\">{headerDate}</div>\n <div className=\"flex min-h-4 items-center gap-1 text-slate-500 text-xs dark:text-slate-300\">\n <span>{fieldLabels.author}:</span>\n {showHeaderSkeleton ? <div className=\"h-3 w-24 animate-pulse rounded bg-slate-200 dark:bg-slate-700\" /> : <span>{headerCreator}</span>}\n {(() => {\n const sourceType = report?.sourceType\n const sourceConfig = sourceType ? sourceTypeConfigs?.[sourceType] : undefined\n return sourceConfig ? <span className={cn(\"ml-2 inline-flex shrink-0 items-center rounded px-2 py-0.5 font-semibold text-[10px] tracking-wide\", sourceConfig.badgeClassName)}>{sourceConfig.label}</span> : null\n })()}\n </div>\n </div>\n <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as DailyReportSidePaneTab)} className=\"flex min-h-0 flex-1 flex-col\">\n <TabsList className=\"grid grid-cols-2 gap-1 rounded-md bg-slate-100 p-1 text-xs dark:bg-slate-800\">\n {[\"article\", \"relations\"].map((tab) => (\n <TabsTrigger\n key={tab}\n value={tab}\n className=\"rounded-md px-2 py-1 font-medium text-slate-600 transition-colors data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm dark:text-slate-200 dark:data-[state=active]:bg-slate-900/70 dark:data-[state=active]:text-slate-100\">\n {tab === \"article\" ? fieldLabels.tabArticle : fieldLabels.tabRelations}\n </TabsTrigger>\n ))}\n </TabsList>\n <TabsContent ref={scrollContainerRef} value=\"article\" className=\"scrollbar-thin flex-1 touch-pan-y overflow-y-auto overflow-x-hidden pt-3 text-sm\">\n {isEditing ? (\n report ? (\n <DailyReportEditForm report={report} onCancel={() => setIsEditing(false)} onPublishSuccess={() => setIsEditing(false)} />\n ) : (\n <p className=\"rounded-lg border border-slate-300 border-dashed bg-slate-50 p-4 text-slate-600 dark:border-slate-600 dark:bg-slate-900/40 dark:text-slate-300\">読み込み中...</p>\n )\n ) : (\n selectedItem && (\n <DailyReportDisplayPane\n report={report}\n isLoading={isLoading}\n error={error}\n selectedItem={selectedItem}\n autoMarkRead={autoMarkRead}\n isActive={isActive}\n onCommentAdded={scrollToBottom}\n userId={userId}\n onDoubleClick={isMine ? () => setIsEditing(true) : undefined}\n />\n )\n )}\n </TabsContent>\n <TabsContent value=\"relations\" className=\"scrollbar-thin flex-1 touch-pan-y overflow-y-auto pt-3 text-sm\">\n <p className=\"rounded-lg border border-slate-300 border-dashed bg-slate-50 p-4 text-slate-600 dark:border-slate-600 dark:bg-slate-900/40 dark:text-slate-300\">{fieldLabels.relationsEmpty}</p>\n </TabsContent>\n </Tabs>\n </>\n )\n\n // If rendered as a standalone pane (desktop)\n if (dailyReportItems) {\n return (\n <aside data-report-id={selectedItem?.reportHubId} className=\"test-daily-report-side-pane hidden min-h-0 min-w-0 flex-1 flex-col gap-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:flex dark:border-slate-700 dark:bg-slate-900/70\" style={heightStyle}>\n {content}\n </aside>\n )\n }\n // If rendered inside mobile overlay\n return (\n <div data-report-id={selectedItem?.reportHubId} className=\"test-daily-report-side-pane flex min-h-0 flex-1 flex-col gap-3 overflow-hidden\">\n {content}\n </div>\n )\n}\n\n/**\n * Shows the selected report details in the display tab, including content and metadata.\n * 表示タブ内で選択した日報の詳細とメタデータを表示するコンポーネント。\n */\nconst DailyReportDisplayPane = ({\n report,\n isLoading,\n error,\n autoMarkRead = false,\n isActive = true,\n onCommentAdded,\n userId,\n onDoubleClick,\n}: {\n report: DailyReportDetail | null\n isLoading: boolean\n error: Error | null\n selectedItem: DailyReportItem\n autoMarkRead?: boolean\n isActive?: boolean\n onCommentAdded?: () => void\n userId?: string | null\n onDoubleClick?: () => void\n}) => {\n // 表示中ユーザーはアクションコンテキストから取得する (root loader 非依存)\n const { toggleStar, toggleRead, deleteReport, user } = useDailyReportActionContext()\n const { fieldLabels } = useDailyReportConfig()\n\n // Use custom hook for comments\n const { uiComments, handleAddComment, handleDeleteComment, pendingDeleteId, setPendingDeleteId, resolvedIdMap } = useDailyReportComments(report, user, userId)\n const { handleToggleRead } = useAutoRead(report, isLoading, autoMarkRead, isActive, toggleRead)\n\n // Auto-scroll to bottom when comments are added (e.g. via SSE)\n const prevReportHubIdRef = useRef<number | null>(null)\n const prevCommentsLengthRef = useRef(uiComments.length)\n\n useEffect(() => {\n const currentReportHubId = report?.reportHubId ?? null\n\n // Reset tracking when switching reports\n if (currentReportHubId !== prevReportHubIdRef.current) {\n prevReportHubIdRef.current = currentReportHubId\n prevCommentsLengthRef.current = uiComments.length\n return\n }\n\n // Scroll if comments increased\n if (uiComments.length > prevCommentsLengthRef.current) {\n onCommentAdded?.()\n }\n prevCommentsLengthRef.current = uiComments.length\n }, [uiComments.length, report?.reportHubId, onCommentAdded])\n\n const handleAddCommentWrapper = async (content: string) => {\n await handleAddComment(content)\n }\n\n const displayContent = useMemo(() => {\n if (!report?.content) return null\n return report.content\n .replace(/<br\\s*\\/?\\s*>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\")\n .replace(/<[^>]+>/g, \"\")\n }, [report])\n\n const handleToggleStar = async () => {\n if (!report) return\n const nextValue = !report.isStarred\n try {\n await toggleStar(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n }\n\n const handleDelete = async () => {\n if (!report) return\n if (!confirm(\"本当に削除しますか?\")) return\n try {\n await deleteReport(report.reportHubId, report.date ?? \"\")\n } catch (_e) {\n alert(\"削除に失敗しました\")\n }\n }\n\n if (error) return <p className=\"text-red-500\">{error.message}</p>\n\n const showSkeleton = !report\n const isStarred = report?.isStarred ?? false\n const isRead = report?.isRead ?? false\n const isMine = !!(userId && report?.userId === userId)\n const hasComments = uiComments.length > 0\n\n const SkeletonText = ({ className = \"w-32\" }: { className?: string }) => <div className={`h-4 animate-pulse rounded bg-slate-200 dark:bg-slate-700 ${className}`} />\n\n return (\n // biome-ignore lint/a11y/noStaticElementInteractions: Double click to edit feature\n <div className=\"relative space-y-4 text-slate-700 dark:text-slate-200\" onDoubleClick={onDoubleClick}>\n {!showSkeleton && report && (\n <div className=\"absolute top-0 right-2 z-10 flex items-center justify-end gap-3\">\n <button type=\"button\" onClick={() => handleToggleRead(false)} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-110\" title={isRead ? \"未読にする\" : \"既読にする\"} data-detail-read-button={report.reportHubId}>\n {isRead ? <span className=\"text-slate-300 hover:text-blue-500\">✓</span> : <UnreadIndicator className=\"hover:text-blue-600\" />}\n </button>\n <button type=\"button\" onClick={handleToggleStar} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-110\" title={isStarred ? \"スターを外す\" : \"スターを付ける\"} data-detail-star-button={report.reportHubId}>\n {isStarred ? <span className=\"text-yellow-400\">★</span> : <span className=\"text-slate-300 hover:text-yellow-400\">☆</span>}\n </button>\n {isMine && (\n <button type=\"button\" onClick={handleDelete} data-detail-delete-button={report.reportHubId} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-110\" title=\"削除する\">\n <Trash2 className=\"h-4 w-4 text-slate-300 hover:text-red-500\" />\n </button>\n )}\n </div>\n )}\n\n <div className=\"flex items-baseline\">\n <span className=\"w-24 shrink-0 font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.subject}</span>\n <div className=\"flex-1 pr-20\">{showSkeleton ? <SkeletonText className=\"w-3/4\" /> : <span className=\"fade-in animate-in text-sm duration-500\">{fallbackText(report?.subject)}</span>}</div>\n </div>\n <div className=\"flex items-baseline\">\n <span className=\"w-24 shrink-0 font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.customer}</span>\n <div className=\"flex-1\">{showSkeleton ? <SkeletonText className=\"w-1/2\" /> : <span className=\"fade-in animate-in text-sm duration-500\">{fallbackText(report?.customerName)}</span>}</div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.content}</div>\n <div className=\"min-h-22 rounded-lg bg-slate-50 p-3 text-sm leading-relaxed dark:bg-slate-800/40\">\n {showSkeleton ? (\n <div className=\"space-y-2\">\n <SkeletonText className=\"w-full\" />\n <SkeletonText className=\"w-11/12\" />\n <SkeletonText className=\"w-4/5\" />\n </div>\n ) : displayContent ? (\n <div className=\"fade-in animate-in whitespace-pre-wrap duration-500\">{displayContent}</div>\n ) : (\n <p className=\"fade-in animate-in text-slate-500 duration-500 dark:text-slate-400\">内容は登録されていません。</p>\n )}\n </div>\n </div>\n <div className=\"flex items-baseline\">\n <span className=\"w-24 shrink-0 font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.visitTime}</span>\n <div className=\"flex-1\">\n {showSkeleton ? (\n <SkeletonText className=\"w-1/3\" />\n ) : (\n <span className=\"fade-in animate-in text-sm duration-500\">\n {fallbackText(report?.visitTimeFrom)} {report?.visitTimeFrom && report?.visitTimeTo ? \"-\" : \"\"} {fallbackText(report?.visitTimeTo)}\n </span>\n )}\n </div>\n </div>\n {report && (\n <div className=\"fade-in animate-in space-y-2 border-slate-100 border-t pt-4 duration-500 dark:border-slate-800\">\n <div className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.comments}</div>\n {hasComments && (\n <DailyReportCommentList\n comments={uiComments}\n reportHubId={report.reportHubId}\n businessDate={report.date ?? \"\"}\n onDeleteComment={handleDeleteComment}\n pendingDeleteId={pendingDeleteId}\n onPendingDeleteIdChange={setPendingDeleteId}\n resolvedIdMap={resolvedIdMap}\n />\n )}\n <DailyReportCommentForm reportHubId={report.reportHubId} businessDate={report.date ?? \"\"} onAddComment={handleAddCommentWrapper} />\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Mobile-only overlay that slides in with the selected daily report details.\n * 選択された日報の詳細をモバイル向けにスライド表示し選択ガードを切り替え可能なオーバーレイ。\n */\nconst DailyReportMobileOverlay = ({ isOpen, selectedItem, onClose, autoMarkRead = false, userId }: { isOpen: boolean; selectedItem: DailyReportItem | null; onClose: () => void; autoMarkRead?: boolean; userId?: string | null }) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const closeButtonRef = useRef<HTMLButtonElement | null>(null)\n\n const { isBlocked: isGuardBlocked, requestBlock, reset: resetGuard } = useSwipeGuardState()\n\n const { report, error, isLoading } = useDailyReportDetail(selectedItem?.reportHubId ?? -1, selectedItem?.businessDate ?? null)\n const displayData = useTransitionDisplay(isOpen, selectedItem ? { item: selectedItem, detail: { report, error, isLoading } } : null)\n const displayItem = displayData?.item ?? null\n const _displayDetail = displayData?.detail ?? EMPTY_DETAIL\n _displayDetail\n const { overlayRef, overlayStyle, attemptClose, isSwiping } = useSwipeToDismissOverlay({\n isOpen,\n onClose,\n isCloseBlocked: useCallback(() => isGuardBlocked, [isGuardBlocked]),\n skipWhenTextSelected: true,\n })\n\n const tapHandlers = useTapClick(() => attemptClose())\n\n /**\n * Manages focus and guard state when overlay opens/closes.\n * オーバーレイの開閉時にフォーカスとガード状態を管理する副作用。\n *\n * Purpose: To set focus on close button for accessibility and reset swipe guard.\n * Dependencies: [isOpen, resetGuard]\n * Cleanup: None.\n */\n useEffect(() => {\n if (isOpen) {\n requestAnimationFrame(() => closeButtonRef.current?.focus())\n } else {\n if (containerRef.current?.contains(document.activeElement)) {\n ;(document.activeElement as HTMLElement).blur()\n }\n resetGuard()\n }\n }, [isOpen, resetGuard])\n\n if (!displayItem) return null\n\n return (\n <div ref={containerRef} className={`fixed inset-0 z-50 flex touch-manipulation transition-[visibility] duration-200 sm:hidden ${isOpen ? \"visible\" : \"invisible\"} ${isOpen && !isSwiping ? \"pointer-events-auto\" : \"pointer-events-none\"}`} aria-hidden={!isOpen}>\n <button type=\"button\" aria-label=\"背景をタップして閉じる\" className={`absolute inset-0 touch-manipulation bg-slate-900/50 transition-opacity duration-200 focus:outline-none ${isOpen ? \"opacity-100\" : \"opacity-0\"}`} {...tapHandlers}></button>\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"日報詳細\"\n className={`relative ml-auto flex h-full w-full max-w-full transform touch-none select-none bg-white shadow-xl transition-transform duration-200 ease-out dark:bg-slate-900/95 ${isOpen ? \"pointer-events-auto\" : \"pointer-events-none\"}`}\n ref={overlayRef}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") {\n e.preventDefault()\n attemptClose()\n }\n }}\n style={overlayStyle}>\n <div className=\"flex h-full w-full flex-col gap-3 p-4\">\n <SwipeCloseGuardContext.Provider value={{ isBlocked: isGuardBlocked, requestBlock }}>\n <DailyReportSidePane selectedItem={displayItem} autoMarkRead={autoMarkRead} isActive={isOpen} userId={userId} />\n </SwipeCloseGuardContext.Provider>\n <div className=\"mt-2 flex justify-start\">\n <button\n type=\"button\"\n ref={closeButtonRef}\n data-overlay-interactive=\"true\"\n className=\"touch-manipulation rounded-full border border-slate-300 px-4 py-2 font-medium text-slate-600 text-sm shadow-sm transition-colors hover:bg-slate-100 dark:border-slate-600 dark:text-slate-200 dark:hover:bg-slate-800\"\n onClick={() => attemptClose()}>\n 閉じる\n </button>\n </div>\n </div>\n </aside>\n </div>\n )\n}\n\n/**\n * Up to three non-empty plain-text lines extracted from HTML content.\n * HTML コンテンツから抽出する最大三つの非空プレーンテキスト行。\n */\nconst extractPreviewLines = (content: string | null): string[] => {\n if (!content) return []\n return content\n .replace(/<br\\s*\\/?\\s*>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\")\n .replace(/<[^>]+>/g, \"\")\n .split(/\\r?\\n/)\n .map((l) => l.replace(/\\u3000/g, \" \").trim())\n .filter((l) => l.length > 0)\n .slice(0, MAX_PREVIEW_LINES)\n}\n\n/**\n * Daily report summary card rendered inside the list tab.\n * List タブ内で表示する日報サマリーカード。\n */\nconst ListReportItem = ({ item, isActive, onSelect }: { item: DailyReportItem; isActive: boolean; onSelect: (id: number) => void }) => {\n const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)\n const { toggleStar, toggleRead, removeStaleItem } = useDailyReportActionContext()\n const { sourceTypeConfigs } = useDailyReportConfig()\n\n // フェッチが一度でも試行されたかを追跡する ref\n const hasEverFetched = useRef(false)\n if (isRefetching) hasEverFetched.current = true\n\n // フェッチ完了後に report が null → stale アイテムとしてリストから除去する安全弁\n // 外部リソース同期: useDailyReportDetail のフェッチ結果に反応する\n useEffect(() => {\n if (!(report || isLoading || isRefetching || error) && hasEverFetched.current && item.reportHubId > 0) {\n removeStaleItem(item.reportHubId)\n }\n }, [report, isLoading, isRefetching, error, item.reportHubId, removeStaleItem])\n\n const tapHandlers = useTapClick(() => onSelect(item.reportHubId))\n\n if (error) {\n return (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n <div className=\"flex h-full items-center justify-center rounded-2xl border border-red-200 bg-red-50 p-4 text-red-700 text-sm\">\n <p>日報 {item.reportHubId.toLocaleString()} の概要読み込みに失敗しました。</p>\n </div>\n </div>\n )\n }\n\n if (isLoading) return <ListSkeletonItem />\n\n if (!report) {\n return (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n <div className=\"flex h-full items-center justify-center rounded-2xl border border-slate-200 bg-slate-50 p-4 text-slate-500 text-sm dark:border-slate-700 dark:bg-slate-900/40 dark:text-slate-400\">\n <p>日報が見つかりません</p>\n </div>\n </div>\n )\n }\n\n const previewLines = extractPreviewLines(report.content).map((line, i) => ({\n line,\n key: `${item.reportHubId}-line-${i}`,\n }))\n const creator = fallbackText(report.employeeName ?? report.author)\n const date = formatToIsoDate(report.date)\n\n const handleToggleStar = async (e: React.MouseEvent) => {\n e.stopPropagation()\n const nextValue = !report.isStarred\n try {\n await toggleStar(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n }\n\n const handleToggleRead = async (e: React.MouseEvent) => {\n e.stopPropagation()\n const nextValue = !report.isRead\n try {\n await toggleRead(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n }\n\n return (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n {/* biome-ignore lint/a11y/useSemanticElements: Card contains nested interactive elements (Star/Read buttons) */}\n <div\n data-daily-report-button={item.reportHubId}\n className={`flex h-full w-full in-[.is-scrolling]:transform-none cursor-pointer select-none flex-col rounded-2xl border in-[.is-scrolling]:border-slate-200 bg-white p-4 text-left in-[.is-scrolling]:shadow-sm shadow-sm in-[.is-scrolling]:transition-none transition-all duration-200 hover:-translate-y-0.5 hover:border-blue-300 hover:shadow-lg dark:bg-slate-900/70 ${\n isActive ? \"border-blue-300 shadow-md ring-2 ring-blue-200 dark:border-blue-500/70 dark:ring-blue-500/40\" : \"border-slate-200 dark:border-slate-700\"\n }`}\n style={{ touchAction: \"manipulation\" }}\n role=\"button\"\n tabIndex={0}\n aria-pressed={isActive}\n {...tapHandlers}>\n <div className=\"mb-3 flex items-center gap-3 text-slate-600 text-sm\">\n <span className=\"inline-flex shrink-0 items-center rounded-full bg-slate-100 px-3 py-1 font-semibold text-xs dark:bg-slate-800 dark:text-slate-200\">\n <span className=\"whitespace-nowrap font-medium text-slate-900 dark:text-slate-100\">{date}</span>\n </span>\n <span className=\"inline-flex min-w-0 max-w-full items-center rounded-full bg-slate-100 px-3 py-1 font-semibold text-xs dark:bg-slate-800 dark:text-slate-200\">\n <span className=\"truncate font-medium text-slate-900 dark:text-slate-100\">{creator}</span>\n </span>\n <div className=\"ml-auto flex items-center gap-1.5\">\n {(() => {\n const sourceType = report?.sourceType\n const sourceConfig = sourceType ? sourceTypeConfigs?.[sourceType] : undefined\n return sourceConfig ? <span className={cn(\"inline-flex shrink-0 items-center rounded px-2 py-0.5 font-semibold text-[10px] tracking-wide\", sourceConfig.badgeClassName)}>{sourceConfig.label}</span> : null\n })()}\n <button\n type=\"button\"\n title={report.isStarred ? \"スターを外す\" : \"スターを付ける\"}\n data-list-star-button={item.reportHubId}\n onClick={handleToggleStar}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 dark:hover:bg-slate-700\">\n {report.isStarred ? <Star className=\"h-3.5 w-3.5 fill-yellow-400 text-yellow-400\" /> : <Star className=\"h-3.5 w-3.5 text-slate-300 dark:text-slate-500\" />}\n </button>\n <button\n type=\"button\"\n title={report.isRead ? \"未読にする\" : \"既読にする\"}\n data-list-read-button={item.reportHubId}\n onClick={handleToggleRead}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 dark:hover:bg-slate-700\">\n {report.isRead ? <Check className=\"h-3.5 w-3.5 text-blue-500\" /> : <UnreadIndicator className=\"text-xs\" />}\n </button>\n </div>\n </div>\n <div className=\"space-y-1 text-slate-700 text-sm leading-relaxed dark:text-slate-200\">\n {previewLines.length > 0 ? (\n previewLines.map(({ line, key }) => (\n <p key={key} className=\"wrap-break-word line-clamp-1\">\n {line}\n </p>\n ))\n ) : (\n <p>内容は登録されていません</p>\n )}\n </div>\n </div>\n </div>\n )\n}\n","import * as TabsPrimitive from \"@radix-ui/react-tabs\"\nimport * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nconst Tabs = TabsPrimitive.Root\n\nconst TabsList = React.forwardRef<React.ElementRef<typeof TabsPrimitive.List>, React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>>(({ className, ...props }, ref) => (\n <TabsPrimitive.List ref={ref} className={cn(\"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground\", className)} {...props} />\n))\nTabsList.displayName = TabsPrimitive.List.displayName\n\nconst TabsTrigger = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Trigger>, React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 font-medium text-sm ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-2xs\",\n className,\n )}\n {...props}\n />\n))\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName\n\n// https://github.com/radix-ui/primitives/issues/2359\nconst TabsContent = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Content>, React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n // forceMount={true} //<=======Add this line\n className={cn(\n \"data-[state=inactive]:hidden\", //<=========Add this line\n \"mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n className,\n )}\n {...props}\n />\n))\nTabsContent.displayName = TabsPrimitive.Content.displayName\n\nexport { Tabs, TabsContent, TabsList, TabsTrigger }\n","/**\n * Top-level daily-report page content (chrome-agnostic).\n * アプリのレイアウトに依存しない日報ページ本体コンポーネント。\n *\n * アプリ側のルートは本コンポーネントを自前のレイアウト (Main 等) で包み、\n * `config` でヘッダー描画・パス・ラベル名を注入する。\n */\nimport { Suspense, useCallback, useEffect, useState } from \"react\"\nimport { Await, useRevalidator } from \"react-router\"\nimport type { DailyReportItem, DailyReportUser } from \"../../shared/types\"\nimport { type DailyReportClientConfigInput, DailyReportConfigProvider, useDailyReportConfig } from \"../config-context\"\nimport { DailyReportActionProvider } from \"../contexts/daily-report-action-context\"\nimport { DAILY_REPORT_FETCH_DEBOUNCE_MS } from \"../utils/constants\"\nimport { DailyReportResolvedContent } from \"./daily-report-resolved-content\"\n\n/**\n * Synchronizes resolved daily report ids into the action context provider state.\n * Also renders the resolved content after synchronization trigger is set.\n *\n * 解決済みの日報 ID を Action Context 側へ同期し、解決済みコンテンツを描画するコンポーネント。\n */\nconst DailyReportResolvedSync = ({ dailyReportItems, userId, onResolved }: { dailyReportItems: DailyReportItem[]; userId?: string | null; onResolved: (items: DailyReportItem[]) => void }) => {\n /**\n * Notifies parent when resolved items change.\n * Dependencies:\n * - dailyReportItems: latest resolved list from Await.\n * - onResolved: parent synchronization callback.\n * Cleanup:\n * - None (pure synchronization side effect).\n *\n * 解決済み items が変化したときに親へ通知します。\n * 依存配列:\n * - dailyReportItems: Await で解決された最新リスト\n * - onResolved: 親への同期コールバック\n * クリーンアップ:\n * - なし(同期のみの副作用)\n */\n useEffect(() => {\n onResolved(dailyReportItems)\n }, [dailyReportItems, onResolved])\n\n return <DailyReportResolvedContent dailyReportItems={dailyReportItems} userId={userId} />\n}\n\n/**\n * Error display component for daily report data fetch failures.\n * 日報データの取得失敗時に表示するエラーコンポーネント。\n */\nexport const DailyReportLoadError = () => {\n const { title, renderHeader } = useDailyReportConfig()\n const { revalidate } = useRevalidator()\n return (\n <>\n {renderHeader({ title, annotation: <span className=\"font-bold text-red-500\">Error</span> })}\n <div className=\"h-full bg-slate-50 p-2\">\n <div className=\"mx-auto flex h-full w-full max-w-6xl flex-1 flex-col gap-6\">\n <div className=\"flex h-full flex-1 flex-col items-center justify-center gap-4 rounded-lg border border-red-200 bg-white p-4 text-slate-500 shadow-sm dark:border-red-900 dark:bg-slate-900/70 dark:text-slate-400\">\n <p className=\"font-medium text-red-600\">日報データの読み込みに失敗しました。</p>\n <button type=\"button\" onClick={() => revalidate()} className=\"rounded bg-slate-100 px-4 py-2 text-slate-700 text-sm hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700\">\n 再読み込み\n </button>\n </div>\n </div>\n </div>\n </>\n )\n}\n\n/**\n * Suspense fallback while daily report identifiers resolve.\n * 日報 ID 解決中のサスペンスフォールバック。\n */\nexport const DailyReportLoading = () => {\n const { title, renderHeader } = useDailyReportConfig()\n return (\n <>\n {renderHeader({\n title,\n annotation: (\n <div className=\"flex flex-wrap gap-2 leading-none\">\n <span>総件数: 読み込み中...</span>\n <span>デバウンス: {DAILY_REPORT_FETCH_DEBOUNCE_MS} ms</span>\n </div>\n ),\n })}\n <div className=\"h-full bg-slate-50 p-2\">\n <div className=\"mx-auto flex h-full w-full max-w-6xl flex-1 flex-col gap-6\">\n <div className=\"flex h-full flex-1 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-500 shadow-sm dark:border-slate-700 dark:bg-slate-900/70 dark:text-slate-300\">日報 ID を読み込み中です...</div>\n </div>\n </div>\n </>\n )\n}\n\nexport type DailyReportPageProps = {\n /** 表示中ユーザー (楽観的コメントの作者名等に使用)。 */\n user?: DailyReportUser\n /** 難読化済みの内部ユーザー ID (未登録ユーザーは null)。 */\n userId?: string | null\n /** clientLoader で defer した日報 ID 一覧 (Promise) または解決済み配列。 */\n dailyReportIds?: Promise<DailyReportItem[]> | DailyReportItem[] | null\n /** クライアント設定の部分上書き (ヘッダー描画・パス・ラベル名・フィールドラベルなど)。 */\n config?: DailyReportClientConfigInput\n}\n\n/**\n * Daily report page content rendering provider, suspense boundary, and resolved views.\n * プロバイダー・サスペンス境界・解決済みビューを描画する日報ページ本体。\n */\nconst DailyReportPageInner = ({ user, userId, dailyReportIds }: Omit<DailyReportPageProps, \"config\">) => {\n const [resolvedInitialItems, setResolvedInitialItems] = useState<DailyReportItem[]>([])\n\n const handleResolvedItems = useCallback((items: DailyReportItem[]) => {\n setResolvedInitialItems(items)\n }, [])\n\n return (\n <DailyReportActionProvider user={user} initialItems={resolvedInitialItems} userId={userId}>\n <Suspense fallback={<DailyReportLoading />}>\n {/* Resolve で非同期取得済み ID を確定表示 */}\n {dailyReportIds ? (\n <Await resolve={dailyReportIds} errorElement={<DailyReportLoadError />}>\n {(resolvedItems) => {\n if (!resolvedItems) {\n throw new Error(\"Daily report ids resolve must not be null\")\n }\n // 非 null を保証して以降の処理で利用\n return <DailyReportResolvedSync dailyReportItems={resolvedItems as DailyReportItem[]} userId={userId} onResolved={handleResolvedItems} />\n }}\n </Await>\n ) : (\n <DailyReportLoading />\n )}\n </Suspense>\n </DailyReportActionProvider>\n )\n}\n\n/**\n * Chrome-agnostic daily report page with client configuration provider.\n * 設定プロバイダー込みの日報ページコンポーネント。\n */\nexport const DailyReportPage = ({ config, ...rest }: DailyReportPageProps) => (\n <DailyReportConfigProvider config={config}>\n <DailyReportPageInner {...rest} />\n </DailyReportConfigProvider>\n)\n","import { Plus } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useEffect, useMemo, useState } from \"react\"\nimport { useSearchParams } from \"react-router\"\nimport type { DailyReportItem } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { Button } from \"../ui/button\"\nimport { Label } from \"../ui/label\"\nimport { Switch } from \"../ui/switch\"\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\"\nimport { DAILY_REPORT_FETCH_DEBOUNCE_MS } from \"../utils/constants\"\nimport { DailyReportDetailList } from \"./daily-report-detail-list\"\nimport { DailyReportList } from \"./daily-report-list\"\n\nconst tabTriggerClass = \"min-w-24 p-2 font-medium text-slate-600 text-xs transition-colors data-[state=active]:bg-white data-[state=active]:text-slate-900 sm:min-w-36 sm:text-sm dark:text-slate-300 dark:data-[state=active]:bg-slate-800 dark:data-[state=active]:text-slate-100\"\n\ntype DailyReportResolvedContentProps = {\n dailyReportItems: DailyReportItem[]\n userId?: string | null\n}\n\n/**\n * Displays fetched daily reports with layout and tabbed virtual scroll views.\n * レイアウトとタブ付き仮想スクロールで取得済み日報を表示するコンポーネント。\n */\nexport const DailyReportResolvedContent = ({ dailyReportItems: _initialItems, userId }: DailyReportResolvedContentProps) => {\n // 自動既読設定\n const [autoMarkRead, setAutoMarkRead] = useState(false)\n const { title, renderHeader, showDevControls } = useDailyReportConfig()\n const { isSseEnabled, toggleSse, createReport, items: dailyReportItems } = useDailyReportActionContext()\n const [searchParams] = useSearchParams()\n\n // 選択状態の管理 (タブ間で共有)\n const [selectedItemId, setSelectedItemId] = useState<number | null>(() => {\n if (dailyReportItems.length === 0) return null\n return dailyReportItems[0].reportHubId\n })\n\n const handleCreateReport = async () => {\n const businessDateParam = searchParams.get(\"businessDate\")\n const today = new Date().toISOString().split(\"T\")[0]\n const dateToUse = businessDateParam || today\n const newId = await createReport(dateToUse)\n setSelectedItemId(newId)\n }\n\n /**\n * Effect to load the auto-read setting from local storage on mount.\n * マウント時にローカルストレージから自動既読設定を読み込む副作用。\n *\n * Purpose: To persist user preference for auto-read behavior.\n * Dependencies: [] - Runs only on mount.\n * Cleanup: None.\n */\n useEffect(() => {\n const stored = localStorage.getItem(\"daily-report-auto-read\")\n if (stored === \"true\") {\n setAutoMarkRead(true)\n }\n }, [])\n\n const handleAutoMarkReadChange = (checked: boolean) => {\n setAutoMarkRead(checked)\n localStorage.setItem(\"daily-report-auto-read\", String(checked))\n }\n\n const tabItems = useMemo<{ value: string; label: string; content: ReactNode }[]>(\n () => [\n {\n value: \"list\",\n label: \"📄 List\",\n content: <DailyReportList dailyReportItems={dailyReportItems} autoMarkRead={autoMarkRead} selectedReportHubId={selectedItemId} onSelectItem={setSelectedItemId} userId={userId} />,\n },\n {\n value: \"tree\",\n label: \"🌳 Tree\",\n content: <div className=\"flex flex-1 items-center justify-center rounded-lg border border-slate-300 border-dashed bg-white p-6 text-slate-500 transition-colors dark:border-slate-700 dark:bg-slate-900/70 dark:text-slate-300\">Tree ビューは現在準備中です</div>,\n },\n {\n value: \"detailList\",\n label: \"📋 DetailList\",\n content: <DailyReportDetailList dailyReportItems={dailyReportItems} userId={userId} selectedItemId={selectedItemId} onSelectItem={setSelectedItemId} />,\n },\n ],\n [dailyReportItems, autoMarkRead, selectedItemId, userId],\n )\n\n return (\n <>\n {renderHeader({\n title,\n annotation: (\n <div className=\"flex flex-wrap gap-2 leading-1\">\n <span>総件数: {dailyReportItems.length.toLocaleString()} 件</span>\n <span>デバウンス: {DAILY_REPORT_FETCH_DEBOUNCE_MS} ms</span>\n </div>\n ),\n })}\n <div className=\"h-full bg-slate-50 dark:bg-slate-500\">\n <div className=\"flex h-full w-full max-w-6xl flex-1 flex-col p-1\">\n <Tabs defaultValue=\"list\" className=\"flex h-full flex-1 flex-col\">\n <div className=\"flex items-center justify-between gap-2 rounded-md bg-slate-50 p-1 transition-colors dark:bg-slate-900/70 dark:text-slate-200\">\n <TabsList className=\"scrollbar-none flex w-full touch-pan-x flex-nowrap overflow-x-auto overflow-y-hidden bg-transparent p-0\">\n {tabItems.map(({ value, label }) => (\n <TabsTrigger key={value} value={value} className={tabTriggerClass}>\n {label}\n </TabsTrigger>\n ))}\n </TabsList>\n <div className=\"flex shrink-0 items-center space-x-1 px-2\">\n <Button variant=\"ghost\" size=\"icon\" onClick={handleCreateReport} title=\"日報を作成\">\n <Plus className=\"h-4 w-4\" />\n </Button>\n {showDevControls && (\n <div className=\"hidden items-center space-x-1 sm:flex\">\n <Switch id=\"sse-mode\" checked={isSseEnabled} onCheckedChange={toggleSse} className=\"scale-75\" />\n <Label htmlFor=\"sse-mode\" className=\"cursor-pointer text-slate-500 text-xs\">\n 購読\n </Label>\n </div>\n )}\n <Switch id=\"auto-read-mode\" checked={autoMarkRead} onCheckedChange={handleAutoMarkReadChange} className=\"scale-75\" />\n <Label htmlFor=\"auto-read-mode\" className=\"cursor-pointer text-slate-500 text-xs\">\n 自動既読\n </Label>\n </div>\n </div>\n {tabItems.map(({ value, content }) => (\n <TabsContent key={value} value={value} className=\"mt-0 flex h-full min-h-0 flex-1 flex-col\">\n {content}\n </TabsContent>\n ))}\n </Tabs>\n </div>\n </div>\n </>\n )\n}\n","import * as SwitchPrimitives from \"@radix-ui/react-switch\"\nimport * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nconst Switch = React.forwardRef<React.ElementRef<typeof SwitchPrimitives.Root>, React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>>(({ className, ...props }, ref) => (\n <SwitchPrimitives.Root\n className={cn(\n \"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n className,\n )}\n {...props}\n ref={ref}>\n <SwitchPrimitives.Thumb className={cn(\"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\")} />\n </SwitchPrimitives.Root>\n))\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n","/**\n * React Router route helpers for the daily-report page (revalidation policy / client loader).\n * 日報ページ向け React Router ルートヘルパー (再検証ポリシー / クライアントローダー)。\n */\nimport type { ShouldRevalidateFunction, ShouldRevalidateFunctionArgs } from \"react-router\"\nimport type { DailyReportItem } from \"../shared/types\"\nimport { defaultDailyReportClientConfig } from \"./config-context\"\n\n/**\n * Prevents loader revalidation when POP navigations keep identical URL state.\n * URL 状態が同一の POP ナビゲーション時にローダー再検証を抑止。\n */\nexport const dailyReportShouldRevalidate: ShouldRevalidateFunction = ({ actionResult, actionStatus, currentUrl, defaultShouldRevalidate, formAction, formData, formEncType, formMethod, json, nextUrl, text }: ShouldRevalidateFunctionArgs) => {\n if (!defaultShouldRevalidate) {\n return false\n }\n\n const isSameUrl = currentUrl.pathname === nextUrl.pathname && currentUrl.search === nextUrl.search && currentUrl.hash === nextUrl.hash\n if (!isSameUrl) {\n return defaultShouldRevalidate\n }\n\n if (formData) {\n const intent = formData.get(\"intent\")\n if (intent === \"toggleRead\" || intent === \"toggleStar\" || intent === \"addComment\" || intent === \"deleteComment\" || intent === \"create\" || intent === \"update\" || intent === \"publish\" || intent === \"delete\") {\n return false\n }\n }\n\n const hasSubmission = actionResult !== undefined || actionStatus !== undefined || formAction !== undefined || formData !== undefined || formEncType !== undefined || formMethod !== undefined || json !== undefined || text !== undefined\n return hasSubmission ? defaultShouldRevalidate : false\n}\n\n/**\n * Fetches the deferred daily report id list from the API endpoint.\n * API エンドポイントから日報 ID 一覧を遅延取得する処理。\n */\nexport const fetchDailyReportIds = (apiBasePath: string = defaultDailyReportClientConfig.apiBasePath): Promise<DailyReportItem[]> =>\n fetch(`${apiBasePath}/ids`, {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" },\n cache: \"no-cache\",\n })\n .then(async (response) => {\n if (!response.ok) {\n throw new Error(`Failed to fetch daily report ids: ${response.status} ${response.statusText}`)\n }\n return response\n })\n .then(async (response) => response.json())\n .then((data) => {\n if (!(data.ids && Array.isArray(data.ids))) {\n throw new Error(\"Invalid daily report ids response\")\n }\n return data.ids as DailyReportItem[]\n })\n\n/**\n * Creates a React Router clientLoader that merges server data with deferred report ids.\n * サーバーデータへ遅延日報 ID を合成する React Router clientLoader を生成する処理。\n *\n * 返す関数へ `hydrate = true as const` を付与済み (初期ハイドレーションでも実行される)。\n */\nexport const createDailyReportClientLoader = <TServerData extends Record<string, unknown>>(options?: { apiBasePath?: string }) => {\n const clientLoader = async (args: { serverLoader: () => Promise<unknown> }): Promise<TServerData & { dailyReportIds: Promise<DailyReportItem[]> }> => {\n // サーバーローダーの解決を待つ間に ID 取得を並行開始する\n const idsPromise = fetchDailyReportIds(options?.apiBasePath)\n const serverData = (await args.serverLoader()) as TServerData\n return {\n ...serverData,\n dailyReportIds: idsPromise,\n }\n }\n clientLoader.hydrate = true as const\n return clientLoader\n}\n"],"mappings":"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","names":["client_exports","__export","BusinessDayThumbOverlay","DAILY_REPORT_FETCH_DEBOUNCE_MS","DEFAULT_ITEM_HEIGHT","DailyReportActionProvider","DailyReportCommentForm","DailyReportCommentItem","DailyReportCommentList","DailyReportConfigProvider","DailyReportDetailList","DailyReportEditForm","DailyReportList","DailyReportLoadError","DailyReportLoading","DailyReportPage","DailyReportResolvedContent","EMPTY_DETAIL","EMPTY_ITEM","FAST_SCROLL_INERTIA_OPTIONS","FAST_SCROLL_WHEEL_MULTIPLIER","ITEM_CONTAINER_HEIGHT","ITEM_VERTICAL_PADDING","MAX_PREVIEW_LINES","PREFETCH_LOOKAHEAD","PREFETCH_MAX_DATES","SCROLL_BAR_WIDTH","TAP_SCROLL_CIRCLE_OPTIONS","UnreadIndicator","VIRTUAL_SCROLL_OVERSCAN_COUNT","WHEEL_RESET_TIMEOUT_MS","acquireMutationLock","applyDailyReportServerUpdates","createDailyReportClientLoader","dailyReportShouldRevalidate","defaultDailyReportClientConfig","defaultDailyReportFieldLabels","deleteDailyReportCache","fetchDailyReportIds","getCachedReport","registerRecentDeletion","releaseMutationLock","subscribeCacheReady","updateDailyReportCache","useDailyReportActionContext","useDailyReportComments","useDailyReportConfig","useDailyReportDetail","useDailyReportPrefetch","useDailyReportSseConnection","useDynamicViewportHeight","writeCache","__toCommonJS","import_jsx_runtime","BusinessDayThumbOverlay","orientation","thumbCenter","label","isDragging","isTapScrollActive","isWheelScrollActive","import_outline","import_react","mergeComments","legacyComments","modernComments","currentUserId","legacyUIComments","c","index","modernUIComments","resolveCommentColorClass","color","normalized","fallbackText","value","trimmed","formatToIsoDate","date","year","month","day","hours","minutes","seconds","import_clsx","import_tailwind_merge","cn","inputs","import_jsx_runtime","checkIsPendingDelete","commentId","pendingDeleteId","resolvedIdMap","DailyReportCommentItem","comment","_reportHubId","_businessDate","onOptimisticDelete","onPendingDeleteIdChange","onTrashClick","isDeleted","setIsDeleted","isPendingDelete","handleConfirmDelete","e","cn","resolveCommentColorClass","fallbackText","formatToIsoDate","import_react","import_jsx_runtime","useAutoResetPendingDelete","pendingDeleteId","onPendingDeleteIdChange","timerRef","handleGlobalClick","e","commentId","DailyReportCommentList","comments","reportHubId","businessDate","onDeleteComment","resolvedIdMap","handleTrashClick","comment","DailyReportCommentItem","DailyReportCommentForm","onAddComment","isSubmitting","setIsSubmitting","formRef","content","input","import_virtualscroll","import_lucide_react","import_react","import_react","import_jsx_runtime","defaultDailyReportFieldLabels","defaultDailyReportClientConfig","title","annotation","DailyReportConfigContext","DailyReportConfigProvider","config","children","merged","useDailyReportConfig","import_react","createLogger","level","prefix","impl","format","message","rest","import_react","ISO_DATE_PATTERN","SLASH_DATE_PATTERN","zeroPad","value","normalizeFromDate","year","month","day","normalizeBusinessDateKey","trimmed","parsed","logger","createLogger","getGlobal","k","d","v","TTL","REVIVE_MS","FETCH_DEBOUNCE_MS","DailyReportCache","now","GC_THRESHOLD","id","entry","key","ex","l","ignoreExpiry","e","dateKey","report","expiresAt","syncList","normalizeBusinessDateKey","listEntry","data","r","updates","strict","date","list","serverReport","cachedReport","c","serverIds","missing","b","reports","fetchStart","t","processed","lastMut","cached","merged","writeCache","exp","sync","getCachedReport","opts","updateDailyReportCache","u","deleteDailyReportCache","acquireMutationLock","releaseMutationLock","registerRecentDeletion","cId","subscribeCacheReady","reportHubId","callback","listener","applyDailyReportServerUpdates","_reportHubIdd","fetchReportById","apiBasePath","pending","start","p","res","fetchReportsByDate","useDailyReportDetail","businessDate","useDailyReportConfig","setReport","error","setError","isLoading","setIsLoading","isRefetching","setIsRefetching","reqRef","onUpdate","uid","token","isStale","found","timer","item","useDailyReportPrefetch","import_react","import_zod","dailyReportInterviewerSchema","dailyReportCommentSchema","dailyReportLabelDefSchema","dailyReportCommentItemSchema","dailyReportDetailSchema","connectedMessageSchema","statusUpdateMessageSchema","commentAddMessageSchema","commentDeleteMessageSchema","reportCreateMessageSchema","reportUpdateMessageSchema","reportPublishMessageSchema","reportDeleteMessageSchema","dailyReportSseMessageSchema","logger","createLogger","useDailyReportSseConnection","isSseEnabled","onMessage","ssePath","useDailyReportConfig","eventSource","reconnectTimer","reconnectAttempt","lastReceivedEventId","connect","event","raw","result","dailyReportSseMessageSchema","e","delayMs","import_jsx_runtime","logger","createLogger","DailyReportActionContext","useDailyReportActionContext","context","getUserDisplayName","user","fullName","DailyReportActionProvider","children","initialItems","userId","apiBasePath","draftLabelName","useDailyReportConfig","pendingAddCommentIds","pendingDeleteCommentIds","pendingStarUpdates","pendingReadUpdates","resolvedIdMap","version","setVersion","items","setItems","isSseEnabled","setIsSseEnabled","pendingAddPromises","manuallyTrackingIds","removedIds","processedClientTempIds","pendingSseQueue","handleSseMessageRef","enqueuePendingSseMessage","reportHubId","message","queue","existing","entry","e","subscribeCacheReady","getCachedReport","msg","addProcessedId","id","isProcessedId","upsertItem","businessDate","prev","nextBusinessDate","item","a","b","left","toggleSse","removeStaleItem","initialIds","prevItems","mergedMap","dateA","handleSseMessage","payload","comment","cached","c","isMyComment","pendingMap","tempId","content","newComment","updatedReport","applyDailyReportServerUpdates","acquireMutationLock","releaseMutationLock","v","commentId","statusType","value","pendingEntry","deleteDailyReportCache","report","useDailyReportSseConnection","executeAction","intent","params","callbacks","options","clientTempId","formData","k","res","error","data","addComment","promise","realId","cachedReport","updateDailyReportCache","deleteComment","resolved","deletedId","registerRecentDeletion","toggleStar","isStarred","toggleReadImpl","isRead","toggleReadRef","toggleRead","createReport","now","dummyReport","writeCache","newId","updateReport","publishReport","oldLabels","newLabels","l","deleteReport","targetItem","i","_data","import_react","useDailyReportComments","report","user","userId","pendingAddCommentIds","pendingDeleteCommentIds","resolvedIdMap","version","addComment","deleteComment","useDailyReportActionContext","pendingDeleteId","setPendingDeleteId","handleDeleteComment","cId","handleAddComment","content","prevUiCommentsRef","comments","mergeComments","deletedIds","c","addedComments","existingIds","authorName","tempId","realId","import_react","useDynamicViewportHeight","containerRef","options","enableMutationObserver","bottomGap","minFallbackHeight","viewportHeight","setViewportHeight","updateViewportHeight","target","toNumber","value","containerTop","parentRect","rootStyle","footerHeight","headerHeight","scrollbarHeight","containerStyle","marginBottom","paddingBottom","viewportBottom","effectiveBottom","layoutTop","effectiveTop","availableHeight","safeAvailableHeight","clampedHeight","previous","frameId","timeoutId","resizeObserver","mutationObserver","scheduleUpdate","containerElement","import_virtualscroll","DAILY_REPORT_FETCH_DEBOUNCE_MS","DEFAULT_ITEM_HEIGHT","ITEM_VERTICAL_PADDING","ITEM_CONTAINER_HEIGHT","FAST_SCROLL_WHEEL_MULTIPLIER","FAST_SCROLL_INERTIA_OPTIONS","PREFETCH_LOOKAHEAD","PREFETCH_MAX_DATES","EMPTY_ITEM","EMPTY_DETAIL","WHEEL_RESET_TIMEOUT_MS","VIRTUAL_SCROLL_OVERSCAN_COUNT","SCROLL_BAR_WIDTH","MAX_PREVIEW_LINES","TAP_SCROLL_CIRCLE_OPTIONS","import_react","import_react_slot","import_class_variance_authority","React","import_jsx_runtime","buttonVariants","Button","className","variant","size","asChild","props","ref","cn","import_jsx_runtime","Input","className","type","props","cn","LabelPrimitive","import_jsx_runtime","Label","className","props","cn","import_jsx_runtime","Textarea","className","props","cn","import_jsx_runtime","DailyReportEditForm","report","onCancel","onPublishSuccess","updateReport","publishReport","useDailyReportActionContext","title","setTitle","content","setContent","isSaving","setIsSaving","syncedReportIdRef","handleSave","handlePublish","Label","Input","e","Textarea","Button","import_jsx_runtime","UnreadIndicator","className","cn","import_jsx_runtime","useReportItemHeightObserver","ref","itemId","onHeightMeasured","element","resizeObserver","DailyReportDetailRow","children","elementRef","formatInterviewerLabel","interviewer","DailyReportDetailList","dailyReportItems","userId","selectedItemId","onSelectItem","itemHeights","setItemHeights","scrollContainerRef","virtualScrollRef","viewportHeight","updateViewportHeight","useDynamicViewportHeight","prefetchDailyReportByDate","useDailyReportPrefetch","overlayLabel","setOverlayLabel","isWheelScrollActive","setIsWheelScrollActive","wheelResetTimerRef","scrollEndTimerRef","dailyReportItemsRef","lastScrolledRef","index","item","onSelectItemRef","el","handle","extendedHandle","id","itemCount","getItem","EMPTY_ITEM","schedulePrefetchForRange","fromIndex","toIndex","boundedFrom","boundedTo","businessDates","candidate","PREFETCH_MAX_DATES","date","handleRangeChange","renderingStartIndex","renderingEndIndex","visibleStartIndex","firstVisibleItem","formatToIsoDate","PREFETCH_LOOKAHEAD","handleScroll","container","renderThumbOverlay","props","BusinessDayThumbOverlay","onWheel","updateItemHeight","height","prev","newMap","getItemHeight","getItemKey","tapScrollCircleOptions","handleCommentAdded","renderItem","DailyReportItemContainer","VIRTUAL_SCROLL_OVERSCAN_COUNT","FAST_SCROLL_WHEEL_MULTIPLIER","FAST_SCROLL_INERTIA_OPTIONS","SkeletonReportItem","onCommentAdded","report","error","isLoading","isRefetching","useDailyReportDetail","removeStaleItem","useDailyReportActionContext","draftLabelName","useDailyReportConfig","isEditing","setIsEditing","hasBypassedAutoEdit","setHasBypassedAutoEdit","autoEditingRef","hasEverFetched","isDraft","l","isMine","DailyReportError","DailyReportEditForm","DailyReportResolvedItem","DailyReportHeader","isRead","isStarred","onToggleRead","onToggleStar","onDelete","visitTimeDisplay","categoryLabel","fieldLabels","sourceTypeConfigs","sourceType","sourceConfig","cn","UnreadIndicator","fallbackText","MetadataPanel","onDoubleClick","formContainerRef","toggleStar","toggleRead","pendingStarUpdates","pendingReadUpdates","deleteReport","user","uiComments","handleAddComment","handleDeleteComment","pendingDeleteId","setPendingDeleteId","resolvedIdMap","useDailyReportComments","handleAddCommentWrapper","content","handleToggleRead","handleToggleStar","handleDelete","normalizedContent","from","to","labels","DailyReportCommentList","DailyReportCommentForm","import_swipe_overlay","import_virtualscroll","import_lucide_react","import_react","TabsPrimitive","React","import_jsx_runtime","Tabs","TabsList","className","props","ref","cn","TabsTrigger","TabsContent","import_jsx_runtime","useMobileOverlayManager","isMobileLayout","selectedReportHubId","dailyReportItems","onSelectItem","isMobileDetailOpen","setIsMobileDetailOpen","isOverlayMounted","setIsOverlayMounted","overlayHistoryKeyRef","lastClosedItemIdRef","bodyOverflowRef","selectedItem","e","shouldShowOverlay","timer","key","onPopState","handleOverlayClose","useAutoRead","report","isLoading","autoMarkRead","isActive","toggleRead","ignoreAutoReadForReportHubId","lastReportHubIdRef","handleToggleRead","isAuto","nextValue","DailyReportList","initialSelectedBusinessDate","initialSelectedReportHubId","userId","scrollContainerRef","virtualScrollRef","viewportHeight","updateViewportHeight","useDynamicViewportHeight","overlayLabel","setOverlayLabel","isWheelScrollActive","setIsWheelScrollActive","setIsMobileLayout","wheelResetTimerRef","appliedInitialSignatureRef","itemCount","scrollEndTimerRef","columnHeightStyle","mq","onMediaChange","onResize","el","onWheel","WHEEL_RESET_TIMEOUT_MS","signature","hasInitial","match","i","targetId","index","item","prev","next","formatToIsoDate","dailyReportItemsRef","id","limit","handleItemSelect","getItem","EMPTY_ITEM","getItemHeight","DEFAULT_ITEM_HEIGHT","handleRangeChange","range","handleItemFocus","handleScroll","container","renderThumbOverlay","props","BusinessDayThumbOverlay","VIRTUAL_SCROLL_OVERSCAN_COUNT","SCROLL_BAR_WIDTH","TAP_SCROLL_CIRCLE_OPTIONS","FAST_SCROLL_INERTIA_OPTIONS","FAST_SCROLL_WHEEL_MULTIPLIER","ITEM_VERTICAL_PADDING","ListReportItem","DailyReportSidePane","DailyReportMobileOverlay","ListSkeletonItem","ITEM_CONTAINER_HEIGHT","heightStyle","activeTab","setActiveTab","isEditing","setIsEditing","draftLabelName","fieldLabels","sourceTypeConfigs","useDailyReportConfig","error","useDailyReportDetail","lastReportIdRef","autoEditRef","isDraft","l","isMine","scrollToBottom","headerDate","headerCreator","fallbackText","showHeaderSkeleton","content","sourceType","sourceConfig","cn","Tabs","v","TabsList","tab","TabsTrigger","TabsContent","DailyReportEditForm","DailyReportDisplayPane","onCommentAdded","onDoubleClick","toggleStar","deleteReport","user","useDailyReportActionContext","uiComments","handleAddComment","handleDeleteComment","pendingDeleteId","setPendingDeleteId","resolvedIdMap","useDailyReportComments","prevReportHubIdRef","prevCommentsLengthRef","currentReportHubId","handleAddCommentWrapper","displayContent","handleToggleStar","handleDelete","showSkeleton","isStarred","isRead","hasComments","SkeletonText","className","UnreadIndicator","DailyReportCommentList","DailyReportCommentForm","isOpen","onClose","containerRef","closeButtonRef","isGuardBlocked","requestBlock","resetGuard","displayData","displayItem","EMPTY_DETAIL","overlayRef","overlayStyle","attemptClose","isSwiping","tapHandlers","extractPreviewLines","MAX_PREVIEW_LINES","onSelect","isRefetching","removeStaleItem","hasEverFetched","previewLines","line","creator","date","import_react","import_react_router","import_lucide_react","import_react","import_react_router","SwitchPrimitives","React","import_jsx_runtime","Switch","className","props","ref","cn","import_jsx_runtime","tabTriggerClass","DailyReportResolvedContent","_initialItems","userId","autoMarkRead","setAutoMarkRead","title","renderHeader","showDevControls","useDailyReportConfig","isSseEnabled","toggleSse","createReport","dailyReportItems","useDailyReportActionContext","searchParams","selectedItemId","setSelectedItemId","handleCreateReport","businessDateParam","today","newId","handleAutoMarkReadChange","checked","tabItems","DailyReportList","DailyReportDetailList","DAILY_REPORT_FETCH_DEBOUNCE_MS","Tabs","TabsList","value","label","TabsTrigger","Button","Switch","Label","content","TabsContent","import_jsx_runtime","DailyReportResolvedSync","dailyReportItems","userId","onResolved","DailyReportResolvedContent","DailyReportLoadError","title","renderHeader","useDailyReportConfig","revalidate","DailyReportLoading","DAILY_REPORT_FETCH_DEBOUNCE_MS","DailyReportPageInner","user","dailyReportIds","resolvedInitialItems","setResolvedInitialItems","handleResolvedItems","items","DailyReportActionProvider","resolvedItems","DailyReportPage","config","rest","DailyReportConfigProvider","dailyReportShouldRevalidate","actionResult","actionStatus","currentUrl","defaultShouldRevalidate","formAction","formData","formEncType","formMethod","json","nextUrl","text","intent","fetchDailyReportIds","apiBasePath","defaultDailyReportClientConfig","response","data","createDailyReportClientLoader","options","clientLoader","args","idsPromise"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/client/components/business-day-thumb-overlay.tsx","../src/client/components/daily-report-comment-item.tsx","../src/shared/comment-adapter.ts","../src/shared/text-utils.ts","../src/client/ui/cn.ts","../src/client/components/daily-report-comment-section.tsx","../src/client/components/daily-report-detail-list.tsx","../src/client/config-context.tsx","../src/client/contexts/daily-report-action-context.tsx","../src/shared/logger.ts","../src/client/hooks/use-daily-report.ts","../src/shared/business-date.ts","../src/client/hooks/use-daily-report-sse-connection.ts","../src/shared/sse-schema.ts","../src/client/hooks/use-daily-report-comments.ts","../src/client/hooks/use-dynamic-viewport-height.ts","../src/client/utils/constants.ts","../src/client/components/daily-report-edit-form.tsx","../src/client/ui/button.tsx","../src/client/ui/input.tsx","../src/client/ui/label.tsx","../src/client/ui/textarea.tsx","../src/client/components/unread-indicator.tsx","../src/client/components/daily-report-list.tsx","../src/client/ui/tabs.tsx","../src/client/components/daily-report-page.tsx","../src/client/components/daily-report-resolved-content.tsx","../src/client/ui/switch.tsx","../src/client/route-helpers.ts"],"sourcesContent":["/**\n * Client entry of @aiquants/daily-report: React components, hooks, and route helpers.\n * @aiquants/daily-report の client エントリ。React コンポーネント・フック・ルートヘルパーを公開。\n */\nexport * from \"./client/components/business-day-thumb-overlay\"\nexport * from \"./client/components/daily-report-comment-item\"\nexport * from \"./client/components/daily-report-comment-section\"\nexport * from \"./client/components/daily-report-detail-list\"\nexport * from \"./client/components/daily-report-edit-form\"\nexport * from \"./client/components/daily-report-list\"\nexport * from \"./client/components/daily-report-page\"\nexport * from \"./client/components/daily-report-resolved-content\"\nexport * from \"./client/components/unread-indicator\"\nexport * from \"./client/config-context\"\nexport * from \"./client/contexts/daily-report-action-context\"\nexport * from \"./client/hooks/use-daily-report\"\nexport * from \"./client/hooks/use-daily-report-comments\"\nexport * from \"./client/hooks/use-daily-report-sse-connection\"\nexport * from \"./client/hooks/use-dynamic-viewport-height\"\nexport * from \"./client/route-helpers\"\nexport * from \"./client/utils/constants\"\n","import type { ScrollBarThumbOverlayRenderProps } from \"@aiquants/virtualscroll\"\nimport type { CSSProperties } from \"react\"\n\n/**\n * Tooltip bubble that anchors to the scrollbar thumb while interacting.\n * スクロール操作中にスクロールバーのサムへ連動表示するツールチップのコンポーネント。\n */\nexport const BusinessDayThumbOverlay = ({ orientation, thumbCenter, label, isDragging, isTapScrollActive, isWheelScrollActive }: ScrollBarThumbOverlayRenderProps & { label: string; isWheelScrollActive: boolean }) => {\n if (!(label.trim() && (isDragging || isTapScrollActive || isWheelScrollActive))) return null\n const style: CSSProperties = orientation === \"vertical\" ? { top: thumbCenter, left: -16, transform: \"translate(-100%, -50%)\" } : { left: thumbCenter, top: -16, transform: \"translate(-50%, -100%)\" }\n\n return (\n <div className=\"pointer-events-none absolute flex items-center\" style={style}>\n <div className=\"rounded-full border border-slate-200 bg-white px-3 py-1 text-slate-700 text-xs shadow-md shadow-slate-400/30 dark:border-slate-700 dark:bg-slate-900/90 dark:text-slate-100\">\n <span className=\"whitespace-nowrap font-semibold text-slate-800 dark:text-slate-100\">{label.trim()}</span>\n </div>\n </div>\n )\n}\n","import { TrashIcon } from \"@heroicons/react/24/outline\"\nimport { useState } from \"react\"\nimport { resolveCommentColorClass, type UIComment } from \"../../shared/comment-adapter\"\nimport { fallbackText, formatToIsoDate } from \"../../shared/text-utils\"\nimport { cn } from \"../ui/cn\"\n\ntype DailyReportCommentItemProps = {\n comment: UIComment\n reportHubId: number\n businessDate: string\n onOptimisticDelete: (commentId: number) => void\n pendingDeleteId: number | string | null\n onPendingDeleteIdChange: (id: number | string | null) => void\n onTrashClick: (e: React.MouseEvent | React.TouchEvent, commentId: number | string) => void\n resolvedIdMap?: Map<number, number>\n}\n\n/**\n * Checks if the current comment is in a pending delete state, considering temporary ID resolution.\n * 現在のコメントが削除保留状態かどうかを判定する。一時IDの解決も考慮する。\n */\nconst checkIsPendingDelete = (commentId: number | string, pendingDeleteId: number | string | null, resolvedIdMap?: Map<number, number>): boolean => {\n if (pendingDeleteId === commentId) return true\n // Case: commentId is temp, pendingDeleteId is resolved real ID\n if (typeof commentId === \"number\" && commentId < 0 && resolvedIdMap?.get(commentId) === pendingDeleteId) return true\n // Case: pendingDeleteId is temp, commentId is resolved real ID\n if (typeof pendingDeleteId === \"number\" && pendingDeleteId < 0 && resolvedIdMap?.get(pendingDeleteId) === commentId) return true\n return false\n}\n\n/**\n * Renders a single comment item with delete functionality.\n * 削除機能付きの単一コメントアイテムを描画するコンポーネント。\n */\nexport const DailyReportCommentItem = ({ comment, reportHubId: _reportHubId, businessDate: _businessDate, onOptimisticDelete, pendingDeleteId, onPendingDeleteIdChange, onTrashClick, resolvedIdMap }: DailyReportCommentItemProps) => {\n const [isDeleted, setIsDeleted] = useState(false)\n\n const isPendingDelete = checkIsPendingDelete(comment.id, pendingDeleteId, resolvedIdMap)\n\n const handleConfirmDelete = (e: React.MouseEvent | React.TouchEvent) => {\n e.stopPropagation()\n e.preventDefault()\n\n onPendingDeleteIdChange(null)\n setIsDeleted(true)\n\n if (typeof comment.id === \"number\") {\n // Optimistic UI Update (API call is handled by parent hook)\n onOptimisticDelete(comment.id)\n }\n }\n\n return (\n <li data-comment-id={comment.id} className={cn(\"group relative rounded-xl border border-slate-200 bg-white p-3\", comment.isMine && \"border-blue-200 bg-blue-50\", isDeleted && \"hidden\", isPendingDelete ? \"z-50\" : \"z-0 hover:z-10\")}>\n <div className=\"mb-1 flex items-center justify-between\">\n <div className={cn(\"font-semibold text-xs\", resolveCommentColorClass(comment.color))}>{fallbackText(comment.authorName)}</div>\n <div className=\"flex items-center gap-2\">\n {comment.createdAt && <div className=\"text-[10px] text-slate-400\">{formatToIsoDate(comment.createdAt)}</div>}\n {comment.isMine && !comment.isLegacy && (\n <div className=\"relative\">\n <button type=\"button\" className={cn(\"transition-colors\", isPendingDelete ? \"text-red-500\" : \"text-slate-400 hover:text-red-500\")} title=\"削除\" aria-label=\"削除\" onClick={(e) => onTrashClick(e, comment.id)}>\n <TrashIcon className=\"h-4 w-4\" />\n </button>\n\n {isPendingDelete && (\n <button\n type=\"button\"\n data-delete-confirmation\n className=\"fade-in zoom-in-95 absolute top-full right-0 z-20 mt-1 animate-in whitespace-nowrap rounded-md bg-red-600 px-3 py-1.5 font-bold text-white text-xs shadow-lg duration-200 hover:bg-red-700 active:scale-95\"\n onClick={handleConfirmDelete}>\n 削除する\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n <div className=\"whitespace-pre-wrap text-slate-700 text-sm leading-relaxed dark:text-slate-200\">{comment.content}</div>\n </li>\n )\n}\n","/**\n * Adapters merging legacy JSON comments and relational comments for the UI.\n * レガシー JSON コメントとリレーショナルコメントを UI 向けに統合するアダプタ群。\n */\nimport type { DailyReportComment, DailyReportCommentItem } from \"./types\"\n\nexport type UIComment = {\n id: string | number\n authorName: string\n content: string\n createdAt?: string\n isMine: boolean\n color?: string\n isLegacy: boolean\n}\n\n/**\n * Merges legacy JSON comments and modern relational comments into a unified UI format.\n * レガシーな JSON コメントとモダンなリレーショナルコメントを統合された UI 形式にマージする。\n */\nexport const mergeComments = (legacyComments: DailyReportComment[], modernComments: DailyReportCommentItem[], currentUserId?: string | null): UIComment[] => {\n // 1. Legacy コメントの変換\n const legacyUIComments: UIComment[] = legacyComments.map((c, index) => ({\n id: `legacy-${index}`,\n authorName: c.name || \"Unknown\",\n content: c.text || \"\",\n isMine: false,\n color: c.color || undefined,\n isLegacy: true,\n }))\n\n // 2. Modern コメントの変換\n const modernUIComments: UIComment[] = modernComments.map((c) => ({\n id: c.id,\n authorName: c.userName,\n content: c.content,\n createdAt: c.createdAt,\n isMine: currentUserId ? c.userId === currentUserId : c.isMine,\n isLegacy: false,\n }))\n\n // 3. 結合 (Legacy を先に表示)\n return [...legacyUIComments, ...modernUIComments]\n}\n\n/**\n * Resolves comment color tokens into Tailwind text classes.\n * コメント色のキーワードを Tailwind のテキストクラスに変換する関数。\n */\nexport const resolveCommentColorClass = (color: string | null | undefined): string => {\n if (!color) {\n return \"text-gray-700\"\n }\n\n const normalized = color.toLowerCase()\n\n if (normalized.includes(\"red\") || normalized.includes(\"赤\")) {\n return \"text-red-600\"\n }\n\n if (normalized.includes(\"green\") || normalized.includes(\"緑\")) {\n return \"text-green-600\"\n }\n\n if (normalized.includes(\"blue\") || normalized.includes(\"青\")) {\n return \"text-blue-600\"\n }\n\n if (normalized.includes(\"yellow\") || normalized.includes(\"黄\")) {\n return \"text-yellow-600\"\n }\n\n if (normalized.includes(\"orange\") || normalized.includes(\"橙\")) {\n return \"text-orange-500\"\n }\n\n return \"text-gray-700\"\n}\n","/**\n * Shared text helpers for daily report view components.\n * 日報ビューコンポーネント向けの共通テキストヘルパー。\n */\n\n/**\n * Normalizes nullable text by trimming whitespace and applying fallback.\n * null 許容の文字列をトリムしつつフォールバック値を適用する関数。\n */\nexport const fallbackText = (value: string | null | undefined): string => {\n if (!value) {\n return \"-\"\n }\n const trimmed = value.trim()\n return trimmed === \"\" ? \"-\" : trimmed\n}\n\n/**\n * Formats a date string to ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).\n * 日付文字列を ISO 8601 形式 (YYYY-MM-DD または YYYY-MM-DDTHH:mm:ss) に整形する関数。\n */\nexport const formatToIsoDate = (value: unknown): string => {\n if (!value) {\n return \"-\"\n }\n if (typeof value !== \"string\") {\n // If value is not a string (e.g. Date object), try to convert it to string or handle it\n if (value instanceof Date) {\n return formatToIsoDate(value.toISOString())\n }\n return String(value)\n }\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return \"-\"\n }\n\n const date = new Date(trimmed)\n if (Number.isNaN(date.getTime())) {\n return trimmed\n }\n\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, \"0\")\n const day = String(date.getDate()).padStart(2, \"0\")\n\n const hasTime = trimmed.includes(\":\") || trimmed.includes(\"T\")\n\n if (hasTime) {\n const hours = String(date.getHours()).padStart(2, \"0\")\n const minutes = String(date.getMinutes()).padStart(2, \"0\")\n const seconds = String(date.getSeconds()).padStart(2, \"0\")\n return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`\n }\n\n return `${year}-${month}-${day}`\n}\n","/**\n * Tailwind class combiner (clsx + tailwind-merge).\n * Tailwind クラスを結合・重複解決するユーティリティ。\n */\nimport { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\n/**\n * Combines class values and resolves Tailwind conflicts.\n * クラス値を結合し Tailwind の衝突を解決する処理。\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { UIComment } from \"../../shared/comment-adapter\"\nimport { DailyReportCommentItem } from \"./daily-report-comment-item\"\n\n/**\n * Custom hook to manage the auto-reset behavior of the pending delete state.\n * 削除保留状態の自動リセット(タイマーおよび外部クリック)を管理するカスタムフック。\n */\nconst useAutoResetPendingDelete = (pendingDeleteId: number | string | null, onPendingDeleteIdChange: (id: number | string | null) => void) => {\n const timerRef = useRef<number | null>(null)\n\n /**\n * Effect to handle global clicks and reset pending delete state if clicked outside.\n * 外部クリックを監視し、削除確認ボタン以外がクリックされた場合に削除保留状態をリセットする副作用。\n *\n * Purpose: To cancel the delete confirmation mode when the user interacts with other parts of the UI.\n * Dependencies: [pendingDeleteId, onPendingDeleteIdChange] - Re-runs when the pending ID changes.\n * Cleanup: Removes event listeners for mousedown and touchstart.\n */\n useEffect(() => {\n if (pendingDeleteId === null) return\n\n const handleGlobalClick = (e: Event) => {\n const target = e.target as HTMLElement\n // Ignore clicks inside the delete confirmation button\n if (target.closest(\"[data-delete-confirmation]\")) return\n\n onPendingDeleteIdChange(null)\n }\n\n document.addEventListener(\"mousedown\", handleGlobalClick)\n document.addEventListener(\"touchstart\", handleGlobalClick)\n\n return () => {\n document.removeEventListener(\"mousedown\", handleGlobalClick)\n document.removeEventListener(\"touchstart\", handleGlobalClick)\n }\n }, [pendingDeleteId, onPendingDeleteIdChange])\n\n /**\n * Effect to clean up the timer on unmount.\n * アンマウント時にタイマーをクリーンアップする副作用。\n *\n * Purpose: To prevent memory leaks and state updates on unmounted components.\n * Dependencies: [] - Runs only on mount/unmount.\n * Cleanup: Clears the timeout if it exists.\n */\n useEffect(() => {\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current)\n }\n }, [])\n\n const handleTrashClick = useCallback(\n (e: React.MouseEvent | React.TouchEvent, commentId: number | string) => {\n e.stopPropagation()\n e.preventDefault()\n\n if (pendingDeleteId === commentId) return\n\n onPendingDeleteIdChange(commentId)\n\n if (timerRef.current) clearTimeout(timerRef.current)\n // Auto-reset after 5 seconds\n timerRef.current = window.setTimeout(() => {\n onPendingDeleteIdChange(null)\n timerRef.current = null\n }, 5000)\n },\n [pendingDeleteId, onPendingDeleteIdChange],\n )\n\n return { handleTrashClick }\n}\n\ntype DailyReportCommentListProps = {\n comments: UIComment[]\n reportHubId: number\n businessDate: string\n onDeleteComment?: (commentId: number) => void\n pendingDeleteId: number | string | null\n onPendingDeleteIdChange: (id: number | string | null) => void\n resolvedIdMap?: Map<number, number>\n}\n\n/**\n * Renders a list of daily report comments.\n * 日報コメントのリストを描画するコンポーネント。\n */\nexport const DailyReportCommentList = ({ comments, reportHubId, businessDate, onDeleteComment, pendingDeleteId, onPendingDeleteIdChange, resolvedIdMap }: DailyReportCommentListProps) => {\n const { handleTrashClick } = useAutoResetPendingDelete(pendingDeleteId, onPendingDeleteIdChange)\n\n if (comments.length === 0) return null\n\n return (\n <ul className=\"mb-3 space-y-2\">\n {comments.map((comment) => (\n <DailyReportCommentItem\n key={comment.id}\n comment={comment}\n reportHubId={reportHubId}\n businessDate={businessDate}\n onOptimisticDelete={onDeleteComment || (() => {})}\n pendingDeleteId={pendingDeleteId}\n onPendingDeleteIdChange={onPendingDeleteIdChange}\n onTrashClick={handleTrashClick}\n resolvedIdMap={resolvedIdMap}\n />\n ))}\n </ul>\n )\n}\n\ntype DailyReportCommentFormProps = {\n reportHubId: number\n businessDate: string\n onAddComment?: (content: string) => Promise<void> | void\n}\n\n/**\n * Renders a form to add a new comment to a daily report.\n * 日報に新しいコメントを追加するためのフォームを描画するコンポーネント。\n */\nexport const DailyReportCommentForm = ({ reportHubId, businessDate, onAddComment }: DailyReportCommentFormProps) => {\n const [isSubmitting, setIsSubmitting] = useState(false)\n const formRef = useRef<HTMLFormElement>(null)\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault()\n const formData = new FormData(e.currentTarget)\n const content = (formData.get(\"content\") as string)?.trim()\n\n if (!(content && onAddComment)) return\n\n setIsSubmitting(true)\n formRef.current?.reset()\n try {\n await onAddComment(content)\n } catch {\n if (formRef.current) {\n const input = formRef.current.elements.namedItem(\"content\") as HTMLInputElement\n if (input) input.value = content\n }\n } finally {\n setIsSubmitting(false)\n }\n }\n\n return (\n <form className=\"flex gap-2\" ref={formRef} onSubmit={handleSubmit}>\n <input type=\"hidden\" name=\"intent\" value=\"addComment\" />\n <input type=\"hidden\" name=\"reportHubId\" value={reportHubId} />\n <input type=\"hidden\" name=\"businessDate\" value={businessDate} />\n <input\n type=\"text\"\n name=\"content\"\n placeholder=\"コメントを追加...\"\n className=\"flex-1 rounded-lg border border-slate-300 bg-slate-50 px-3 py-2 text-sm focus:border-blue-500 focus:bg-white focus:outline-none dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200\"\n autoComplete=\"off\"\n />\n <button type=\"submit\" className=\"rounded-lg bg-blue-600 px-4 py-2 font-medium text-sm text-white hover:bg-blue-700 disabled:opacity-50\" disabled={isSubmitting}>\n 送信\n </button>\n </form>\n )\n}\n","/**\n * Virtual-scroll based detailed daily report view backed by fetcher-driven data hooks.\n * useFetcher ベースのデータフックで日報詳細を提供する仮想スクロールビュー。\n */\n\nimport { type ScrollBarTapCircleOptions, type ScrollBarThumbOverlayRenderProps, tapScrollCircleSampleVisual, VirtualScroll, type VirtualScrollHandle } from \"@aiquants/virtualscroll\"\n\n/**\n * Handle type augmented with E2E test helper methods.\n * E2E テスト用ヘルパーメソッドで拡張されたハンドル型。\n */\ntype DailyReportVirtualScrollHandle = VirtualScrollHandle & {\n findReportIndex: (id: number) => number\n getReportItem: (index: number) => DailyReportItem | undefined\n selectItem: (id: number) => void\n}\n\nimport { Check, Star, Trash2 } from \"lucide-react\"\nimport { type RefObject, useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { fallbackText, formatToIsoDate } from \"../../shared/text-utils\"\nimport type { DailyReportDetail, DailyReportItem } from \"../../shared/types\"\nimport { type DailyReportFieldLabels, useDailyReportConfig } from \"../config-context\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { useDailyReportDetail, useDailyReportPrefetch } from \"../hooks/use-daily-report\"\nimport { useDailyReportComments } from \"../hooks/use-daily-report-comments\"\nimport { useDynamicViewportHeight } from \"../hooks/use-dynamic-viewport-height\"\nimport { EMPTY_ITEM, FAST_SCROLL_INERTIA_OPTIONS, FAST_SCROLL_WHEEL_MULTIPLIER, PREFETCH_LOOKAHEAD, PREFETCH_MAX_DATES, VIRTUAL_SCROLL_OVERSCAN_COUNT } from \"../utils/constants\"\nimport { BusinessDayThumbOverlay } from \"./business-day-thumb-overlay\"\nimport { DailyReportCommentForm, DailyReportCommentList } from \"./daily-report-comment-section\"\nimport { DailyReportEditForm } from \"./daily-report-edit-form\"\nimport { UnreadIndicator } from \"./unread-indicator\"\n\n/**\n * Observes container height changes and reports measured values upstream.\n * コンテナの高さ変化を監視し測定結果を親へ通知するフック。\n */\nconst useReportItemHeightObserver = (ref: RefObject<HTMLDivElement | null>, itemId: number, onHeightMeasured: (id: number, height: number) => void) => {\n useEffect(() => {\n const element = ref.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver(() => {\n onHeightMeasured(itemId, element.offsetHeight)\n })\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [itemId, onHeightMeasured, ref])\n}\n\n/**\n * Wraps a daily report item row, observing its height and providing a stable test identifier.\n * 日報アイテム行をラップし、高さを監視するとともに安定したテスト識別子を提供するコンポーネント。\n */\nconst DailyReportDetailRow = ({ itemId, onHeightMeasured, children }: { itemId: number; onHeightMeasured: (id: number, height: number) => void; children: React.ReactNode }) => {\n const elementRef = useRef<HTMLDivElement | null>(null)\n useReportItemHeightObserver(elementRef, itemId, onHeightMeasured)\n return (\n <div ref={elementRef} className=\"px-2 pb-3\" data-daily-report-button={itemId}>\n {children}\n </div>\n )\n}\n\n/**\n * Formats interviewer labels with optional affiliation.\n * 面談者情報を所属付きのラベル文字列に整形する関数。\n */\nconst formatInterviewerLabel = (interviewer: DailyReportDetail[\"interviewers\"][number]): string => {\n if (interviewer.affiliation?.trim()) {\n return `${interviewer.name}(${interviewer.affiliation})`\n }\n return interviewer.name\n}\n\ntype DailyReportDetailListProps = {\n dailyReportItems: DailyReportItem[]\n userId?: string | null\n selectedItemId?: number | null\n onSelectItem?: (id: number | null) => void\n}\n\n/**\n * Daily report detail cards rendered with dynamic height virtual scroll.\n * 動的高さの仮想スクロールで日報詳細カードを描画するコンポーネント。\n */\nexport const DailyReportDetailList = ({ dailyReportItems, userId, selectedItemId, onSelectItem }: DailyReportDetailListProps) => {\n const [itemHeights, setItemHeights] = useState<Map<number, number>>(new Map())\n const scrollContainerRef = useRef<HTMLDivElement | null>(null)\n const virtualScrollRef = useRef<VirtualScrollHandle>(null)\n const { viewportHeight, updateViewportHeight } = useDynamicViewportHeight(scrollContainerRef, { enableMutationObserver: true })\n const prefetchDailyReportByDate = useDailyReportPrefetch()\n const [overlayLabel, setOverlayLabel] = useState<string>(\"\")\n const [isWheelScrollActive, setIsWheelScrollActive] = useState(false)\n const wheelResetTimerRef = useRef<number | null>(null)\n const scrollEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const dailyReportItemsRef = useRef(dailyReportItems)\n dailyReportItemsRef.current = dailyReportItems\n\n // 同一 selectedItemId かつ同一 index への重複スクロールを防止する ref\n const lastScrolledRef = useRef<{ id: number; index: number } | null>(null)\n\n useEffect(() => {\n if (selectedItemId != null && virtualScrollRef.current) {\n const index = dailyReportItems.findIndex((item) => item.reportHubId === selectedItemId)\n if (index !== -1) {\n // ID もインデックスも同じなら再スクロール不要\n if (lastScrolledRef.current?.id === selectedItemId && lastScrolledRef.current?.index === index) return\n virtualScrollRef.current.scrollToIndex(index, { align: \"top\" })\n lastScrolledRef.current = { id: selectedItemId, index }\n }\n } else {\n lastScrolledRef.current = null\n }\n }, [selectedItemId, dailyReportItems])\n\n // onSelectItem の最新参照を保持する ref (useEffect 依存配列を不要にする)\n const onSelectItemRef = useRef(onSelectItem)\n onSelectItemRef.current = onSelectItem\n\n useEffect(() => {\n if (virtualScrollRef.current) {\n const el = document.querySelector(\".test-daily-report-detail-list\")\n if (el) {\n const handle = virtualScrollRef.current\n const extendedHandle: DailyReportVirtualScrollHandle = Object.assign({}, handle, {\n findReportIndex: (id: number) => dailyReportItemsRef.current.findIndex((item) => item.reportHubId === id),\n getReportItem: (index: number) => dailyReportItemsRef.current[index],\n selectItem: (id: number) => onSelectItemRef.current?.(id),\n })\n ;(el as Element & { __virtualScroll: DailyReportVirtualScrollHandle }).__virtualScroll = extendedHandle\n }\n }\n })\n\n const itemCount = dailyReportItems.length\n\n const getItem = useCallback(\n (index: number): DailyReportItem => {\n if (index >= itemCount) return EMPTY_ITEM\n return dailyReportItems[index]\n },\n [dailyReportItems, itemCount],\n )\n\n const schedulePrefetchForRange = useCallback(\n (fromIndex: number, toIndex: number) => {\n if (itemCount === 0) return\n const boundedFrom = Math.max(0, fromIndex)\n const boundedTo = Math.min(itemCount - 1, toIndex)\n if (boundedFrom > boundedTo) return\n\n const businessDates = new Set<string>()\n for (let index = boundedFrom; index <= boundedTo; index++) {\n const candidate = getItem(index)\n if (candidate.businessDate) businessDates.add(candidate.businessDate)\n if (businessDates.size >= PREFETCH_MAX_DATES) break\n }\n businessDates.forEach((date) => {\n prefetchDailyReportByDate(date)\n })\n },\n [getItem, itemCount, prefetchDailyReportByDate],\n )\n\n /**\n * Handles visible range changes to update overlay label and schedule prefetching.\n * 表示範囲の変更を処理し、オーバーレイラベルの更新とプリフェッチのスケジュールを行う。\n */\n const handleRangeChange = useCallback(\n ({ renderingStartIndex, renderingEndIndex, visibleStartIndex }: { renderingStartIndex: number; renderingEndIndex: number; visibleStartIndex: number }) => {\n if (itemCount === 0) return\n const firstVisibleItem = getItem(visibleStartIndex)\n if (firstVisibleItem?.businessDate) {\n setOverlayLabel(formatToIsoDate(firstVisibleItem.businessDate))\n }\n schedulePrefetchForRange(renderingStartIndex, renderingEndIndex)\n schedulePrefetchForRange(renderingEndIndex + 1, renderingEndIndex + PREFETCH_LOOKAHEAD)\n schedulePrefetchForRange(renderingStartIndex - PREFETCH_LOOKAHEAD, renderingStartIndex - 1)\n },\n [getItem, itemCount, schedulePrefetchForRange],\n )\n\n const handleScroll = useCallback(() => {\n const container = scrollContainerRef.current\n if (!container) return\n\n if (!container.classList.contains(\"is-scrolling\")) {\n container.classList.add(\"is-scrolling\")\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n container.classList.remove(\"is-scrolling\")\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [])\n\n const renderThumbOverlay = useCallback((props: ScrollBarThumbOverlayRenderProps) => <BusinessDayThumbOverlay {...props} label={overlayLabel} isWheelScrollActive={isWheelScrollActive} />, [overlayLabel, isWheelScrollActive])\n\n /**\n * Sets up wheel event listener to toggle overlay visibility.\n * ホイールイベントリスナーを設定し、オーバーレイの表示状態を切り替える。\n */\n useEffect(() => {\n const el = scrollContainerRef.current\n if (!el) return\n const onWheel = () => {\n setIsWheelScrollActive(true)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n wheelResetTimerRef.current = window.setTimeout(() => {\n setIsWheelScrollActive(false)\n wheelResetTimerRef.current = null\n }, 240)\n }\n el.addEventListener(\"wheel\", onWheel, { passive: true })\n return () => {\n el.removeEventListener(\"wheel\", onWheel)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n }\n }, [])\n\n const updateItemHeight = useCallback((id: number, height: number) => {\n setItemHeights((prev) => {\n if (prev.get(id) === height) return prev\n const newMap = new Map(prev)\n newMap.set(id, height)\n return newMap\n })\n if (virtualScrollRef.current) {\n const index = dailyReportItemsRef.current.findIndex((item) => item.reportHubId === id)\n if (index !== -1) {\n virtualScrollRef.current.updateItemSize(index, height)\n }\n }\n }, [])\n\n const getItemHeight = useCallback(\n (index: number) => {\n const item = getItem(index)\n return itemHeights.get(item.reportHubId) || 350\n },\n [getItem, itemHeights],\n )\n\n const getItemKey = useCallback(\n (index: number) => {\n const item = getItem(index)\n return item.reportHubId\n },\n [getItem],\n )\n\n const tapScrollCircleOptions = useMemo<ScrollBarTapCircleOptions>(() => ({ enabled: true, renderVisual: tapScrollCircleSampleVisual }), [])\n\n /**\n * Updates viewport height when item count changes.\n * アイテム数変更時にビューポート高さを更新する。\n */\n useEffect(() => {\n if (itemCount >= 0) updateViewportHeight()\n }, [itemCount, updateViewportHeight])\n\n /**\n * Schedules initial prefetch when items are loaded.\n * アイテム読み込み時に初期プリフェッチをスケジュールする。\n */\n useEffect(() => {\n if (itemCount === 0) return\n schedulePrefetchForRange(0, Math.min(itemCount - 1, PREFETCH_LOOKAHEAD))\n }, [itemCount, schedulePrefetchForRange])\n\n const handleCommentAdded = useCallback((itemId: number) => {\n const index = dailyReportItemsRef.current.findIndex((item) => item.reportHubId === itemId)\n if (index >= 0) {\n setTimeout(() => virtualScrollRef.current?.scrollToIndex(index, { align: \"bottom\" }), 300)\n }\n }, [])\n\n const renderItem = useCallback((item: DailyReportItem) => <DailyReportItemContainer key={item.reportHubId} item={item} onHeightMeasured={updateItemHeight} onCommentAdded={handleCommentAdded} userId={userId} />, [updateItemHeight, handleCommentAdded, userId])\n\n return (\n <>\n <div ref={scrollContainerRef} className=\"flex min-h-0 flex-1 flex-col\">\n <VirtualScroll\n ref={virtualScrollRef}\n itemCount={itemCount}\n getItem={getItem}\n getItemKey={getItemKey}\n getItemHeight={getItemHeight}\n viewportSize={viewportHeight}\n overscanCount={VIRTUAL_SCROLL_OVERSCAN_COUNT}\n scrollBarOptions={{\n width: 8,\n tapScrollCircleOptions: tapScrollCircleOptions,\n renderThumbOverlay: renderThumbOverlay,\n }}\n behaviorOptions={{\n wheelSpeedMultiplier: FAST_SCROLL_WHEEL_MULTIPLIER,\n inertiaOptions: FAST_SCROLL_INERTIA_OPTIONS,\n }}\n onRangeChange={handleRangeChange}\n onScroll={handleScroll}\n className=\"t-detail-list test-daily-report-detail-list relative w-full\">\n {renderItem}\n </VirtualScroll>\n </div>\n {itemCount === 0 && (\n <div className=\"py-8 text-center\">\n <p className=\"text-slate-500\">データがありません</p>\n </div>\n )}\n </>\n )\n}\n\n/**\n * Loading skeleton for the virtual scroll detail entry.\n * 仮想スクロール詳細行の読み込み中スケルトン表示。\n */\nconst SkeletonReportItem = () => (\n <div className=\"rounded-2xl border border-slate-200 bg-white p-4 shadow-sm\">\n <div className=\"mb-4 flex flex-wrap gap-2\">\n <div className=\"h-6 w-20 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-24 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-24 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-28 rounded-full bg-slate-200\"></div>\n </div>\n <div className=\"mb-3 grid gap-3 md:grid-cols-[minmax(0,1fr)_220px]\">\n <div className=\"space-y-3\">\n <div className=\"h-5 w-40 rounded bg-slate-200\"></div>\n <div className=\"h-5 w-56 rounded bg-slate-200\"></div>\n <div className=\"h-24 rounded-lg bg-slate-200\"></div>\n </div>\n <div className=\"rounded-xl bg-slate-100 p-3\">\n <div className=\"mb-2 h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"mb-2 h-4 w-28 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-32 rounded bg-slate-200\"></div>\n </div>\n </div>\n <div className=\"space-y-3\">\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"flex flex-wrap gap-2\">\n <div className=\"h-6 w-16 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-16 rounded-full bg-slate-200\"></div>\n <div className=\"h-6 w-16 rounded-full bg-slate-200\"></div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-16 rounded-lg bg-slate-200\"></div>\n <div className=\"h-16 rounded-lg bg-slate-200\"></div>\n </div>\n </div>\n </div>\n)\n\n/**\n * Chooses the appropriate daily report item representation based on fetch state.\n * フェッチ状態に応じて日報アイテムの表示コンポーネントを選択する関数。\n */\nconst DailyReportItemContainer = ({ item, onHeightMeasured, onCommentAdded, userId }: { item: DailyReportItem; onHeightMeasured: (id: number, height: number) => void; onCommentAdded?: (itemId: number) => void; userId?: string | null }) => {\n const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)\n const { removeStaleItem } = useDailyReportActionContext()\n const { draftLabelName } = useDailyReportConfig()\n const [isEditing, setIsEditing] = useState(false)\n const [hasBypassedAutoEdit, setHasBypassedAutoEdit] = useState(false)\n // 自動編集モード (ドラフト検出による自動遷移) かどうかを追跡\n const autoEditingRef = useRef(false)\n\n // フェッチが一度でも試行されたかを追跡する ref\n const hasEverFetched = useRef(false)\n if (isRefetching) hasEverFetched.current = true\n\n // フェッチ完了後に report が null → stale アイテムとしてリストから除去する安全弁\n // 外部リソース同期: useDailyReportDetail のフェッチ結果に反応する\n useEffect(() => {\n if (!(report || isLoading || isRefetching || error) && hasEverFetched.current && item.reportHubId > 0) {\n removeStaleItem(item.reportHubId)\n }\n }, [report, isLoading, isRefetching, error, item.reportHubId, removeStaleItem])\n\n const isDraft = report?.labels.some((l) => l.name === draftLabelName)\n const isMine = userId && report?.userId === userId\n\n // ドラフトレポートの自動編集モード制御\n // - ドラフト状態かつ自分のレポート: 自動的にエディタモード\n // - SSE でドラフト→公開に遷移: 自動編集モードの場合のみビューモードへ解除\n useEffect(() => {\n if (report && isMine && isDraft && !isEditing && !hasBypassedAutoEdit) {\n setIsEditing(true)\n autoEditingRef.current = true\n } else if (report && !isDraft && isEditing && autoEditingRef.current) {\n // SSE で下書き→公開に遷移: 自動編集モードの場合のみビューモードへ\n setIsEditing(false)\n autoEditingRef.current = false\n }\n }, [report, isMine, isDraft, isEditing, hasBypassedAutoEdit])\n\n return (\n <DailyReportDetailRow itemId={item.reportHubId} onHeightMeasured={onHeightMeasured}>\n {error ? (\n <DailyReportError itemId={item.reportHubId} />\n ) : !report ? (\n <SkeletonReportItem />\n ) : isEditing ? (\n <DailyReportEditForm\n report={report}\n onCancel={() => {\n setIsEditing(false)\n setHasBypassedAutoEdit(true)\n }}\n onPublishSuccess={() => {\n setIsEditing(false)\n }}\n />\n ) : (\n <DailyReportResolvedItem item={item} report={report} onCommentAdded={onCommentAdded} userId={userId} onDoubleClick={isMine ? () => setIsEditing(true) : undefined} />\n )}\n </DailyReportDetailRow>\n )\n}\n\n/**\n * Renders the header section of a daily report.\n * 日報のヘッダー部分を描画するコンポーネント。\n */\nconst DailyReportHeader = ({\n report,\n isRead,\n isStarred,\n isMine,\n onToggleRead,\n onToggleStar,\n onDelete,\n visitTimeDisplay,\n categoryLabel,\n fieldLabels,\n}: {\n report: DailyReportDetail\n isRead: boolean\n isStarred: boolean\n isMine: boolean\n onToggleRead: () => void\n onToggleStar: () => void\n onDelete: () => void\n visitTimeDisplay: string\n categoryLabel: string\n fieldLabels: DailyReportFieldLabels\n}) => (\n <div className=\"flex flex-wrap items-center gap-2 font-medium text-slate-600 text-xs\">\n <button\n type=\"button\"\n onClick={onToggleRead}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 focus:outline-none dark:hover:bg-slate-700\"\n title={isRead ? \"未読にする\" : \"既読にする\"}\n data-detail-read-button={report.reportHubId}>\n {isRead ? <Check className=\"h-3.5 w-3.5 text-blue-500\" /> : <UnreadIndicator className=\"text-xs\" />}\n </button>\n <button\n type=\"button\"\n onClick={onToggleStar}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 focus:outline-none dark:hover:bg-slate-700\"\n title={isStarred ? \"スターを外す\" : \"スターを付ける\"}\n data-detail-star-button={report.reportHubId}>\n {isStarred ? <Star className=\"h-3.5 w-3.5 fill-yellow-400 text-yellow-400\" /> : <Star className=\"h-3.5 w-3.5 text-slate-300 dark:text-slate-500\" />}\n </button>\n {isMine && (\n <button type=\"button\" onClick={onDelete} data-detail-delete-button={report.reportHubId} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-125\" title=\"削除する\">\n <Trash2 className=\"h-4 w-4 text-slate-300 hover:text-red-500\" />\n </button>\n )}\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-blue-50 px-3 py-1 font-semibold text-blue-700 text-xs\">\n <span className=\"text-[10px] text-blue-500 uppercase tracking-wide\">{fieldLabels.id}</span>\n {report.reportHubId}\n </span>\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.businessDate}</span>\n {formatToIsoDate(report.date)}\n </span>\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.author}</span>\n {fallbackText(report.employeeName)}\n </span>\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.visitTime}</span>\n {visitTimeDisplay || \"-\"}\n </span>\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-3 py-1\">\n <span className=\"text-[10px] text-slate-400 uppercase tracking-wide\">{fieldLabels.updatedAt}</span>\n {formatToIsoDate(report.updatedAt)}\n </span>\n {categoryLabel && (\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-amber-100 px-3 py-1 text-amber-700\">\n <span className=\"text-[10px] text-amber-500 uppercase tracking-wide\">{fieldLabels.category}</span>\n {categoryLabel}\n </span>\n )}\n </div>\n)\n\n/**\n * Renders the side panel with detailed metadata.\n * 詳細なメタデータを表示するサイドパネルを描画するコンポーネント。\n */\nconst MetadataPanel = ({ report, visitTimeDisplay, categoryLabel, fieldLabels }: { report: DailyReportDetail; visitTimeDisplay: string; categoryLabel: string; fieldLabels: DailyReportFieldLabels }) => (\n <div className=\"flex flex-col gap-3 rounded-xl bg-slate-100 p-4 text-slate-600 text-xs\">\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.author}</span>\n <span className=\"text-slate-700 text-sm\">{fallbackText(report.author)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.createdAt}</span>\n <span className=\"text-slate-700 text-sm\">{formatToIsoDate(report.createdAt)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.updatedBy}</span>\n <span className=\"text-slate-700 text-sm\">{fallbackText(report.updatedBy)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.updatedAt}</span>\n <span className=\"text-slate-700 text-sm\">{formatToIsoDate(report.updatedAt)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.businessDate}</span>\n <span className=\"text-slate-700 text-sm\">{formatToIsoDate(report.date)}</span>\n </div>\n {visitTimeDisplay && (\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.visitTime}</span>\n <span className=\"text-slate-700 text-sm\">{visitTimeDisplay}</span>\n </div>\n )}\n {categoryLabel && (\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-[10px] text-slate-500 uppercase tracking-wide\">{fieldLabels.categoryInfo}</span>\n <span className=\"text-slate-700 text-sm\">{categoryLabel}</span>\n </div>\n )}\n </div>\n)\n\n/**\n * Renders the resolved daily report detail card with metadata and content.\n * 解決済みの日報詳細カードをメタデータと内容込みで描画する関数。\n */\nconst DailyReportResolvedItem = ({ item, report, onCommentAdded, userId, onDoubleClick }: { item: DailyReportItem; report: DailyReportDetail; onCommentAdded?: (itemId: number) => void; userId?: string | null; onDoubleClick?: () => void }) => {\n const formContainerRef = useRef<HTMLDivElement>(null)\n // 表示中ユーザーはアクションコンテキストから取得する (root loader 非依存)\n const { toggleStar, toggleRead, pendingStarUpdates, pendingReadUpdates, deleteReport, user } = useDailyReportActionContext()\n const { uiComments, handleAddComment, handleDeleteComment, pendingDeleteId, setPendingDeleteId, resolvedIdMap } = useDailyReportComments(report, user, userId)\n const { fieldLabels } = useDailyReportConfig()\n\n const handleAddCommentWrapper = async (content: string) => {\n await handleAddComment(content)\n onCommentAdded?.(item.reportHubId)\n }\n\n const isStarred = pendingStarUpdates.current.has(report.reportHubId) ? (pendingStarUpdates.current.get(report.reportHubId) ?? false) : report.isStarred\n const isRead = pendingReadUpdates.current.has(report.reportHubId) ? (pendingReadUpdates.current.get(report.reportHubId) ?? false) : report.isRead\n const isMine = !!(userId && report.userId === userId)\n\n const handleToggleRead = async () => {\n try {\n await toggleRead(report.reportHubId, !isRead, report.date ?? \"\")\n } catch {}\n }\n\n const handleToggleStar = async () => {\n try {\n await toggleStar(report.reportHubId, !isStarred, report.date ?? \"\")\n } catch {}\n }\n\n const handleDelete = async () => {\n if (!confirm(\"本当に削除しますか?\")) return\n await deleteReport(report.reportHubId, report.date ?? \"\")\n }\n\n const normalizedContent = useMemo(() => (report.content ? report.content.replace(/<br\\s*\\/??\\s*>/gi, \"\\n\").trim() : \"\"), [report.content])\n const visitTimeDisplay = useMemo(() => {\n const from = report.visitTimeFrom?.trim()\n const to = report.visitTimeTo?.trim()\n return from && to ? `${from} - ${to}` : from || to || \"\"\n }, [report.visitTimeFrom, report.visitTimeTo])\n const categoryLabel = useMemo(() => {\n const labels: string[] = []\n if (report.category?.trim()) labels.push(`${fieldLabels.category}: ${report.category}`)\n if (report.creationCategory?.trim()) labels.push(`${fieldLabels.creationCategory}: ${report.creationCategory}`)\n return labels.join(\" / \")\n }, [report.category, report.creationCategory, fieldLabels.category, fieldLabels.creationCategory])\n\n return (\n // biome-ignore lint/a11y/noStaticElementInteractions: Double click to edit feature\n <div\n onDoubleClick={onDoubleClick}\n className={`flex h-full w-full flex-col rounded-2xl border bg-white p-4 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:border-blue-300 hover:shadow-lg dark:border-slate-700 dark:bg-slate-900/70 ${!isRead ? \"border-l-4 border-l-blue-500\" : \"\"}`}>\n <div className=\"flex flex-col gap-4\">\n <DailyReportHeader\n report={report}\n isRead={isRead}\n isStarred={isStarred}\n isMine={isMine}\n onToggleRead={handleToggleRead}\n onToggleStar={handleToggleStar}\n onDelete={handleDelete}\n visitTimeDisplay={visitTimeDisplay}\n categoryLabel={categoryLabel}\n fieldLabels={fieldLabels}\n />\n <div className=\"grid gap-4 md:grid-cols-[minmax(0,1fr)_240px]\">\n <div className=\"flex flex-col gap-4\">\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.customer}</span>\n <span className=\"font-semibold text-base text-slate-900\">{fallbackText(report.customerName)}</span>\n </div>\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.subject}</span>\n <span className=\"text-slate-700 text-sm\">{fallbackText(report.subject)}</span>\n </div>\n </div>\n {normalizedContent && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.content}</span>\n <div className=\"whitespace-pre-wrap rounded-xl bg-slate-50 p-3 text-slate-700 text-sm leading-relaxed\">{normalizedContent}</div>\n </div>\n )}\n {report.interviewers.length > 0 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.interviewers}</span>\n <ul className=\"flex flex-wrap gap-2\">\n {report.interviewers.map((interviewer) => (\n <li key={`${interviewer.name}-${interviewer.affiliation || \"\"}`} className=\"rounded-full bg-slate-100 px-3 py-1 text-slate-600 text-xs\">\n {formatInterviewerLabel(interviewer)}\n </li>\n ))}\n </ul>\n </div>\n )}\n {uiComments.length > 0 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide\">{fieldLabels.comments}</span>\n <DailyReportCommentList\n comments={uiComments}\n reportHubId={report.reportHubId}\n businessDate={report.date ?? \"\"}\n onDeleteComment={handleDeleteComment}\n pendingDeleteId={pendingDeleteId}\n onPendingDeleteIdChange={setPendingDeleteId}\n resolvedIdMap={resolvedIdMap}\n />\n </div>\n )}\n <div className=\"mt-auto\" ref={formContainerRef}>\n <DailyReportCommentForm reportHubId={report.reportHubId} businessDate={report.date ?? \"\"} onAddComment={handleAddCommentWrapper} />\n </div>\n </div>\n <MetadataPanel report={report} visitTimeDisplay={visitTimeDisplay} categoryLabel={categoryLabel} fieldLabels={fieldLabels} />\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Displays an error banner when daily report detail retrieval fails.\n * 日報詳細の取得に失敗した際のエラーバナーを表示する関数。\n */\nconst DailyReportError = ({ itemId }: { itemId: number }) => (\n <div className=\"rounded-2xl border border-red-200 bg-red-50 p-4 text-red-700 text-sm\">\n <p>日報 {itemId.toLocaleString()} の読み込みに失敗しました。</p>\n </div>\n)\n","/**\n * Client-side DI configuration for the daily-report UI (paths, chrome slots, labels).\n * 日報 UI のクライアント側 DI 設定 (パス・クローム差し込み・ラベル名) を提供するコンテキスト。\n */\nimport { createContext, type ReactNode, useContext, useMemo } from \"react\"\n\n/** ヘッダー描画スロットに渡される引数。 */\nexport type DailyReportHeaderProps = {\n title: string\n annotation?: ReactNode\n}\n\n/**\n * Display labels for the report detail/list fields, sections, and side-pane tabs.\n * 日報の詳細/一覧カードのフィールド・セクション・タブ見出しの表示ラベル群。\n *\n * パッケージは中立な英語ラベルのみを既定として同梱し、ドメイン固有の表現 (言語・業務語彙) は\n * 消費アプリが `fieldLabels` で注入する。これによりパッケージ本体が業務ドメイン知識を持たない。\n */\nexport type DailyReportFieldLabels = {\n id: string\n businessDate: string\n author: string\n visitTime: string\n createdAt: string\n updatedAt: string\n updatedBy: string\n category: string\n categoryInfo: string\n creationCategory: string\n customer: string\n subject: string\n content: string\n interviewers: string\n comments: string\n tabArticle: string\n tabRelations: string\n relationsEmpty: string\n}\n\n/** クライアント層の設定一式。 */\nexport type DailyReportClientConfig = {\n /** 画面タイトル (ヘッダーへ渡す)。 */\n title: string\n /** API ベースパス (例 `/daily_report/api`)。末尾スラッシュ無し。 */\n apiBasePath: string\n /** SSE エンドポイントパス (例 `/sse/daily_report/updates`)。 */\n ssePath: string\n /** 下書き状態を表すラベル名。UI の編集モード判定に使う。既定はプレースホルダーなので消費側で自アプリのラベル名へ上書きする。 */\n draftLabelName: string\n /** 詳細/一覧カードのフィールド見出しラベル。既定は中立英語。アプリが自ドメインの表記を注入する。 */\n fieldLabels: DailyReportFieldLabels\n /** ヘッダー描画スロット。アプリのヘッダーコンポーネントを差し込む。 */\n renderHeader: (props: DailyReportHeaderProps) => ReactNode\n /** 開発向けコントロール (SSE 購読トグル等) を表示するか。 */\n showDevControls: boolean\n}\n\n/**\n * Partial client-config override. `fieldLabels` may itself be partially overridden.\n * クライアント設定の部分上書き型。`fieldLabels` も部分上書き可 (プロバイダーが深くマージする)。\n */\nexport type DailyReportClientConfigInput = Partial<Omit<DailyReportClientConfig, \"fieldLabels\">> & {\n fieldLabels?: Partial<DailyReportFieldLabels>\n}\n\n/** 中立英語のフィールドラベル既定 (ドメイン非依存)。 */\nexport const defaultDailyReportFieldLabels: DailyReportFieldLabels = {\n id: \"ID\",\n businessDate: \"Business date\",\n author: \"Author\",\n visitTime: \"Visit time\",\n createdAt: \"Created at\",\n updatedAt: \"Updated at\",\n updatedBy: \"Updated by\",\n category: \"Category\",\n categoryInfo: \"Category\",\n creationCategory: \"Creation category\",\n customer: \"Customer\",\n subject: \"Subject\",\n content: \"Content\",\n interviewers: \"Attendees\",\n comments: \"Comments\",\n tabArticle: \"Overview\",\n tabRelations: \"Related\",\n relationsEmpty: \"No related view configured yet.\",\n}\n\n/** 既定設定 (消費側は必要なフィールドのみ部分上書きする)。 */\nexport const defaultDailyReportClientConfig: DailyReportClientConfig = {\n title: \"Daily Reports\",\n apiBasePath: \"/daily_report/api\",\n ssePath: \"/sse/daily_report/updates\",\n // 中立プレースホルダー。実運用のラベル名 (アプリ固有) は Provider / config prop で上書きする。\n draftLabelName: \"draft\",\n fieldLabels: defaultDailyReportFieldLabels,\n renderHeader: ({ title, annotation }) => (\n <header className=\"flex flex-wrap items-baseline justify-between gap-2 px-2 py-1\">\n <span className=\"text-nowrap text-slate-700 text-sm dark:text-slate-200\">{title}</span>\n {annotation ? <span className=\"text-slate-600 text-xs dark:text-slate-300\">{annotation}</span> : null}\n </header>\n ),\n showDevControls: false,\n}\n\nconst DailyReportConfigContext = createContext<DailyReportClientConfig>(defaultDailyReportClientConfig)\n\n/**\n * Provides a partial override of the daily-report client configuration.\n * 日報クライアント設定の部分上書きを提供するプロバイダー。\n */\nexport const DailyReportConfigProvider = ({ config, children }: { config?: DailyReportClientConfigInput; children: ReactNode }) => {\n // 部分設定を既定値へ浅くマージする (fieldLabels のみ 1 段深くマージして部分上書きを許す)\n const merged = useMemo<DailyReportClientConfig>(\n () => ({\n ...defaultDailyReportClientConfig,\n ...config,\n fieldLabels: { ...defaultDailyReportFieldLabels, ...(config?.fieldLabels ?? {}) },\n }),\n [config],\n )\n return <DailyReportConfigContext.Provider value={merged}>{children}</DailyReportConfigContext.Provider>\n}\n\n/**\n * Reads the daily-report client configuration (defaults when no provider).\n * 日報クライアント設定を読む (プロバイダー未設置時は既定値)。\n */\nexport const useDailyReportConfig = (): DailyReportClientConfig => useContext(DailyReportConfigContext)\n","/**\n * Action context providing optimistic CRUD + SSE synchronization for daily reports.\n * 日報の楽観的 CRUD と SSE 同期を提供するアクションコンテキスト。\n */\nimport { type Context, createContext, type ReactNode, useCallback, useContext, useEffect, useRef, useState } from \"react\"\nimport { createLogger, LogLevel } from \"../../shared/logger\"\nimport type { DailyReportSseMessage } from \"../../shared/sse-schema\"\nimport type { DailyReportCommentItem, DailyReportDetail, DailyReportItem, DailyReportUser } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\nimport { acquireMutationLock, applyDailyReportServerUpdates, deleteDailyReportCache, getCachedReport, registerRecentDeletion, releaseMutationLock, subscribeCacheReady, updateDailyReportCache, writeCache } from \"../hooks/use-daily-report\"\nimport { useDailyReportSseConnection } from \"../hooks/use-daily-report-sse-connection\"\n\nconst logger = createLogger(LogLevel.INFO, \"daily-report-action-context\")\n\ntype ActionResponse = {\n status: string\n reportHubId: number\n newComment?: DailyReportCommentItem\n updatedStatus?: { isStarred?: boolean; isRead?: boolean }\n deletedCommentId?: string\n updatedAt?: string // ISO string from server\n report?: DailyReportDetail // Full report for creation/updates\n clientTempId?: string // Validation token for optimistic updates\n [key: string]: unknown\n}\n\ntype DailyReportActionContextType = {\n /** 表示中ユーザー (楽観的コメントの作者名等に使用)。 */\n user?: DailyReportUser\n /** reportHubId -> Map<tempCommentId, content> (追加コメントの一時IDと内容) */\n pendingAddCommentIds: React.MutableRefObject<Map<number, Map<number, string>>>\n /** reportHubId -> Set<tempCommentId> (削除された一時ID) */\n pendingDeleteCommentIds: React.MutableRefObject<Map<number, Set<number>>>\n /** reportHubId -> boolean (スターの保留状態) */\n pendingStarUpdates: React.MutableRefObject<Map<number, boolean>>\n /** reportHubId -> boolean (既読の保留状態) */\n pendingReadUpdates: React.MutableRefObject<Map<number, boolean>>\n /** tempId -> realId (解決されたID) */\n resolvedIdMap: React.MutableRefObject<Map<number, number>>\n /** 状態変更通知用バージョン */\n version: number\n /** 日報リスト */\n items: DailyReportItem[]\n /** SSE購読が有効かどうか */\n isSseEnabled: boolean\n /** SSE購読の有効/無効を切り替える */\n toggleSse: () => void\n\n /**\n * コメントを追加します。\n * @param reportHubId 日報HubID\n * @param content コメント内容\n * @param businessDate 営業日\n */\n addComment: (reportHubId: number, content: string, businessDate: string) => Promise<void>\n /**\n * コメントを削除します。\n * @param reportHubId 日報HubID\n * @param commentId コメントID (一時IDまたは実ID)\n * @param businessDate 営業日\n */\n deleteComment: (reportHubId: number, commentId: number, businessDate: string) => Promise<void>\n /**\n * スターの状態を切り替えます。\n * @param reportHubId 日報HubID\n * @param isStarred 新しいスター状態\n * @param businessDate 営業日\n */\n toggleStar: (reportHubId: number, isStarred: boolean, businessDate: string) => Promise<void>\n /**\n * 既読の状態を切り替えます。\n * @param reportHubId 日報HubID\n * @param isRead 新しい既読状態\n * @param businessDate 営業日\n */\n toggleRead: (reportHubId: number, isRead: boolean, businessDate: string) => Promise<void>\n createReport: (businessDate: string) => Promise<number>\n updateReport: (reportHubId: number, data: { title?: string; content?: string }, businessDate: string) => Promise<void>\n publishReport: (reportHubId: number, businessDate: string) => Promise<void>\n deleteReport: (reportHubId: number, businessDate: string) => Promise<void>\n /** stale アイテムをリストから除去する安全弁 */\n removeStaleItem: (reportHubId: number) => void\n}\n\n// HMR / dual-bundle (ESM+CJS) でもコンテキスト identity を一意に保つため、\n// グローバルへ登録した単一インスタンスを常に使用する。\ndeclare global {\n var __DailyReportActionContext: Context<DailyReportActionContextType | null> | undefined\n}\n\nconst DailyReportActionContext: Context<DailyReportActionContextType | null> = globalThis.__DailyReportActionContext ?? createContext<DailyReportActionContextType | null>(null)\nglobalThis.__DailyReportActionContext = DailyReportActionContext\n\nexport const useDailyReportActionContext = () => {\n const context = useContext(DailyReportActionContext)\n if (!context) {\n throw new Error(\"useDailyReportActionContext must be used within a DailyReportActionProvider\")\n }\n return context\n}\n\nconst getUserDisplayName = (user: DailyReportUser | undefined): string => {\n if (!user) return \"Unknown\"\n if (user.displayName?.trim()) return user.displayName\n if (user.name) {\n const fullName = `${user.name.familyName ?? \"\"} ${user.name.givenName ?? \"\"}`.trim()\n if (fullName) return fullName\n }\n return user.emails?.[0]?.value ?? \"Unknown\"\n}\n\n/**\n * Provides a context for daily report actions including comments, stars, leads, and basic CRUD operations.\n * Also manages optimistic UI updates and real-time synchronization via SSE.\n * 日報のアクション(コメント追加・削除、スター、既読)を管理するプロバイダー。\n * 楽観的UI更新とAPI通信の抽象化を提供します。\n */\nexport const DailyReportActionProvider = ({ children, user, initialItems = [], userId }: { children: ReactNode; user?: DailyReportUser; initialItems?: DailyReportItem[]; userId?: string | null }) => {\n const { apiBasePath, draftLabelName } = useDailyReportConfig()\n const pendingAddCommentIds = useRef<Map<number, Map<number, string>>>(new Map())\n const pendingDeleteCommentIds = useRef<Map<number, Set<number>>>(new Map())\n const pendingStarUpdates = useRef<Map<number, boolean>>(new Map())\n const pendingReadUpdates = useRef<Map<number, boolean>>(new Map())\n const resolvedIdMap = useRef<Map<number, number>>(new Map())\n const [version, setVersion] = useState(0)\n const [items, setItems] = useState<DailyReportItem[]>(initialItems)\n const [isSseEnabled, setIsSseEnabled] = useState(!!userId)\n const pendingAddPromises = useRef<Map<number, Promise<number>>>(new Map())\n const manuallyTrackingIds = useRef<Set<number>>(new Set())\n /** 削除済み ID を記録し、マージロジックでの復活を防止する */\n const removedIds = useRef<Set<number>>(new Set())\n /** 自分が発行したアクションのID(SSEエコー無視用) */\n const processedClientTempIds = useRef<Set<string>>(new Set())\n\n /**\n * Queue for SSE messages that arrived before the target report was cached.\n * キャッシュ未取得時に到着した SSE メッセージの待機キュー。\n * key: reportHubId, value: { messages, unsubscribe, timer }\n */\n const pendingSseQueue = useRef<\n Map<\n number,\n {\n messages: Exclude<DailyReportSseMessage, { type: \"connected\" }>[]\n unsubscribe: () => void\n timer: ReturnType<typeof setTimeout>\n }\n >\n >(new Map())\n\n /**\n * Ref to always access the latest handleSseMessage for queue replay.\n * キューの再処理時に常に最新の handleSseMessage を参照するための ref。\n */\n const handleSseMessageRef = useRef<(payload: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => void>(() => {})\n\n /**\n * Enqueue an SSE message for a report that isn't cached yet.\n * Subscribes to cache-ready events and replays queued messages when the cache becomes available.\n *\n * キャッシュ未取得のレポートへの SSE メッセージをキューに追加する。\n * キャッシュ書き込みを監視し、書き込まれたらキューのメッセージを再処理する。\n */\n const enqueuePendingSseMessage = useCallback((reportHubId: number, message: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => {\n const queue = pendingSseQueue.current\n const existing = queue.get(reportHubId)\n\n if (existing) {\n // 既存キューにメッセージを追加するだけ\n existing.messages.push(message)\n return\n }\n\n // 新規キューを作成\n const entry = {\n messages: [message],\n unsubscribe: () => {},\n timer: setTimeout(() => {\n // 30秒経過してもキャッシュが来なければキューを破棄\n const e = queue.get(reportHubId)\n if (e) {\n e.unsubscribe()\n queue.delete(reportHubId)\n logger.warn(`[SSE Queue] Expired: reportHubId=${reportHubId}, dropped=${e.messages.length} messages`)\n }\n }, 30_000),\n }\n\n // キャッシュ書き込みを監視\n entry.unsubscribe = subscribeCacheReady(reportHubId, () => {\n const e = queue.get(reportHubId)\n if (!e) return\n\n // キャッシュが書き込まれたか確認\n if (!getCachedReport(reportHubId, { ignoreExpiry: true })) return\n\n // キューをクリーンアップ\n e.unsubscribe()\n clearTimeout(e.timer)\n queue.delete(reportHubId)\n\n // キューのメッセージを最新の handleSseMessage で再処理\n logger.info(`[SSE Queue] Replaying ${e.messages.length} messages for reportHubId=${reportHubId}`)\n for (const msg of e.messages) {\n handleSseMessageRef.current(msg)\n }\n })\n\n queue.set(reportHubId, entry)\n }, [])\n\n const addProcessedId = (id: string) => {\n processedClientTempIds.current.add(id)\n // Auto-remove after 30 seconds to prevent memory leaks\n // 30秒後に自動削除(メモリリーク防止)\n setTimeout(() => {\n processedClientTempIds.current.delete(id)\n }, 30000)\n }\n\n const isProcessedId = useCallback((id: string | undefined): boolean => {\n return !!id && processedClientTempIds.current.has(id)\n }, [])\n\n /**\n * Upserts a report item into the list while keeping businessDate-desc order.\n *\n * レポートアイテムをリストへ upsert し、businessDate 降順を維持します。\n */\n const upsertItem = useCallback((reportHubId: number, businessDate: string | null | undefined) => {\n setItems((prev) => {\n const nextBusinessDate = businessDate ?? prev.find((item) => item.reportHubId === reportHubId)?.businessDate ?? null\n const exists = prev.some((item) => item.reportHubId === reportHubId)\n const merged = exists ? prev.map((item) => (item.reportHubId === reportHubId ? { ...item, businessDate: nextBusinessDate } : item)) : [{ reportHubId, businessDate: nextBusinessDate }, ...prev]\n\n return merged.sort((a, b) => {\n const left = a.businessDate ?? \"\"\n const right = b.businessDate ?? \"\"\n return right.localeCompare(left)\n })\n })\n }, [])\n\n // 未登録ユーザーは SSE を有効化できない\n const toggleSse = () => {\n if (!userId) return\n setIsSseEnabled((prev) => !prev)\n }\n\n /**\n * Remove a stale item from the list as a safety net.\n * フェッチ後にレポートが見つからなかった場合の安全弁としてアイテムを除去する。\n */\n const removeStaleItem = useCallback((reportHubId: number) => {\n removedIds.current.add(reportHubId)\n setItems((prev) => prev.filter((item) => item.reportHubId !== reportHubId))\n }, [])\n\n /**\n * Merge initialItems with prevItems instead of replacing.\n * Items already in the list are kept even if temporarily absent from the API response.\n * Removal is handled exclusively by the report-delete SSE handler.\n *\n * initialItems と prevItems をマージする(全置換ではない)。\n * API レスポンスから一時的に欠落した ID も prevItems に残っていれば保持する。\n * 削除は report-delete SSE ハンドラーでのみ行う。\n */\n // initialItems と prevItems のマージ — 削除済み ID の復活を防止する\n // 外部リソース同期: React Router loader の initialItems 更新に反応する\n useEffect(() => {\n // initialItems に含まれない removedIds エントリは不要なのでクリーンアップ\n const initialIds = new Set(initialItems.map((item) => item.reportHubId))\n // Set のイテレーション中の delete は JS 仕様で安全 (visited は再訪問しない)\n for (const id of removedIds.current) {\n if (!initialIds.has(id)) {\n removedIds.current.delete(id)\n }\n }\n\n setItems((prevItems) => {\n // initialItems をベースにマップを構築(removedIds を除外)\n const mergedMap = new Map<number, DailyReportItem>()\n for (const item of initialItems) {\n if (!removedIds.current.has(item.reportHubId)) {\n mergedMap.set(item.reportHubId, item)\n }\n }\n\n // prevItems にのみ存在する ID を保持(削除は report-delete SSE で行う)\n for (const item of prevItems) {\n if (!(mergedMap.has(item.reportHubId) || removedIds.current.has(item.reportHubId))) {\n mergedMap.set(item.reportHubId, item)\n }\n }\n\n // businessDate 降順でソート\n return Array.from(mergedMap.values()).sort((a, b) => {\n const dateA = a.businessDate ? new Date(a.businessDate).getTime() : 0\n const dateB = b.businessDate ? new Date(b.businessDate).getTime() : 0\n return dateB - dateA\n })\n })\n }, [initialItems])\n\n const handleSseMessage = useCallback(\n (payload: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => {\n // Ignore echo of own actions\n // 自分が発行したアクションのエコーは無視する\n // clientTempId は connected 以外すべてのメッセージに存在する(型で保証済み)\n if (isProcessedId(payload.clientTempId)) {\n return\n }\n\n switch (payload.type) {\n case \"comment-add\": {\n const { comment, reportHubId } = payload\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n if (cached) {\n // Check for duplicates\n // 重複チェック\n if (cached.commentItems.some((c) => c.id === comment.id)) return\n\n // Check if this comment matches a pending optimistic comment\n let isMyComment = false\n const pendingMap = pendingAddCommentIds.current.get(reportHubId)\n if (pendingMap) {\n for (const [tempId, content] of pendingMap.entries()) {\n if (content === comment.content) {\n pendingMap.delete(tempId)\n resolvedIdMap.current.set(tempId, comment.id)\n isMyComment = true\n break\n }\n }\n }\n\n const newComment: DailyReportCommentItem = {\n ...comment,\n userName: comment.userName || (isMyComment ? getUserDisplayName(user) : \"Unknown\"),\n createdAt: comment.createdAt || new Date().toISOString(),\n isMine: isMyComment, // Use the detected ownership\n }\n\n const updatedReport = {\n ...cached,\n commentItems: [...cached.commentItems, newComment].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()),\n }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n // Update mutation time to prevent stale fetch overwrite\n // 古いフェッチによる上書きを防ぐために変更時刻を更新\n acquireMutationLock(reportHubId)\n releaseMutationLock(reportHubId)\n setVersion((v) => v + 1) // UI更新トリガー\n } else {\n // キャッシュ未取得 → フェッチ完了後にリプレイ\n enqueuePendingSseMessage(reportHubId, payload)\n }\n break\n }\n case \"comment-delete\": {\n const { commentId, reportHubId } = payload\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n if (cached) {\n const updatedReport = {\n ...cached,\n commentItems: cached.commentItems.filter((c) => c.id !== commentId),\n }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n // Update mutation time to prevent stale fetch overwrite\n // 古いフェッチによる上書きを防ぐために変更時刻を更新\n acquireMutationLock(reportHubId)\n releaseMutationLock(reportHubId)\n setVersion((v) => v + 1)\n } else {\n enqueuePendingSseMessage(reportHubId, payload)\n }\n break\n }\n case \"status-update\": {\n const { statusType, value, reportHubId } = payload\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n\n if (statusType === \"star\") {\n if (pendingStarUpdates.current.get(reportHubId) === value) {\n pendingStarUpdates.current.delete(reportHubId)\n }\n if (cached) {\n const updatedReport = { ...cached, isStarred: value }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n setVersion((v) => v + 1)\n } else {\n enqueuePendingSseMessage(reportHubId, payload)\n }\n } else if (statusType === \"read\") {\n if (pendingReadUpdates.current.get(reportHubId) === value) {\n pendingReadUpdates.current.delete(reportHubId)\n }\n if (cached) {\n const updatedReport = { ...cached, isRead: value }\n applyDailyReportServerUpdates(reportHubId, updatedReport)\n setVersion((v) => v + 1)\n } else {\n enqueuePendingSseMessage(reportHubId, payload)\n }\n }\n break\n }\n case \"report-delete\": {\n const { reportHubId } = payload\n manuallyTrackingIds.current.delete(reportHubId)\n // マージロジックでの復活を防止するために削除済み ID を記録\n removedIds.current.add(reportHubId)\n\n // 削除されたレポートへの待機中 SSE メッセージを破棄\n const pendingEntry = pendingSseQueue.current.get(reportHubId)\n if (pendingEntry) {\n pendingEntry.unsubscribe()\n clearTimeout(pendingEntry.timer)\n pendingSseQueue.current.delete(reportHubId)\n }\n\n const cached = getCachedReport(reportHubId, { ignoreExpiry: true })\n if (cached?.date) {\n deleteDailyReportCache(reportHubId, cached.date)\n setVersion((v) => v + 1)\n }\n setItems((prev) => prev.filter((item) => item.reportHubId !== reportHubId))\n break\n }\n case \"report-create\":\n case \"report-update\":\n case \"report-publish\": {\n // report はスキーマで必須なのでバリデーション通過後は必ず存在する(if ガード不要)\n const { report } = payload\n applyDailyReportServerUpdates(report.reportHubId, report)\n if (payload.type === \"report-create\" || payload.type === \"report-publish\") {\n manuallyTrackingIds.current.add(report.reportHubId)\n upsertItem(report.reportHubId, report.date)\n }\n setVersion((v) => v + 1)\n break\n }\n }\n },\n [isProcessedId, upsertItem, user, enqueuePendingSseMessage],\n )\n\n // 常に最新の handleSseMessage をキューのリプレイから参照できるようにする\n handleSseMessageRef.current = handleSseMessage\n\n /**\n * Cleanup all pending SSE queues on unmount.\n * アンマウント時に全キューをクリーンアップする。\n */\n useEffect(() => {\n return () => {\n for (const entry of pendingSseQueue.current.values()) {\n entry.unsubscribe()\n clearTimeout(entry.timer)\n }\n pendingSseQueue.current.clear()\n }\n }, [])\n\n useDailyReportSseConnection({ isSseEnabled, onMessage: handleSseMessage })\n\n /**\n * Common helper function to execute API actions.\n * APIアクションを実行する共通ヘルパー関数。\n */\n const executeAction = async <T,>(\n reportHubId: number,\n intent: string,\n businessDate: string,\n params: Record<string, string>,\n callbacks: {\n onOptimistic?: () => void\n onSuccess?: (data: ActionResponse) => Promise<T> | T\n onFailure?: () => void\n },\n options?: {\n keepLock?: boolean\n skipLock?: boolean\n },\n ): Promise<T> => {\n if (!options?.skipLock) acquireMutationLock(reportHubId)\n callbacks.onOptimistic?.()\n\n // Generate or use provided clientTempId\n const clientTempId = params.clientTempId || crypto.randomUUID()\n addProcessedId(clientTempId)\n\n const formData = new FormData()\n formData.append(\"intent\", intent)\n formData.append(\"reportHubId\", String(reportHubId))\n formData.append(\"businessDate\", businessDate)\n formData.append(\"operationTimestamp\", String(Date.now()))\n formData.append(\"clientTempId\", clientTempId)\n\n for (const [k, v] of Object.entries(params)) {\n if (k !== \"clientTempId\") {\n formData.append(k, v)\n }\n }\n\n try {\n const res = await fetch(`${apiBasePath}/action`, {\n method: \"POST\",\n body: formData,\n headers: { Accept: \"application/json\" },\n })\n if (!res.ok) {\n const error = new Error(`Failed to ${intent}`)\n Object.assign(error, { status: res.status })\n throw error\n }\n const data = await res.json()\n if (data.status !== \"OK\" || (intent !== \"create\" && String(data.reportHubId) !== String(reportHubId))) {\n throw new Error(`Invalid response for ${intent}`)\n }\n if (typeof data.clientTempId !== \"string\" || data.clientTempId !== clientTempId) {\n throw new Error(`Invalid clientTempId for ${intent}`)\n }\n return (await callbacks.onSuccess?.(data)) as T\n } catch (e) {\n logger.error(`${intent} failed:`, e)\n callbacks.onFailure?.()\n throw e\n } finally {\n if (!(options?.keepLock || options?.skipLock)) {\n releaseMutationLock(reportHubId)\n }\n }\n }\n\n const addComment = async (reportHubId: number, content: string, businessDate: string) => {\n const id = Number(reportHubId)\n const tempId = -1 * Date.now()\n const promise = executeAction(\n id,\n \"addComment\",\n businessDate,\n { content, clientTempId: String(tempId) },\n {\n onOptimistic: () => {\n if (!pendingAddCommentIds.current.has(id)) pendingAddCommentIds.current.set(id, new Map())\n pendingAddCommentIds.current.get(id)?.set(tempId, content)\n setVersion((v) => v + 1)\n },\n onSuccess: (data) => {\n if (!data.newComment) throw new Error(\"No new comment returned\")\n const realId = data.newComment.id\n resolvedIdMap.current.set(tempId, realId)\n pendingAddCommentIds.current.get(id)?.delete(tempId)\n\n const cachedReport = getCachedReport(id, { ignoreExpiry: true })\n if (cachedReport && !cachedReport.commentItems.some((c) => c.id === realId)) {\n updateDailyReportCache(id, {\n commentItems: [...cachedReport.commentItems, { ...data.newComment, id: realId, isMine: true, userName: getUserDisplayName(user) }],\n updatedAt: data.updatedAt,\n })\n }\n return realId\n },\n onFailure: () => {\n pendingAddCommentIds.current.get(id)?.delete(tempId)\n },\n },\n { skipLock: true },\n ).finally(() => pendingAddPromises.current.delete(tempId))\n\n pendingAddPromises.current.set(tempId, promise)\n await promise\n }\n\n const deleteComment = async (reportHubId: number, commentId: number, businessDate: string) => {\n const id = Number(reportHubId)\n // 1. Optimistic UI Update\n if (!pendingDeleteCommentIds.current.has(id)) pendingDeleteCommentIds.current.set(id, new Set())\n pendingDeleteCommentIds.current.get(id)?.add(commentId)\n setVersion((v) => v + 1)\n\n // 2. Resolve ID\n let realId = commentId\n if (commentId < 0) {\n const resolved = resolvedIdMap.current.get(commentId)\n if (resolved !== undefined) {\n realId = resolved\n } else {\n const promise = pendingAddPromises.current.get(commentId)\n if (!promise) return // 既に削除済みかエラー\n try {\n realId = await promise\n } catch {\n return // 追加失敗時は削除不要\n }\n }\n }\n\n // 3. Server Request\n await executeAction(\n id,\n \"deleteComment\",\n businessDate,\n { commentId: String(realId) },\n {\n onSuccess: (data) => {\n const deletedId = typeof data.deletedCommentId === \"string\" ? Number(data.deletedCommentId) : realId\n const cachedReport = getCachedReport(id, { ignoreExpiry: true })\n if (cachedReport) {\n updateDailyReportCache(id, {\n commentItems: cachedReport.commentItems.filter((c) => c.id !== deletedId),\n updatedAt: data.updatedAt,\n })\n registerRecentDeletion(deletedId)\n }\n pendingDeleteCommentIds.current.get(id)?.delete(commentId)\n },\n onFailure: () => {\n pendingDeleteCommentIds.current.get(id)?.delete(commentId)\n },\n },\n { skipLock: true },\n )\n }\n\n const toggleStar = async (reportHubId: number, isStarred: boolean, businessDate: string) => {\n const id = Number(reportHubId)\n await executeAction(\n id,\n \"toggleStar\",\n businessDate,\n { isStarred: String(isStarred) },\n {\n onOptimistic: () => {\n updateDailyReportCache(id, { isStarred })\n pendingStarUpdates.current.set(id, isStarred)\n setVersion((v) => v + 1)\n },\n onSuccess: (data) => {\n if (data.updatedStatus && typeof data.updatedStatus.isStarred === \"boolean\") {\n const cached = getCachedReport(id, { ignoreExpiry: true })\n if (cached) {\n updateDailyReportCache(id, { isStarred: data.updatedStatus.isStarred, updatedAt: data.updatedAt })\n setVersion((v) => v + 1)\n }\n }\n pendingStarUpdates.current.delete(id)\n },\n onFailure: () => {\n updateDailyReportCache(id, { isStarred: !isStarred })\n pendingStarUpdates.current.delete(id)\n },\n },\n { skipLock: true },\n )\n }\n\n const toggleReadImpl = async (reportHubId: number, isRead: boolean, businessDate: string) => {\n const id = Number(reportHubId)\n await executeAction(\n id,\n \"toggleRead\",\n businessDate,\n { isRead: String(isRead) },\n {\n onOptimistic: () => {\n updateDailyReportCache(id, { isRead })\n pendingReadUpdates.current.set(id, isRead)\n setVersion((v) => v + 1)\n },\n onSuccess: (data) => {\n if (data.updatedStatus && typeof data.updatedStatus.isRead === \"boolean\") {\n const cached = getCachedReport(id, { ignoreExpiry: true })\n if (cached) {\n updateDailyReportCache(id, { isRead: data.updatedStatus.isRead, updatedAt: data.updatedAt })\n setVersion((v) => v + 1)\n }\n }\n pendingReadUpdates.current.delete(id)\n },\n onFailure: () => {\n updateDailyReportCache(id, { isRead: !isRead })\n pendingReadUpdates.current.delete(id)\n },\n },\n { skipLock: true },\n )\n }\n // ref 経由で最新実装を参照し、安定した関数 identity を提供する\n // (SSE イベントによるコンテキスト再レンダリングで auto-read タイマーがリセットされるのを防止)\n const toggleReadRef = useRef(toggleReadImpl)\n toggleReadRef.current = toggleReadImpl\n const toggleRead = useCallback((reportHubId: number, isRead: boolean, businessDate: string) => toggleReadRef.current(reportHubId, isRead, businessDate), [])\n\n const createReport = async (businessDate: string) => {\n const tempId = -1 * Date.now()\n // Optimistic UI update\n const now = new Date().toISOString()\n const dummyReport: DailyReportDetail = {\n reportHubId: tempId,\n date: businessDate,\n createdAt: now,\n author: getUserDisplayName(user),\n userId: user?.id ?? \"unknown\",\n employeeName: getUserDisplayName(user),\n updatedBy: getUserDisplayName(user),\n updatedAt: now,\n category: null,\n creationCategory: null,\n visitTimeFrom: null,\n visitTimeTo: null,\n customerName: null,\n interviewers: [],\n subject: \"\",\n content: \"\",\n comments: [],\n isRead: true,\n isStarred: false,\n labels: [{ id: 0, name: draftLabelName, color: \"#9ca3af\" }],\n commentItems: [],\n }\n writeCache(dummyReport, Date.now() + 300000, false)\n\n setItems((prev) => [\n {\n reportHubId: tempId,\n businessDate: businessDate,\n },\n ...prev,\n ])\n\n try {\n const res = await executeAction<ActionResponse>(\n 0,\n \"create\",\n businessDate,\n { clientTempId: String(tempId) },\n {\n onSuccess: (data) => data,\n },\n )\n // Validate response mapping\n if (res.clientTempId && res.clientTempId !== String(tempId)) {\n logger.warn(`[createReport] ID mismatch. req=${tempId}, res=${res.clientTempId}`)\n // Mismatch implies this response is not for this specific request. Throwing here triggers revert.\n throw new Error(\"Optimistic ID mismatch\")\n }\n\n const newId = Number(res.reportHubId)\n\n // newId を一時的にロックし、createReport 以前に開始された背景 prefetch の\n // cacheMany による stale データ上書きを防止する\n acquireMutationLock(newId)\n\n // Seed cache with server response (checking staleness)\n if (res.report) {\n applyDailyReportServerUpdates(newId, res.report)\n setVersion((v) => v + 1)\n }\n\n setItems((prev) => {\n // If newId is already present (e.g. from SSE), just remove tempId to avoid duplication\n if (prev.some((item) => item.reportHubId === newId)) {\n return prev.filter((item) => item.reportHubId !== tempId)\n }\n return prev.map((item) => (item.reportHubId === tempId ? { ...item, reportHubId: newId } : item))\n })\n\n // ロック解放: lastMutations に newId が記録され、以降の cacheMany でも保護される\n setTimeout(() => releaseMutationLock(newId), 1000)\n\n return newId\n } catch (e) {\n logger.error(\"[createReport] Failed or mismatched\", e)\n // Revert optimistic update\n setItems((prev) => prev.filter((item) => item.reportHubId !== tempId))\n throw e\n }\n }\n\n const updateReport = async (reportHubId: number, data: { title?: string; content?: string }, businessDate: string) => {\n const id = Number(reportHubId)\n await executeAction(\n id,\n \"update\",\n businessDate,\n { ...(data.title && { title: data.title }), ...(data.content && { content: data.content }) },\n {\n onSuccess: () => {\n const cached = getCachedReport(id, { ignoreExpiry: true })\n if (cached) {\n updateDailyReportCache(id, {\n subject: data.title ?? cached.subject,\n content: data.content ?? cached.content,\n })\n setVersion((v) => v + 1)\n }\n },\n },\n )\n }\n\n const publishReport = async (reportHubId: number, businessDate: string) => {\n const id = Number(reportHubId)\n const cached = getCachedReport(id, { ignoreExpiry: true })\n const oldLabels = cached?.labels || []\n\n await executeAction(\n id,\n \"publish\",\n businessDate,\n {},\n {\n onOptimistic: () => {\n if (cached) {\n const newLabels = cached.labels.filter((l) => l.name !== draftLabelName)\n updateDailyReportCache(id, {\n labels: newLabels,\n })\n setVersion((v) => v + 1)\n }\n },\n onSuccess: (data) => {\n // サーバーからのレスポンスを正とする\n if (data.report) {\n applyDailyReportServerUpdates(id, data.report)\n setVersion((v) => v + 1)\n }\n },\n onFailure: () => {\n // ロールバック\n updateDailyReportCache(id, {\n labels: oldLabels,\n })\n setVersion((v) => v + 1)\n },\n },\n { skipLock: true },\n )\n }\n\n const deleteReport = async (reportHubId: number, businessDate: string) => {\n const id = Number(reportHubId)\n const targetItem = items.find((i) => i.reportHubId === id)\n\n // マージロジックでの復活を防止するために削除済み ID を記録\n removedIds.current.add(id)\n // Optimistic UI update\n setItems((prev) => prev.filter((item) => item.reportHubId !== id))\n\n try {\n await executeAction(\n id,\n \"delete\",\n businessDate,\n {},\n {\n onSuccess: (_data) => {\n deleteDailyReportCache(id, businessDate)\n // Note: Item already removed from list optimistically\n },\n },\n )\n } catch (e: unknown) {\n const error = e as { status?: number }\n // 既に削除済み(404)の場合は、成功とみなしてロールバックしない(キャッシュはクリアする)\n if (error.status === 404) {\n deleteDailyReportCache(id, businessDate)\n return\n }\n\n // それ以外のエラーの場合は、楽観的更新をロールバックする\n removedIds.current.delete(id)\n if (targetItem) {\n setItems((prev) => {\n if (prev.some((i) => i.reportHubId === id)) return prev\n return [targetItem, ...prev].sort((a, b) => (b.businessDate ?? \"\").localeCompare(a.businessDate ?? \"\"))\n })\n }\n }\n }\n\n return (\n <DailyReportActionContext.Provider\n value={{\n user,\n pendingAddCommentIds,\n pendingDeleteCommentIds,\n pendingStarUpdates,\n pendingReadUpdates,\n resolvedIdMap,\n version,\n items,\n isSseEnabled,\n toggleSse,\n addComment,\n deleteComment,\n toggleStar,\n toggleRead,\n createReport,\n updateReport,\n publishReport,\n deleteReport,\n removeStaleItem,\n }}>\n {children}\n </DailyReportActionContext.Provider>\n )\n}\n","/**\n * Minimal level-filtered logger used internally by the daily-report package.\n * daily-report パッケージ内部で使う最小のレベルフィルタ付きロガー。\n *\n * 消費アプリのロガー実装 (console 互換の debug/info/warn/error) を注入でき、\n * 未注入時は console にフォールバックする。\n */\n\n/** 出力フィルタリング用のログレベル。 */\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\n/** Console 互換のロガーインターフェース。 */\nexport interface DailyReportLogger {\n debug(message?: unknown, ...optionalParams: unknown[]): void\n info(message?: unknown, ...optionalParams: unknown[]): void\n warn(message?: unknown, ...optionalParams: unknown[]): void\n error(message?: unknown, ...optionalParams: unknown[]): void\n}\n\n/**\n * Creates a prefixed, level-filtered logger delegating to the given implementation.\n * 指定実装へ委譲するプレフィックス付き・レベルフィルタ付きロガーを生成する処理。\n */\nexport const createLogger = (level: LogLevel, prefix: string, impl: DailyReportLogger = console): DailyReportLogger => {\n // 文字列メッセージはプレフィックスを連結、それ以外は先頭引数として付加する\n const format = (message: unknown): unknown[] => (typeof message === \"string\" ? [`${prefix} ${message}`] : [prefix, message])\n return {\n debug: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.DEBUG) impl.debug(...format(message), ...rest)\n },\n info: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.INFO) impl.info(...format(message), ...rest)\n },\n warn: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.WARN) impl.warn(...format(message), ...rest)\n },\n error: (message?: unknown, ...rest: unknown[]) => {\n if (level <= LogLevel.ERROR) impl.error(...format(message), ...rest)\n },\n }\n}\n","/**\n * Suspense-friendly daily report detail loader (fetch-driven).\n * fetch ベースの日報詳細ローダー。\n */\nimport { useCallback, useEffect, useRef, useState } from \"react\"\nimport { normalizeBusinessDateKey } from \"../../shared/business-date\"\nimport { createLogger, LogLevel } from \"../../shared/logger\"\nimport type { DailyReportDetail } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\n\nconst logger = createLogger(LogLevel.INFO, \"use-daily-report\")\n\n// --- Configuration & Constants ---\n/**\n * Retrieve global configuration or default.\n * グローバル設定またはデフォルト値を取得する。\n */\nconst getGlobal = (k: string, d: number): number => {\n if (typeof window === \"undefined\") return d\n const v = (window as unknown as Record<string, unknown>)[k]\n return typeof v === \"number\" && !Number.isNaN(v) ? v : d\n}\nconst TTL = getGlobal(\"__DAILY_REPORT_CACHE_TTL__\", 300_000)\nconst REVIVE_MS = getGlobal(\"__DAILY_REPORT_CACHE_REVIVE_MS__\", 60_000)\nconst FETCH_DEBOUNCE_MS = 120\n\n// --- Cache Store (Centralized State) ---\ntype CacheEntry<T> = { data: T; expiresAt: number }\n\nconst DailyReportCache = {\n reports: new Map<number, CacheEntry<DailyReportDetail>>(),\n lists: new Map<string, CacheEntry<DailyReportDetail[]>>(),\n pendingLists: new Map<string, Promise<DailyReportDetail[]>>(),\n pendingReports: new Map<number, Promise<DailyReportDetail>>(),\n listeners: new Set<(id: number) => void>(),\n locks: new Set<number>(),\n lastMutations: new Map<number, number>(),\n recentDeletes: new Map<number, number>(),\n lastPrune: 0,\n\n /**\n * Prune expired entries from the cache.\n * キャッシュから期限切れのエントリを削除する。\n */\n prune() {\n const now = Date.now()\n // Prune at most once per minute\n if (now - this.lastPrune < 60_000) return\n this.lastPrune = now\n\n // SWR のために古いデータを保持する。TTL 超過後すぐには削除せず、十分に古い(例えば 1時間経過した)データのみを破棄する\n const GC_THRESHOLD = 60 * 60 * 1000 // 1 hour\n for (const [id, entry] of this.reports) {\n if (now - entry.expiresAt > GC_THRESHOLD) this.reports.delete(id)\n }\n for (const [key, entry] of this.lists) {\n if (now - entry.expiresAt > GC_THRESHOLD) this.lists.delete(key)\n }\n },\n\n /**\n * Check if the cache entry is still valid.\n * キャッシュエントリがまだ有効かを確認する。\n */\n isValid: (ex: number) => ex > Date.now(),\n /**\n * Notify all listeners of a report update.\n * レポートの更新をすべてのリスナーに通知する。\n */\n notify: (id: number) => {\n DailyReportCache.listeners.forEach((l) => {\n l(id)\n })\n },\n\n /**\n * Retrieve a cached report by ID.\n * ID でキャッシュされたレポートを取得する。\n */\n getReport(id: number, ignoreExpiry = false) {\n const e = this.reports.get(id)\n return e && (ignoreExpiry || this.isValid(e.expiresAt)) ? e.data : null\n },\n\n /**\n * Retrieve a cached list of reports by business date key.\n * 営業日キーでレポートのキャッシュリストを取得する。\n */\n getList(dateKey: string | null, ignoreExpiry = false) {\n const e = dateKey && this.lists.get(dateKey)\n return e && (ignoreExpiry || this.isValid(e.expiresAt)) ? e.data : null\n },\n\n /**\n * Set a report in the cache and optionally sync with the list cache.\n * キャッシュにレポートを設定し、必要に応じてリストキャッシュと同期する。\n */\n set(report: DailyReportDetail, expiresAt: number, syncList = true) {\n this.reports.set(report.reportHubId, { data: report, expiresAt })\n if (syncList) {\n const dateKey = normalizeBusinessDateKey(report.date)\n const listEntry = dateKey ? this.lists.get(dateKey) : null\n if (listEntry && dateKey) {\n const exists = listEntry.data.some((r) => r.reportHubId === report.reportHubId)\n const data = exists ? listEntry.data.map((r) => (r.reportHubId === report.reportHubId ? report : r)) : [report, ...listEntry.data]\n this.lists.set(dateKey, { ...listEntry, data })\n }\n }\n this.notify(report.reportHubId)\n },\n\n /**\n * Update a cached report with partial data.\n * 部分的なデータでキャッシュされたレポートを更新する。\n */\n update(id: number, updates: Partial<DailyReportDetail>, strict = true) {\n const entry = this.reports.get(id)\n if (!entry?.data) return\n\n if (strict && updates.updatedAt && entry.data.updatedAt) {\n if (new Date(updates.updatedAt).getTime() < new Date(entry.data.updatedAt).getTime()) {\n logger.warn(`[DailyReportCache] Stale update skipped id=${id}`)\n return\n }\n }\n this.set({ ...entry.data, ...updates } as DailyReportDetail, Math.max(entry.expiresAt, Date.now() + REVIVE_MS))\n },\n\n /**\n * Delete a report from the cache and update the corresponding list.\n * キャッシュからレポートを削除し、対応するリストを更新する。\n */\n delete(id: number, date: string) {\n this.reports.delete(id)\n const dateKey = normalizeBusinessDateKey(date)\n const list = dateKey && this.lists.get(dateKey)\n if (list && dateKey) {\n this.lists.set(dateKey, { ...list, data: list.data.filter((r) => r.reportHubId !== id) })\n }\n this.notify(id)\n },\n\n /**\n * Merge server report with cached report to handle optimistic updates and zombies.\n * サーバーレポートとキャッシュレポートをマージして、楽観的更新とゾンビコメントを処理する。\n */\n merge(serverReport: DailyReportDetail, cachedReport?: DailyReportDetail | null) {\n let report = serverReport\n // Filter zombies\n if (report.commentItems.some((c) => this.recentDeletes.has(c.id))) {\n report = { ...report, commentItems: report.commentItems.filter((c) => !this.recentDeletes.has(c.id)) }\n }\n // Restore missing \"my comments\"\n if (cachedReport) {\n const serverIds = new Set(report.commentItems.map((c) => c.id))\n const missing = cachedReport.commentItems.filter((c) => !serverIds.has(c.id) && c.isMine && Date.now() - new Date(c.createdAt).getTime() < 10000)\n if (missing.length > 0) {\n report = {\n ...report,\n commentItems: [...report.commentItems, ...missing].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()),\n }\n }\n }\n return report\n },\n\n /**\n * Hydrate the cache with fetched reports, handling comment merges and locks.\n * フェッチされたレポートでキャッシュをハイドレートし、コメントのマージとロックを処理する。\n */\n cacheMany(reports: DailyReportDetail[], dateKey: string | null, fetchStart = 0) {\n if (!reports.length) return []\n this.prune()\n const now = Date.now(),\n expiresAt = now + TTL\n for (const [id, t] of this.recentDeletes) if (now - t > 10000) this.recentDeletes.delete(id)\n\n const processed = reports.map((r) => {\n // Original logic: If locked, return server data directly (don't update cache, don't merge)\n // ロックされている場合はサーバーデータをそのまま返す(キャッシュ更新せず、マージもしない)\n if (this.locks.has(r.reportHubId)) return r\n\n // Original logic: If recently mutated locally, prefer cached data\n // 最近ローカルで変更された場合は、キャッシュデータを優先する\n const lastMut = this.lastMutations.get(r.reportHubId)\n if (lastMut && fetchStart > 0 && lastMut > fetchStart) {\n return this.reports.get(r.reportHubId)?.data ?? r\n }\n\n const cached = this.reports.get(r.reportHubId)?.data\n const merged = this.merge(r, cached)\n this.set(merged, expiresAt, false)\n return merged\n })\n if (dateKey) this.lists.set(dateKey, { data: processed, expiresAt })\n return processed\n },\n}\n\n// --- Public API Exports ---\n/**\n * Update the cache for a single report.\n * 単一レポートのキャッシュを更新する。\n */\nexport const writeCache = (r: DailyReportDetail, exp: number, sync = true) => DailyReportCache.set(r, exp, sync)\n/**\n * Get a cached report by ID.\n * ID でキャッシュされたレポートを取得する。\n */\nexport const getCachedReport = (id: number, opts?: { ignoreExpiry?: boolean }) => DailyReportCache.getReport(id, opts?.ignoreExpiry)\n/**\n * Update specific fields of a cached report.\n * キャッシュされたレポートの特定のフィールドを更新する。\n */\nexport const updateDailyReportCache = (id: number, u: Partial<DailyReportDetail>) => DailyReportCache.update(id, u)\n/**\n * Delete a report from the cache.\n * キャッシュからレポートを削除する。\n */\nexport const deleteDailyReportCache = (id: number, d: string) => DailyReportCache.delete(id, d)\n\nexport const acquireMutationLock = (id: number) => {\n DailyReportCache.locks.add(id)\n DailyReportCache.lastMutations.set(id, Date.now())\n}\nexport const releaseMutationLock = (id: number) => {\n DailyReportCache.locks.delete(id)\n DailyReportCache.lastMutations.set(id, Date.now())\n}\nexport const registerRecentDeletion = (cId: number) => DailyReportCache.recentDeletes.set(cId, Date.now())\n\n/**\n * Subscribe to cache-ready events for a specific report.\n * Returns an unsubscribe function.\n *\n * 特定レポートのキャッシュ書き込みを監視する。\n * 解除関数を返す。\n */\nexport const subscribeCacheReady = (reportHubId: number, callback: () => void): (() => void) => {\n const listener = (id: number) => {\n if (id === reportHubId) {\n callback()\n }\n }\n DailyReportCache.listeners.add(listener)\n return () => {\n DailyReportCache.listeners.delete(listener)\n }\n}\n\nexport const applyDailyReportServerUpdates = (_reportHubIdd: number, report: DailyReportDetail) => {\n DailyReportCache.prune()\n DailyReportCache.set(report, Date.now() + TTL)\n}\n\n// --- Data Loading ---\n/**\n * Fetch a single report by ID.\n * ID で単一のレポートを取得する。\n */\nconst fetchReportById = async (apiBasePath: string, id: number) => {\n // 1. Cache Check\n const cached = DailyReportCache.getReport(id)\n if (cached) return cached\n\n // 2. Prevent Duplicate Requests\n const pending = DailyReportCache.pendingReports.get(id)\n if (pending) return pending\n\n // 3. Fetch\n const start = Date.now()\n const p = fetch(`${apiBasePath}/report?reportHubId=${id}`, { cache: \"no-cache\" })\n .then((res) => (res.ok ? res.json() : Promise.reject(`HTTP ${res.status}`)))\n .then((data) => {\n if (data.error) throw new Error(data.error.message)\n const report = data.report as DailyReportDetail\n // 単一レポートの取得時にキャッシュを一括更新する\n return DailyReportCache.cacheMany([report], null, start)[0]\n })\n .finally(() => DailyReportCache.pendingReports.delete(id))\n DailyReportCache.pendingReports.set(id, p)\n return p\n}\n\n/**\n * Fetch reports by date key.\n * 日付キーでレポートを取得する。\n */\nconst fetchReportsByDate = async (apiBasePath: string, dateKey: string) => {\n const list = DailyReportCache.getList(dateKey)\n if (list) return list\n\n const pending = DailyReportCache.pendingLists.get(dateKey)\n if (pending) return pending\n\n const start = Date.now()\n const p = fetch(`${apiBasePath}/business-date?businessDate=${encodeURIComponent(dateKey)}`, { cache: \"no-cache\" })\n .then((res) => (res.ok ? res.json() : Promise.reject(`HTTP ${res.status}`)))\n .then((data) => {\n if (data.error) throw new Error(data.error.message)\n return DailyReportCache.cacheMany(data.reports ?? [], dateKey, start)\n })\n .finally(() => DailyReportCache.pendingLists.delete(dateKey))\n DailyReportCache.pendingLists.set(dateKey, p)\n return p\n}\n\n// --- Hooks ---\nexport type DailyReportDetailResource = { report: DailyReportDetail | null; error: Error | null; isLoading: boolean; isRefetching: boolean }\n\n/**\n * Provides a fetcher-driven daily report detail reader with business-date aware caching.\n * 営業日キャッシュ対応の日報詳細取得フック。\n */\nexport const useDailyReportDetail = (reportHubId: number, businessDate: string | null): DailyReportDetailResource => {\n const { apiBasePath } = useDailyReportConfig()\n const [report, setReport] = useState<DailyReportDetail | null>(() => DailyReportCache.getReport(reportHubId))\n const [error, setError] = useState<Error | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [isRefetching, setIsRefetching] = useState(false)\n const reqRef = useRef({ id: reportHubId, token: 0 })\n\n // 1. Cache Subscription\n // キャッシュの購読: グローバルな DailyReportCache が更新されたときにローカルの状態を反応的に更新する。\n // 依存関係: [reportHubId] - 対象のレポート ID が変更されたときのみ再購読する。\n // クリーンアップ: メモリリークを防ぐためにリスナーを解除する。\n useEffect(() => {\n const onUpdate = (uid: number) => {\n if (uid === reportHubId) {\n const r = DailyReportCache.getReport(reportHubId, true)\n setReport(r)\n }\n }\n DailyReportCache.listeners.add(onUpdate)\n return () => {\n DailyReportCache.listeners.delete(onUpdate)\n }\n }, [reportHubId])\n\n // 2. Data Loading & Stale Check\n // データのロードと有効期限切れチェック: データのフェッチサイクル、 stale-while-revalidate ロジック、リクエストのデバウンスを管理する。\n // 依存関係: [reportHubId, businessDate, apiBasePath]\n useEffect(() => {\n const dateKey = normalizeBusinessDateKey(businessDate)\n const token = (reqRef.current.token || 0) + 1\n reqRef.current = { id: reportHubId, token }\n\n if (reportHubId <= 0) {\n setReport(null)\n setError(reportHubId === 0 ? new Error(\"Invalid ID\") : null)\n setIsLoading(false)\n setIsRefetching(false)\n return\n }\n\n // Cache Check\n let cached = DailyReportCache.getReport(reportHubId, true)\n let isStale = false\n if (cached) {\n isStale = !DailyReportCache.isValid(DailyReportCache.reports.get(reportHubId)?.expiresAt ?? 0)\n } else if (dateKey) {\n const list = DailyReportCache.getList(dateKey, true)\n const found = list?.find((r) => r.reportHubId === reportHubId)\n if (found) {\n cached = found\n isStale = !DailyReportCache.isValid(DailyReportCache.lists.get(dateKey)?.expiresAt ?? 0)\n }\n }\n setReport(cached)\n\n // If cached and valid, we are done\n if (cached && !isStale) {\n setError(null)\n setIsLoading(false)\n setIsRefetching(false)\n return\n }\n\n setIsLoading(!cached)\n setIsRefetching(true)\n\n const timer = setTimeout(async () => {\n if (reqRef.current.token !== token) return\n try {\n let r: DailyReportDetail | null = null\n if (dateKey) {\n const res = await fetchReportsByDate(apiBasePath, dateKey)\n r = res.find((item) => item.reportHubId === reportHubId) ?? null\n } else {\n r = await fetchReportById(apiBasePath, reportHubId)\n }\n\n if (reqRef.current.token === token) {\n setReport(r)\n setError(null)\n }\n } catch (e) {\n if (reqRef.current.token === token) setError(e instanceof Error ? e : new Error(\"Load failed\"))\n } finally {\n if (reqRef.current.token === token) {\n setIsLoading(false)\n setIsRefetching(false)\n }\n }\n }, FETCH_DEBOUNCE_MS)\n\n return () => clearTimeout(timer)\n }, [reportHubId, businessDate, apiBasePath])\n\n return { report, error, isLoading, isRefetching }\n}\n\n/**\n * Provides a business-date aware prefetch helper.\n * 営業日キャッシュを活用した日報プリフェッチ補助フック。\n */\nexport const useDailyReportPrefetch = () => {\n const { apiBasePath } = useDailyReportConfig()\n return useCallback(\n (date: string | null) => {\n const k = normalizeBusinessDateKey(date)\n if (k && !DailyReportCache.getList(k)) fetchReportsByDate(apiBasePath, k).catch((e) => logger.warn(\"Prefetch error\", k, e))\n },\n [apiBasePath],\n )\n}\n","/**\n * Business date normalization and formatting helper functions.\n * 営業日の正規化とフォーマットを支援するユーティリティ群。\n */\n\ntype BusinessDateInput = string | Date | null | undefined\n\nconst ISO_DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst SLASH_DATE_PATTERN = /^\\d{4}\\/\\d{2}\\/\\d{2}$/\n\nconst zeroPad = (value: number): string => {\n return value < 10 ? `0${value}` : `${value}`\n}\n\nconst normalizeFromDate = (value: Date): string | null => {\n if (Number.isNaN(value.getTime())) {\n return null\n }\n\n const year = value.getFullYear()\n const month = zeroPad(value.getMonth() + 1)\n const day = zeroPad(value.getDate())\n return `${year}-${month}-${day}`\n}\n\n/**\n * Normalizes business date input into YYYY-MM-DD format string.\n * 営業日を YYYY-MM-DD 形式の文字列へ正規化する。\n */\nexport const normalizeBusinessDateKey = (value: BusinessDateInput): string | null => {\n if (value === null || value === undefined) {\n return null\n }\n\n if (value instanceof Date) {\n return normalizeFromDate(value)\n }\n\n const trimmed = value.trim()\n if (trimmed === \"\") {\n return null\n }\n\n if (ISO_DATE_PATTERN.test(trimmed)) {\n return trimmed\n }\n\n if (SLASH_DATE_PATTERN.test(trimmed)) {\n return trimmed.replaceAll(\"/\", \"-\")\n }\n\n const parsed = new Date(trimmed)\n return normalizeFromDate(parsed)\n}\n\n/**\n * Converts normalized business date into display format YYYY-MM-DD.\n * 正規化済みの営業日を YYYY-MM-DD 形式の表示文字列へ変換する。\n */\nexport const formatBusinessDateDisplay = (value: BusinessDateInput): string | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n return normalized\n}\n\n/**\n * Parses normalized business date into Date object.\n * 正規化した営業日を Date オブジェクトへ変換する。\n */\nexport const parseBusinessDateKeyToDate = (value: BusinessDateInput): Date | null => {\n const normalized = normalizeBusinessDateKey(value)\n if (!normalized) {\n return null\n }\n\n const [year, month, day] = normalized.split(\"-\").map((part) => Number.parseInt(part, 10))\n if ([year, month, day].some((component) => Number.isNaN(component))) {\n return null\n }\n\n return new Date(year, month - 1, day)\n}\n","/**\n * SSE connection lifecycle hook for daily-report real-time updates.\n * 日報リアルタイム更新の SSE 接続ライフサイクルフック。\n */\nimport { useEffect } from \"react\"\nimport { createLogger, LogLevel } from \"../../shared/logger\"\nimport { type DailyReportSseMessage, dailyReportSseMessageSchema } from \"../../shared/sse-schema\"\nimport { useDailyReportConfig } from \"../config-context\"\n\nconst logger = createLogger(LogLevel.INFO, \"daily-report-sse-connection\")\n\ntype UseDailyReportSseConnectionOptions = {\n isSseEnabled: boolean\n onMessage: (payload: Exclude<DailyReportSseMessage, { type: \"connected\" }>) => void\n}\n\n/**\n * Manages Daily Report SSE connection lifecycle with reconnect handling.\n * Handles open, message parse/dispatch, error logging, retry, and cleanup.\n *\n * 日報 SSE 接続のライフサイクルを管理する Hook。\n * 接続開始、メッセージ解析と配送、エラー時再接続、クリーンアップを管理します。\n */\nexport const useDailyReportSseConnection = ({ isSseEnabled, onMessage }: UseDailyReportSseConnectionOptions) => {\n const { ssePath } = useDailyReportConfig()\n\n /**\n * Maintains SSE connection while enabled and retries on error.\n * isSseEnabled が true の間は接続を維持し、エラー時は再接続します。\n */\n useEffect(() => {\n if (!isSseEnabled) return\n\n let eventSource: EventSource | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let reconnectAttempt = 0\n let lastReceivedEventId: string | undefined\n\n const connect = () => {\n eventSource?.close()\n eventSource = new EventSource(`${ssePath}${lastReceivedEventId ? `?lastEventId=${lastReceivedEventId}` : \"\"}`)\n\n eventSource.onopen = () => {\n reconnectAttempt = 0\n logger.info(\"SSE Connected\")\n }\n\n eventSource.onmessage = (event) => {\n if (event.lastEventId) {\n lastReceivedEventId = event.lastEventId\n }\n try {\n const raw = JSON.parse(event.data)\n const result = dailyReportSseMessageSchema.safeParse(raw)\n if (!result.success) {\n logger.error(\"SSE Validation Error\", result.error.format())\n return\n }\n if (result.data.type === \"connected\") {\n logger.info(\"SSE Subscribe Ready\")\n return\n }\n onMessage(result.data)\n } catch (e) {\n logger.error(\"SSE Parse Error\", e)\n }\n }\n\n eventSource.onerror = (e) => {\n eventSource?.close()\n eventSource = null\n reconnectAttempt++\n const delayMs = Math.min(2000 * 2 ** (reconnectAttempt - 1), 30000)\n logger.error(`SSE Error. Retrying in ${delayMs}ms (attempt ${reconnectAttempt})...`, e)\n reconnectTimer = setTimeout(connect, delayMs)\n }\n }\n\n connect()\n\n return () => {\n if (eventSource) eventSource.close()\n if (reconnectTimer) clearTimeout(reconnectTimer)\n }\n }, [isSseEnabled, onMessage, ssePath])\n}\n","/**\n * Zod schemas for daily-report SSE messages (client/server shared contract).\n * 日報 SSE メッセージの zod スキーマ (client / server 共有契約)。\n */\nimport { z } from \"zod\"\nimport type { DailyReportCommentItem as DeclaredComment, DailyReportDetail as DeclaredDetail } from \"./types\"\n\n// --- サブスキーマ(types.ts の型と構造的に一致させる) ---\n\nexport const dailyReportInterviewerSchema = z.object({\n name: z.string(),\n affiliation: z.string().nullable(),\n})\n\nexport const dailyReportCommentSchema = z.object({\n name: z.string().nullable(),\n text: z.string().nullable(),\n color: z.string().nullable(),\n})\n\nexport const dailyReportLabelDefSchema = z.object({\n id: z.number(),\n name: z.string(),\n color: z.string().nullable(),\n})\n\nexport const dailyReportCommentItemSchema = z.object({\n id: z.number(),\n userId: z.string(),\n userName: z.string(),\n content: z.string(),\n createdAt: z.string(),\n isMine: z.boolean(),\n})\n\nexport const dailyReportDetailSchema = z.object({\n reportHubId: z.number(),\n date: z.string().nullable(),\n createdAt: z.string().nullable(),\n author: z.string(),\n userId: z.string(),\n employeeName: z.string().nullable(),\n updatedBy: z.string().nullable(),\n updatedAt: z.string().nullable(),\n category: z.string().nullable(),\n creationCategory: z.string().nullable(),\n visitTimeFrom: z.string().nullable(),\n visitTimeTo: z.string().nullable(),\n customerName: z.string().nullable(),\n interviewers: z.array(dailyReportInterviewerSchema),\n subject: z.string().nullable(),\n content: z.string().nullable(),\n comments: z.array(dailyReportCommentSchema),\n isRead: z.boolean(),\n isStarred: z.boolean(),\n labels: z.array(dailyReportLabelDefSchema),\n commentItems: z.array(dailyReportCommentItemSchema),\n})\n\n// --- SSE メッセージスキーマ(8種) ---\n\nexport const connectedMessageSchema = z.object({\n type: z.literal(\"connected\"),\n})\n\nexport const statusUpdateMessageSchema = z.object({\n type: z.literal(\"status-update\"),\n reportHubId: z.number(),\n statusType: z.enum([\"star\", \"read\"]),\n value: z.boolean(),\n clientTempId: z.string(),\n // recipientRawUserId はサーバーサイドフィルタリング専用。\n // SSE ルートが raw JSON から取得してフィルタ後に除去する。\n // クライアントには到達しないが、サーバー側の publish で .parse() を通すため定義が必要。\n recipientRawUserId: z.number().optional(),\n})\n\nexport const commentAddMessageSchema = z.object({\n type: z.literal(\"comment-add\"),\n reportHubId: z.number(),\n comment: dailyReportCommentItemSchema,\n clientTempId: z.string(),\n})\n\nexport const commentDeleteMessageSchema = z.object({\n type: z.literal(\"comment-delete\"),\n reportHubId: z.number(),\n commentId: z.number(),\n clientTempId: z.string(),\n})\n\nexport const reportCreateMessageSchema = z.object({\n type: z.literal(\"report-create\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportUpdateMessageSchema = z.object({\n type: z.literal(\"report-update\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n recipientRawUserId: z.number().optional(),\n})\n\nexport const reportPublishMessageSchema = z.object({\n type: z.literal(\"report-publish\"),\n reportHubId: z.number(),\n report: dailyReportDetailSchema,\n clientTempId: z.string(),\n})\n\nexport const reportDeleteMessageSchema = z.object({\n type: z.literal(\"report-delete\"),\n reportHubId: z.number(),\n clientTempId: z.string(),\n})\n\n// --- Discriminated Union ---\n\nexport const dailyReportSseMessageSchema = z.discriminatedUnion(\"type\", [\n connectedMessageSchema,\n statusUpdateMessageSchema,\n commentAddMessageSchema,\n commentDeleteMessageSchema,\n reportCreateMessageSchema,\n reportUpdateMessageSchema,\n reportPublishMessageSchema,\n reportDeleteMessageSchema,\n])\n\n// --- 型エクスポート ---\n\nexport type DailyReportSseMessage = z.infer<typeof dailyReportSseMessageSchema>\n\n// --- コンパイル時の型互換チェック ---\n// Zod推論型が既存の型定義に代入可能であることを保証する。\n// ここでエラーが出たらスキーマと types.ts がずれている。\n\ntype _AssertDetailCompat = z.infer<typeof dailyReportDetailSchema> extends DeclaredDetail ? true : never\ntype _AssertCommentCompat = z.infer<typeof dailyReportCommentItemSchema> extends DeclaredComment ? true : never\nconst _detailCheck: _AssertDetailCompat = true\nconst _commentCheck: _AssertCommentCompat = true\nvoid _detailCheck\nvoid _commentCheck\n","/**\n * Comment management hook merging server data with optimistic local updates.\n * サーバーデータとローカルの楽観的更新を統合して日報コメントを管理するフック。\n */\nimport { useCallback, useMemo, useRef, useState } from \"react\"\nimport { mergeComments, type UIComment } from \"../../shared/comment-adapter\"\nimport type { DailyReportDetail } from \"../../shared/types\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\n\ntype User = { displayName?: string; name?: { familyName?: string; givenName?: string }; emails?: { value: string }[] }\n\n/**\n * Manages daily report comments, integrating server data with optimistic local updates.\n * サーバーデータとローカルの楽観的更新を統合して日報コメントを管理するフック。\n */\nexport const useDailyReportComments = (report: DailyReportDetail | null, user: User | null | undefined, userId?: string | null) => {\n const { pendingAddCommentIds, pendingDeleteCommentIds, resolvedIdMap, version, addComment, deleteComment } = useDailyReportActionContext()\n const [pendingDeleteId, setPendingDeleteId] = useState<number | string | null>(null)\n\n const handleDeleteComment = useCallback((cId: number) => report && deleteComment(report.reportHubId, cId, report.date ?? \"\"), [report, deleteComment])\n const handleAddComment = useCallback((content: string) => (report && user ? addComment(report.reportHubId, content, report.date ?? \"\") : Promise.resolve()), [report, user, addComment])\n\n const prevUiCommentsRef = useRef<UIComment[]>([])\n\n // UI用コメントリストの生成: サーバーデータ、レガシーコメント、楽観的更新(追加・削除)をマージして表示用データを構築する。\n // 依存関係: [report, user, userId, pendingDeleteCommentIds, pendingAddCommentIds, resolvedIdMap, version]\n // クリーンアップ: なし\n // biome-ignore lint/correctness/useExhaustiveDependencies: version は ref (pending*) 変更時の再計算トリガーとして意図的に依存させる\n const uiComments = useMemo(() => {\n if (!report) return []\n\n // 1. Base: Merge legacy and new comments\n // 基本データ: レガシーコメントと新しい形式のコメントをマージ。\n let comments = mergeComments(report.comments, report.commentItems, userId)\n\n // 2. Filter: Remove pending deletions\n // フィルタリング: 削除保留中のコメントを除外。\n const deletedIds = pendingDeleteCommentIds.current.get(report.reportHubId)\n if (deletedIds?.size) comments = comments.filter((c) => !deletedIds.has(c.id as number))\n\n // 3. Add: Append pending optimistic additions\n // 追加: 楽観的に追加されたコメントを末尾に追加。\n const addedComments = pendingAddCommentIds.current.get(report.reportHubId)\n if (addedComments?.size && user) {\n const existingIds = new Set(comments.map((c) => c.id))\n const authorName = user.displayName?.trim() || (user.name ? `${user.name.familyName ?? \"\"} ${user.name.givenName ?? \"\"}`.trim() : \"\") || user.emails?.[0]?.value || \"Unknown\"\n\n for (const [tempId, content] of addedComments) {\n if (deletedIds?.has(tempId)) continue\n if (existingIds.has(tempId)) continue\n\n // Skip if real ID resolved and already present\n // 実IDが解決済みで既にリストに存在する場合はスキップ。\n const realId = resolvedIdMap.current.get(tempId)\n if (realId && existingIds.has(realId)) continue\n\n // Skip if content duplicated (server data arrived before ID resolution)\n // 内容が重複している場合(ID解決前にサーバーデータが到達した場合)はスキップ。\n if (comments.some((c) => c.isMine && c.content === content && typeof c.id === \"number\" && c.id > 0)) continue\n\n comments.push({ id: tempId, authorName, content, createdAt: new Date().toISOString(), isMine: true, isLegacy: false })\n }\n }\n\n // Stable reference check\n // 参照の安定性チェック: 内容に変更がなければ前回の参照を返すことで不要な再レンダリングを防ぐ。\n if (JSON.stringify(comments) === JSON.stringify(prevUiCommentsRef.current)) return prevUiCommentsRef.current\n prevUiCommentsRef.current = comments\n return comments\n }, [report, user, userId, pendingDeleteCommentIds, pendingAddCommentIds, resolvedIdMap, version])\n\n return { uiComments, handleAddComment, handleDeleteComment, pendingDeleteId, setPendingDeleteId, resolvedIdMap: resolvedIdMap.current }\n}\n","/**\n * Shared hook for calculating viewport height of virtual scroll containers.\n * 仮想スクロールコンテナのビューポート高さを計測する共通フック。\n */\nimport { type RefObject, useCallback, useEffect, useState } from \"react\"\n\ntype UseDynamicViewportHeightOptions = {\n enableMutationObserver?: boolean\n bottomGap?: number\n minFallbackHeight?: number\n}\n\ntype UseDynamicViewportHeightResult = {\n viewportHeight: number\n updateViewportHeight: () => void\n}\n\n/**\n * Computes and updates the viewport height for scroll containers while reacting to layout changes.\n * レイアウト変化に追従しながらスクロールコンテナのビューポート高さを算出して更新する関数。\n */\nexport const useDynamicViewportHeight = (containerRef: RefObject<HTMLDivElement | null>, options?: UseDynamicViewportHeightOptions): UseDynamicViewportHeightResult => {\n const { enableMutationObserver = false, bottomGap = 4, minFallbackHeight = 240 } = options ?? {}\n const [viewportHeight, setViewportHeight] = useState(600)\n\n const updateViewportHeight = useCallback(() => {\n if (typeof window === \"undefined\") {\n return\n }\n\n const target = containerRef.current\n if (!target) {\n return\n }\n\n // 要素が非表示の場合は計測しない (display: none など)\n if (target.offsetParent === null && window.getComputedStyle(target).position !== \"fixed\") {\n return\n }\n\n const toNumber = (value: string) => Number.parseFloat(value) || 0\n const { top: containerTop } = target.getBoundingClientRect()\n const parentRect = target.parentElement?.getBoundingClientRect()\n const rootStyle = window.getComputedStyle(document.documentElement)\n const footerHeight = toNumber(rootStyle.getPropertyValue(\"--footer-height\"))\n const headerHeight = toNumber(rootStyle.getPropertyValue(\"--header-height\"))\n const scrollbarHeight = toNumber(rootStyle.getPropertyValue(\"--scrollbar-height\"))\n const containerStyle = window.getComputedStyle(target)\n const marginBottom = toNumber(containerStyle.marginBottom)\n const paddingBottom = toNumber(containerStyle.paddingBottom)\n const viewportBottom = window.innerHeight - footerHeight - scrollbarHeight - bottomGap\n const effectiveBottom = parentRect ? Math.min(parentRect.bottom, viewportBottom) : viewportBottom\n const layoutTop = parentRect ? Math.max(parentRect.top, headerHeight) : headerHeight\n const effectiveTop = Math.max(containerTop, layoutTop)\n const availableHeight = effectiveBottom - effectiveTop - marginBottom - paddingBottom\n const safeAvailableHeight = Number.isFinite(availableHeight) ? Math.floor(availableHeight) : 0\n const clampedHeight = safeAvailableHeight > 0 ? safeAvailableHeight : minFallbackHeight\n\n if (clampedHeight > 0 && Number.isFinite(clampedHeight)) {\n setViewportHeight((previous) => (Math.abs(previous - clampedHeight) > 1 ? clampedHeight : previous))\n }\n }, [bottomGap, containerRef, minFallbackHeight])\n\n useEffect(() => {\n if (typeof window === \"undefined\") {\n return\n }\n\n let frameId: number | null = null\n let timeoutId: number | null = null\n let resizeObserver: ResizeObserver | null = null\n let mutationObserver: MutationObserver | null = null\n const scheduleUpdate = () => {\n if (frameId !== null) {\n window.cancelAnimationFrame(frameId)\n }\n frameId = window.requestAnimationFrame(() => {\n updateViewportHeight()\n })\n }\n\n // 初期表示直後とわずかな遅延後に計測して安定させる\n scheduleUpdate()\n timeoutId = window.setTimeout(() => {\n scheduleUpdate()\n }, 150)\n\n const containerElement = containerRef.current\n if (typeof ResizeObserver !== \"undefined\" && containerElement) {\n resizeObserver = new ResizeObserver(() => {\n // コンテナと親要素のリサイズ変化に追従する\n scheduleUpdate()\n })\n resizeObserver.observe(containerElement)\n if (containerElement.parentElement) {\n resizeObserver.observe(containerElement.parentElement)\n }\n }\n\n if (enableMutationObserver && typeof MutationObserver !== \"undefined\") {\n mutationObserver = new MutationObserver(() => {\n // ルートのスタイル変更にも追従する\n scheduleUpdate()\n })\n mutationObserver.observe(document.documentElement, { attributes: true, attributeFilter: [\"style\"] })\n }\n\n window.addEventListener(\"resize\", scheduleUpdate)\n\n return () => {\n if (frameId !== null) {\n window.cancelAnimationFrame(frameId)\n }\n if (timeoutId !== null) {\n window.clearTimeout(timeoutId)\n }\n window.removeEventListener(\"resize\", scheduleUpdate)\n resizeObserver?.disconnect()\n mutationObserver?.disconnect()\n }\n }, [enableMutationObserver, updateViewportHeight, containerRef])\n\n return {\n viewportHeight,\n updateViewportHeight,\n }\n}\n","import { type ScrollBarTapCircleOptions, type ScrollPaneInertiaOptions, tapScrollCircleSampleVisual } from \"@aiquants/virtualscroll\"\nimport type { DailyReportItem } from \"../../shared/types\"\nimport type { DailyReportDetailResource } from \"../hooks/use-daily-report\"\n\n/**\n * Shared constants for Daily Report data fetching parameters.\n * 日報データ取得で利用する共通パラメーター。\n */\nexport const DAILY_REPORT_FETCH_DEBOUNCE_MS = 180\n\nexport const DEFAULT_ITEM_HEIGHT = 160\nexport const ITEM_VERTICAL_PADDING = 6\nexport const ITEM_CONTAINER_HEIGHT = DEFAULT_ITEM_HEIGHT - ITEM_VERTICAL_PADDING\nexport const FAST_SCROLL_WHEEL_MULTIPLIER = 0.8\nexport const FAST_SCROLL_INERTIA_OPTIONS: ScrollPaneInertiaOptions = { maxVelocity: 18, deceleration: 0.0018, startVelocityThreshold: 0.03 }\nexport const PREFETCH_LOOKAHEAD = 20\nexport const PREFETCH_MAX_DATES = 6\nexport const EMPTY_ITEM: DailyReportItem = { reportHubId: -1, businessDate: null }\nexport const EMPTY_DETAIL: DailyReportDetailResource = { report: null, error: null, isLoading: false, isRefetching: false }\nexport const WHEEL_RESET_TIMEOUT_MS = 240\nexport const VIRTUAL_SCROLL_OVERSCAN_COUNT = 15\nexport const SCROLL_BAR_WIDTH = 8\nexport const MAX_PREVIEW_LINES = 3\nexport const TAP_SCROLL_CIRCLE_OPTIONS: ScrollBarTapCircleOptions = {\n enabled: true,\n renderVisual: tapScrollCircleSampleVisual,\n maxSpeedCurve: { exponentialSteepness: 7.5, exponentialScale: 14, easedOffset: 0.18 },\n maxSpeedMultiplier: 18,\n}\n","import { useEffect, useRef, useState } from \"react\"\nimport type { DailyReportDetail } from \"../../shared/types\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { Button } from \"../ui/button\"\nimport { Input } from \"../ui/input\"\nimport { Label } from \"../ui/label\"\nimport { Textarea } from \"../ui/textarea\"\n\ntype DailyReportEditFormProps = {\n report: DailyReportDetail\n onCancel?: () => void\n onPublishSuccess?: () => void\n}\n\nexport const DailyReportEditForm = ({ report, onCancel, onPublishSuccess }: DailyReportEditFormProps) => {\n const { updateReport, publishReport } = useDailyReportActionContext()\n const [title, setTitle] = useState(report.subject ?? \"\")\n const [content, setContent] = useState(report.content ?? \"\")\n const [isSaving, setIsSaving] = useState(false)\n\n // 同一レポート編集中に SSE/キャッシュ更新でフォームがリセットされるのを防止するための ID 追跡\n const syncedReportIdRef = useRef(report.reportHubId)\n\n // レポート切り替え時のみフォーム状態を同期する (同一レポートのキャッシュ更新ではリセットしない)\n useEffect(() => {\n if (report.reportHubId !== syncedReportIdRef.current) {\n setTitle(report.subject ?? \"\")\n setContent(report.content ?? \"\")\n syncedReportIdRef.current = report.reportHubId\n }\n }, [report.reportHubId, report.subject, report.content])\n\n const handleSave = async () => {\n setIsSaving(true)\n try {\n await updateReport(report.reportHubId, { title, content }, report.date ?? \"\")\n } finally {\n setIsSaving(false)\n }\n }\n\n const handlePublish = async () => {\n setIsSaving(true)\n try {\n // Ensure latest changes are saved before publishing\n await updateReport(report.reportHubId, { title, content }, report.date ?? \"\")\n await publishReport(report.reportHubId, report.date ?? \"\")\n onPublishSuccess?.()\n } finally {\n setIsSaving(false)\n }\n }\n\n return (\n <div className=\"space-y-6 p-1\">\n <div className=\"space-y-2\">\n <Label htmlFor={`report-title-${report.reportHubId}`}>タイトル</Label>\n <Input id={`report-title-${report.reportHubId}`} value={title} onChange={(e) => setTitle(e.target.value)} placeholder=\"日報のタイトルを入力\" disabled={isSaving} />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor={`report-content-${report.reportHubId}`}>内容</Label>\n <Textarea id={`report-content-${report.reportHubId}`} value={content} onChange={(e) => setContent(e.target.value)} placeholder=\"日報の内容を入力\" className=\"min-h-50\" disabled={isSaving} />\n </div>\n\n <div className=\"flex justify-end gap-2\">\n {onCancel && (\n <Button variant=\"outline\" onClick={onCancel} disabled={isSaving}>\n キャンセル\n </Button>\n )}\n <Button variant=\"secondary\" onClick={handleSave} disabled={isSaving}>\n 保存\n </Button>\n <Button onClick={handlePublish} disabled={isSaving}>\n 公開\n </Button>\n </div>\n </div>\n )\n}\n","import { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport * as React from \"react\"\nimport { cn } from \"./cn\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n)\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />\n})\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n","import type * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n )\n}\n\nexport { Input }\n","import * as LabelPrimitive from \"@radix-ui/react-label\"\nimport type * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nfunction Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\"flex select-none items-center gap-2 font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50\", className)}\n {...props}\n />\n )\n}\n\nexport { Label }\n","import type * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex min-h-15 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n )\n}\n\nexport { Textarea }\n","import { cn } from \"../ui/cn\"\n\ntype UnreadIndicatorProps = {\n className?: string\n}\n\n/**\n * A simple blue dot indicator used to represent unread status.\n * 未読状態を表すためのシンプルな青いドットインジケーター。\n */\nexport const UnreadIndicator = ({ className }: UnreadIndicatorProps) => {\n return <span className={cn(\"text-blue-500\", className)}>●</span>\n}\n","/**\n * Virtual-scrolling summary list for daily reports driven by fetcher-based detail hooks.\n * useFetcher ベースの詳細取得フックで日報サマリーを仮想スクロール表示。\n */\nimport { SwipeCloseGuardContext, useSwipeGuardState, useSwipeToDismissOverlay, useTapClick, useTransitionDisplay } from \"@aiquants/swipe-overlay\"\nimport { type ScrollBarThumbOverlayRenderProps, VirtualScroll, type VirtualScrollHandle } from \"@aiquants/virtualscroll\"\nimport { Check, Star, Trash2 } from \"lucide-react\"\nimport { type CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { fallbackText, formatToIsoDate } from \"../../shared/text-utils\"\nimport type { DailyReportDetail, DailyReportItem } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { useDailyReportDetail } from \"../hooks/use-daily-report\"\nimport { useDailyReportComments } from \"../hooks/use-daily-report-comments\"\nimport { useDynamicViewportHeight } from \"../hooks/use-dynamic-viewport-height\"\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\"\nimport {\n DEFAULT_ITEM_HEIGHT,\n EMPTY_DETAIL,\n EMPTY_ITEM,\n FAST_SCROLL_INERTIA_OPTIONS,\n FAST_SCROLL_WHEEL_MULTIPLIER,\n ITEM_CONTAINER_HEIGHT,\n ITEM_VERTICAL_PADDING,\n MAX_PREVIEW_LINES,\n SCROLL_BAR_WIDTH,\n TAP_SCROLL_CIRCLE_OPTIONS,\n VIRTUAL_SCROLL_OVERSCAN_COUNT,\n WHEEL_RESET_TIMEOUT_MS,\n} from \"../utils/constants\"\nimport { BusinessDayThumbOverlay } from \"./business-day-thumb-overlay\"\nimport { DailyReportCommentForm, DailyReportCommentList } from \"./daily-report-comment-section\"\nimport { DailyReportEditForm } from \"./daily-report-edit-form\"\nimport { UnreadIndicator } from \"./unread-indicator\"\n\ntype TestableVirtualScrollHandle = VirtualScrollHandle & {\n findReportIndex: (id: number) => number\n getReportIds: (limit?: number) => number[]\n}\n\ntype DailyReportListProps = {\n dailyReportItems: DailyReportItem[]\n initialSelectedBusinessDate?: string | null\n initialSelectedReportHubId?: number | null\n autoMarkRead?: boolean\n selectedReportHubId: number | null\n onSelectItem: (reportHubId: number | null) => void\n userId?: string | null\n}\n\n/**\n * Manages mobile overlay state, history navigation, and scroll locking.\n * モバイルオーバーレイの状態、履歴ナビゲーション、スクロールロックを管理するフック。\n */\nconst useMobileOverlayManager = (isMobileLayout: boolean, selectedReportHubId: number | null, dailyReportItems: DailyReportItem[], onSelectItem: (reportHubId: number | null) => void) => {\n const [isMobileDetailOpen, setIsMobileDetailOpen] = useState(false)\n const [isOverlayMounted, setIsOverlayMounted] = useState(false)\n const overlayHistoryKeyRef = useRef<string | null>(null)\n const lastClosedItemIdRef = useRef<number | null>(null)\n const bodyOverflowRef = useRef<string | null>(null)\n\n const selectedItem = useMemo(() => (selectedReportHubId === null ? null : (dailyReportItems.find((e) => e.reportHubId === selectedReportHubId) ?? null)), [dailyReportItems, selectedReportHubId])\n const shouldShowOverlay = isMobileLayout && isMobileDetailOpen && selectedItem !== null\n\n /**\n * Manages the mounting state of the mobile overlay for animation purposes.\n * アニメーション用にモバイルオーバーレイのマウント状態を管理する副作用。\n *\n * Purpose: To delay unmounting until the close animation completes.\n * Dependencies: [shouldShowOverlay]\n * Cleanup: Clears the timeout.\n */\n useEffect(() => {\n if (shouldShowOverlay) {\n setIsOverlayMounted(true)\n } else {\n const timer = setTimeout(() => setIsOverlayMounted(false), 300)\n return () => clearTimeout(timer)\n }\n }, [shouldShowOverlay])\n\n /**\n * Manages mobile history state and body scroll locking.\n * モバイル履歴状態と Body スクロールロックを管理する副作用。\n *\n * Purpose: To support browser back button for closing overlay and prevent background scrolling.\n * Dependencies: [isMobileLayout, isMobileDetailOpen, selectedReportHubId]\n * Cleanup: Restores body overflow style.\n */\n useEffect(() => {\n if (typeof window === \"undefined\") return\n\n // History Management\n if (isMobileLayout && isMobileDetailOpen && selectedReportHubId !== null && !overlayHistoryKeyRef.current) {\n const key = `overlay-${Date.now()}`\n window.history.pushState({ ...window.history.state, dailyReportOverlay: key }, \"\", window.location.href)\n overlayHistoryKeyRef.current = key\n } else if (!(isMobileLayout && isMobileDetailOpen) && overlayHistoryKeyRef.current) {\n overlayHistoryKeyRef.current = null\n }\n\n // Scroll Lock\n if (isMobileDetailOpen) {\n bodyOverflowRef.current = document.body.style.overflow\n document.body.style.overflow = \"hidden\"\n } else if (bodyOverflowRef.current !== null) {\n document.body.style.overflow = bodyOverflowRef.current\n bodyOverflowRef.current = null\n }\n\n return () => {\n if (bodyOverflowRef.current !== null) document.body.style.overflow = bodyOverflowRef.current\n }\n }, [isMobileLayout, isMobileDetailOpen, selectedReportHubId])\n\n /**\n * Handles browser back navigation (popstate).\n * ブラウザの戻る操作 (popstate) を処理する副作用。\n *\n * Purpose: To close the mobile overlay when the user presses the back button.\n * Dependencies: [selectedReportHubId]\n * Cleanup: Removes popstate listener.\n */\n useEffect(() => {\n if (typeof window === \"undefined\") return\n const onPopState = () => {\n if (overlayHistoryKeyRef.current) {\n lastClosedItemIdRef.current = selectedReportHubId\n overlayHistoryKeyRef.current = null\n setIsMobileDetailOpen(false)\n onSelectItem(null)\n }\n }\n window.addEventListener(\"popstate\", onPopState)\n return () => window.removeEventListener(\"popstate\", onPopState)\n }, [selectedReportHubId, onSelectItem])\n\n const handleOverlayClose = useCallback(() => {\n if (!isMobileLayout) return\n lastClosedItemIdRef.current = selectedReportHubId\n setIsMobileDetailOpen(false)\n onSelectItem(null)\n if (overlayHistoryKeyRef.current && typeof window !== \"undefined\") {\n overlayHistoryKeyRef.current = null\n window.history.back()\n }\n }, [isMobileLayout, selectedReportHubId, onSelectItem])\n\n return {\n isMobileDetailOpen,\n setIsMobileDetailOpen,\n isOverlayMounted,\n shouldShowOverlay,\n handleOverlayClose,\n lastClosedItemIdRef,\n selectedItem,\n }\n}\n\n/**\n * Manages auto-read logic and read status toggling.\n * 自動既読ロジックと既読状態の切り替えを管理するフック。\n */\nconst useAutoRead = (report: DailyReportDetail | null, isLoading: boolean, autoMarkRead: boolean, isActive: boolean, toggleRead: (reportHubId: number, isRead: boolean, date: string) => Promise<void>) => {\n const ignoreAutoReadForReportHubId = useRef<number | null>(null)\n const lastReportHubIdRef = useRef<number | undefined>(undefined)\n\n if (lastReportHubIdRef.current !== report?.reportHubId) {\n lastReportHubIdRef.current = report?.reportHubId\n ignoreAutoReadForReportHubId.current = null\n }\n\n /**\n * Resets suppression flag when inactive.\n * 非アクティブ時に抑制フラグをリセットする副作用。\n *\n * Purpose: To re-enable auto-read when the report is reopened.\n * Dependencies: [isActive]\n * Cleanup: None.\n */\n useEffect(() => {\n if (!isActive) ignoreAutoReadForReportHubId.current = null\n }, [isActive])\n\n /**\n * Handles the read status toggle logic.\n * 既読状態の切り替え処理を行うコールバック。\n *\n * Purpose: To toggle read status and manage auto-read suppression.\n * Dependencies: [report, toggleRead]\n */\n const handleToggleRead = useCallback(\n async (isAuto: boolean = false) => {\n if (!report) return\n if (!isAuto) ignoreAutoReadForReportHubId.current = report.reportHubId\n\n const nextValue = !report.isRead\n try {\n await toggleRead(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n },\n [report, toggleRead],\n )\n\n /**\n * Automatically marks the report as read after a delay.\n * 一定時間後にレポートを自動的に既読にする副作用。\n *\n * Purpose: To mark reports as read when viewed for a sufficient duration.\n * Dependencies: [report, isLoading, autoMarkRead, handleToggleRead]\n * Cleanup: Clears the timeout.\n */\n useEffect(() => {\n if (autoMarkRead && report && !report.isRead && !isLoading) {\n if (ignoreAutoReadForReportHubId.current === report.reportHubId) return\n const timer = setTimeout(() => handleToggleRead(true), 500)\n return () => clearTimeout(timer)\n }\n }, [report, isLoading, autoMarkRead, handleToggleRead])\n\n return { handleToggleRead }\n}\n\n/**\n * Daily report list cards with key metadata and preview content via virtual scrolling.\n * 主なメタデータと内容プレビューを仮想スクロール表示する日報リスト。\n */\nexport const DailyReportList = ({ dailyReportItems, initialSelectedBusinessDate = null, initialSelectedReportHubId = null, autoMarkRead = false, selectedReportHubId, onSelectItem, userId }: DailyReportListProps) => {\n const scrollContainerRef = useRef<HTMLDivElement | null>(null)\n const virtualScrollRef = useRef<VirtualScrollHandle>(null)\n const { viewportHeight, updateViewportHeight } = useDynamicViewportHeight(scrollContainerRef)\n const [overlayLabel, setOverlayLabel] = useState<string>(\"\")\n const [isWheelScrollActive, setIsWheelScrollActive] = useState(false)\n const [isMobileLayout, setIsMobileLayout] = useState(false)\n const wheelResetTimerRef = useRef<number | null>(null)\n const appliedInitialSignatureRef = useRef<string | null>(null)\n const itemCount = dailyReportItems.length\n const scrollEndTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const { isMobileDetailOpen, setIsMobileDetailOpen, isOverlayMounted, shouldShowOverlay, handleOverlayClose, lastClosedItemIdRef, selectedItem } = useMobileOverlayManager(isMobileLayout, selectedReportHubId, dailyReportItems, onSelectItem)\n\n const columnHeightStyle = useMemo<CSSProperties | undefined>(() => (Number.isFinite(viewportHeight) && viewportHeight > 0 ? { height: viewportHeight } : undefined), [viewportHeight])\n\n /**\n * Manages global event listeners (Media Query, Wheel, Resize).\n * メディアクエリ、ホイール、リサイズなどのグローバルイベントリスナーを管理する副作用。\n *\n * Purpose: To handle responsive layout changes and scroll interactions.\n * Dependencies: [updateViewportHeight]\n * Cleanup: Removes all event listeners and clears timers.\n */\n useEffect(() => {\n if (typeof window === \"undefined\") return\n\n const mq = window.matchMedia(\"(max-width: 639px)\")\n const onMediaChange = (e: MediaQueryListEvent) => setIsMobileLayout(e.matches)\n setIsMobileLayout(mq.matches)\n mq.addEventListener(\"change\", onMediaChange)\n\n const onResize = () => requestAnimationFrame(updateViewportHeight)\n window.addEventListener(\"resize\", onResize)\n updateViewportHeight()\n\n const el = scrollContainerRef.current\n const onWheel = () => {\n setIsWheelScrollActive(true)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n wheelResetTimerRef.current = window.setTimeout(() => {\n setIsWheelScrollActive(false)\n wheelResetTimerRef.current = null\n }, WHEEL_RESET_TIMEOUT_MS)\n }\n el?.addEventListener(\"wheel\", onWheel, { passive: true })\n\n return () => {\n mq.removeEventListener(\"change\", onMediaChange)\n window.removeEventListener(\"resize\", onResize)\n el?.removeEventListener(\"wheel\", onWheel)\n if (wheelResetTimerRef.current) clearTimeout(wheelResetTimerRef.current)\n }\n }, [updateViewportHeight])\n\n /**\n * Synchronizes initial selection from props when data changes.\n * データ変更時にプロパティからの初期選択状態を同期する副作用。\n *\n * Purpose: To respect deep-linked selection (ID or Date) when items are loaded.\n * Dependencies: [dailyReportItems, initialSelectedBusinessDate, initialSelectedReportHubId, selectedReportHubId, itemCount, onSelectItem]\n * Cleanup: None.\n */\n useEffect(() => {\n if (!itemCount) {\n appliedInitialSignatureRef.current = null\n if (selectedReportHubId !== null) onSelectItem(null)\n return\n }\n\n const signature = `${initialSelectedReportHubId ?? \"\"}|${initialSelectedBusinessDate ?? \"\"}`\n const hasInitial = (initialSelectedReportHubId ?? null) !== null || (initialSelectedBusinessDate ?? null) !== null\n\n if (!hasInitial) appliedInitialSignatureRef.current = null\n\n if (hasInitial && appliedInitialSignatureRef.current !== signature) {\n const match = (initialSelectedReportHubId && dailyReportItems.find((i) => i.reportHubId === initialSelectedReportHubId)) || (initialSelectedBusinessDate && dailyReportItems.find((i) => i.businessDate === initialSelectedBusinessDate))\n if (match) {\n appliedInitialSignatureRef.current = signature\n if (selectedReportHubId !== match.reportHubId) onSelectItem(match.reportHubId)\n return\n }\n }\n }, [dailyReportItems, initialSelectedBusinessDate, initialSelectedReportHubId, selectedReportHubId, itemCount, onSelectItem])\n\n /**\n * Restores focus after closing overlay (Desktop only).\n * オーバーレイを閉じた後のフォーカス復帰 (デスクトップのみ) を行う副作用。\n *\n * Purpose: To maintain keyboard focus context for accessibility.\n * Dependencies: [isMobileDetailOpen, isMobileLayout, dailyReportItems]\n * Cleanup: None.\n */\n useEffect(() => {\n if (isMobileLayout || isMobileDetailOpen || lastClosedItemIdRef.current === null) return\n const targetId = lastClosedItemIdRef.current\n lastClosedItemIdRef.current = null\n\n const index = dailyReportItems.findIndex((item) => item.reportHubId === targetId)\n if (index >= 0) {\n requestAnimationFrame(() => virtualScrollRef.current?.focusItemAtIndex(index))\n }\n }, [isMobileDetailOpen, isMobileLayout, dailyReportItems, lastClosedItemIdRef])\n\n /**\n * Syncs overlay label with the first item's date.\n * オーバーレイラベルを先頭アイテムの日付と同期する副作用。\n *\n * Purpose: To provide a default label for the scroll thumb overlay.\n * Dependencies: [dailyReportItems, itemCount]\n * Cleanup: None.\n */\n useEffect(() => {\n if (itemCount > 0)\n setOverlayLabel((prev) => {\n const next = formatToIsoDate(dailyReportItems[0]?.businessDate)\n return prev === next ? prev : next\n })\n }, [dailyReportItems, itemCount])\n\n const dailyReportItemsRef = useRef(dailyReportItems)\n dailyReportItemsRef.current = dailyReportItems\n\n useEffect(() => {\n if (virtualScrollRef.current) {\n const el = document.querySelector(\".test-daily-report-list\") as (Element & { __virtualScroll?: TestableVirtualScrollHandle }) | null\n if (el) {\n el.__virtualScroll = {\n ...virtualScrollRef.current,\n findReportIndex: (id: number) => dailyReportItemsRef.current.findIndex((item) => item.reportHubId === id),\n getReportIds: (limit = 20) => dailyReportItemsRef.current.slice(0, limit).map((item) => item.reportHubId),\n }\n }\n }\n })\n\n const handleItemSelect = useCallback(\n (id: number) => {\n if (id <= 0) return\n onSelectItem(id)\n if (isMobileLayout) setIsMobileDetailOpen(true)\n },\n [isMobileLayout, onSelectItem, setIsMobileDetailOpen],\n )\n\n const getItem = useCallback((index: number) => (index < itemCount ? dailyReportItems[index] : EMPTY_ITEM), [dailyReportItems, itemCount])\n const getItemHeight = useCallback(() => DEFAULT_ITEM_HEIGHT, [])\n\n const handleRangeChange = useCallback(\n (range: { visibleStartIndex: number }) => {\n const item = getItem(range.visibleStartIndex)\n if (item?.reportHubId > 0)\n setOverlayLabel((prev) => {\n const next = formatToIsoDate(item.businessDate)\n return prev === next ? prev : next\n })\n },\n [getItem],\n )\n\n const handleItemFocus = useCallback(\n (index: number) => {\n const item = getItem(index)\n if (item && item.reportHubId > 0) {\n if (item.reportHubId === selectedReportHubId) return\n onSelectItem(item.reportHubId)\n }\n },\n [getItem, onSelectItem, selectedReportHubId],\n )\n\n const handleScroll = useCallback(() => {\n const container = scrollContainerRef.current\n if (!container) return\n\n if (!container.classList.contains(\"is-scrolling\")) {\n container.classList.add(\"is-scrolling\")\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n container.classList.remove(\"is-scrolling\")\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [])\n\n const renderThumbOverlay = useCallback((props: ScrollBarThumbOverlayRenderProps) => <BusinessDayThumbOverlay {...props} label={overlayLabel} isWheelScrollActive={isWheelScrollActive} />, [overlayLabel, isWheelScrollActive])\n\n return (\n <>\n <div className=\"flex h-full min-h-0 gap-4\">\n <div ref={scrollContainerRef} className=\"flex min-h-0 w-full sm:w-90\" style={columnHeightStyle}>\n <VirtualScroll\n ref={virtualScrollRef}\n itemCount={itemCount}\n getItem={getItem}\n getItemHeight={getItemHeight}\n viewportSize={viewportHeight}\n overscanCount={VIRTUAL_SCROLL_OVERSCAN_COUNT}\n scrollBarOptions={{\n width: SCROLL_BAR_WIDTH,\n tapScrollCircleOptions: TAP_SCROLL_CIRCLE_OPTIONS,\n renderThumbOverlay: renderThumbOverlay,\n }}\n behaviorOptions={{\n inertiaOptions: FAST_SCROLL_INERTIA_OPTIONS,\n wheelSpeedMultiplier: FAST_SCROLL_WHEEL_MULTIPLIER,\n }}\n onRangeChange={handleRangeChange}\n onScroll={handleScroll}\n onItemFocus={handleItemFocus}\n contentInsets={{ top: ITEM_VERTICAL_PADDING, bottom: ITEM_VERTICAL_PADDING }}\n className=\"test-daily-report-list relative w-full\">\n {(item) => <ListReportItem key={item.reportHubId} item={item} isActive={item.reportHubId === selectedReportHubId} onSelect={handleItemSelect} />}\n </VirtualScroll>\n </div>\n <DailyReportSidePane dailyReportItems={dailyReportItems} selectedItem={selectedItem} heightStyle={columnHeightStyle} autoMarkRead={!isMobileLayout && autoMarkRead} isActive={selectedReportHubId !== null} userId={userId} />\n </div>\n {itemCount === 0 && (\n <div className=\"py-8 text-center\">\n <p className=\"text-slate-500\">データがありません</p>\n </div>\n )}\n {isOverlayMounted && <DailyReportMobileOverlay isOpen={shouldShowOverlay} selectedItem={selectedItem} onClose={handleOverlayClose} autoMarkRead={isMobileLayout && autoMarkRead} userId={userId} />}\n </>\n )\n}\n\n/**\n * Loading skeleton representation for daily report list items.\n * 日報リストアイテム用読み込み中スケルトン表示。\n */\nconst ListSkeletonItem = () => (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n <div className=\"flex h-full animate-pulse flex-col justify-between rounded-2xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-700 dark:bg-slate-900/70\">\n <div className=\"mb-3 flex flex-wrap items-center gap-3\">\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-24 rounded bg-slate-200\"></div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-slate-200\"></div>\n <div className=\"h-4 w-11/12 rounded bg-slate-200\"></div>\n <div className=\"h-4 w-5/6 rounded bg-slate-200\"></div>\n </div>\n </div>\n </div>\n)\n\ntype DailyReportSidePaneTab = \"article\" | \"relations\"\n\n/**\n * Side pane that exposes quick tabs for the selected report summary, editing, and correlation views.\n * 選択した日報の概要表示・編集・相関ビューを切り替え表示するサイドペイン。\n */\nconst DailyReportSidePane = ({\n dailyReportItems,\n selectedItem,\n heightStyle,\n autoMarkRead = false,\n isActive = true,\n userId,\n}: {\n dailyReportItems?: DailyReportItem[] // Optional for mobile overlay usage\n selectedItem: DailyReportItem | null\n heightStyle?: CSSProperties\n autoMarkRead?: boolean\n isActive?: boolean\n userId?: string | null\n}) => {\n const [activeTab, setActiveTab] = useState<DailyReportSidePaneTab>(\"article\")\n const [isEditing, setIsEditing] = useState(false)\n const { draftLabelName, fieldLabels } = useDailyReportConfig()\n const { report, error, isLoading } = useDailyReportDetail(selectedItem?.reportHubId ?? -1, selectedItem?.businessDate ?? null)\n\n const scrollContainerRef = useRef<HTMLDivElement>(null)\n const lastReportIdRef = useRef<number | null>(null)\n // 自動編集モード (ドラフト検出による自動遷移) かどうかを追跡\n const autoEditRef = useRef(false)\n\n // レポート選択時のエディタモード自動制御\n // - 新しいレポート選択: ドラフトならエディタモード、公開済みならビューモード\n // - 同一レポートの SSE 更新: ドラフト→公開遷移時に自動編集のみ解除\n useEffect(() => {\n if (!report) return\n\n const isDraft = report.labels.some((l) => l.name === draftLabelName)\n\n if (report.reportHubId !== lastReportIdRef.current) {\n // 選択レポートが変更された\n lastReportIdRef.current = report.reportHubId\n if (isDraft) {\n setIsEditing(true)\n autoEditRef.current = true\n } else {\n setIsEditing(false)\n autoEditRef.current = false\n }\n } else if (!isDraft && isEditing && autoEditRef.current) {\n // SSE で同一レポートが下書き→公開に遷移: 自動編集モードのみ解除\n // ユーザーがダブルクリックで手動編集中の場合は解除しない\n setIsEditing(false)\n autoEditRef.current = false\n }\n }, [report, isEditing, draftLabelName])\n\n const isMine = !!(userId && report?.userId === userId)\n\n const scrollToBottom = useCallback(() => {\n setTimeout(() => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.scrollTo({ top: scrollContainerRef.current.scrollHeight, behavior: \"smooth\" })\n }\n }, 100)\n }, [])\n\n if (dailyReportItems && (dailyReportItems.length === 0 || !selectedItem)) {\n return (\n <aside className=\"hidden min-h-0 min-w-0 flex-1 gap-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:block dark:border-slate-700 dark:bg-slate-900/70\" style={heightStyle}>\n <div className=\"flex flex-1 items-center justify-center rounded-xl border border-slate-300 border-dashed bg-slate-50 p-4 text-center text-slate-500 text-sm dark:border-slate-600 dark:bg-slate-900/40 dark:text-slate-300\">\n <p>左側の一覧から日報を選択してください。</p>\n </div>\n </aside>\n )\n }\n\n const headerDate = formatToIsoDate(report?.date ?? selectedItem?.businessDate)\n const headerCreator = report?.employeeName || report?.author ? fallbackText(report.employeeName ?? report.author) : \"\"\n const showHeaderSkeleton = isLoading || !report\n\n const content = (\n <>\n <div className=\"space-y-1\">\n <div className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.businessDate}</div>\n <div className=\"text-slate-900 text-sm dark:text-slate-100\">{headerDate}</div>\n <div className=\"flex min-h-4 items-center gap-1 text-slate-500 text-xs dark:text-slate-300\">\n <span>{fieldLabels.author}:</span>\n {showHeaderSkeleton ? <div className=\"h-3 w-24 animate-pulse rounded bg-slate-200 dark:bg-slate-700\" /> : <span>{headerCreator}</span>}\n </div>\n </div>\n <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as DailyReportSidePaneTab)} className=\"flex min-h-0 flex-1 flex-col\">\n <TabsList className=\"grid grid-cols-2 gap-1 rounded-md bg-slate-100 p-1 text-xs dark:bg-slate-800\">\n {[\"article\", \"relations\"].map((tab) => (\n <TabsTrigger\n key={tab}\n value={tab}\n className=\"rounded-md px-2 py-1 font-medium text-slate-600 transition-colors data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm dark:text-slate-200 dark:data-[state=active]:bg-slate-900/70 dark:data-[state=active]:text-slate-100\">\n {tab === \"article\" ? fieldLabels.tabArticle : fieldLabels.tabRelations}\n </TabsTrigger>\n ))}\n </TabsList>\n <TabsContent ref={scrollContainerRef} value=\"article\" className=\"scrollbar-thin flex-1 touch-pan-y overflow-y-auto overflow-x-hidden pt-3 text-sm\">\n {isEditing ? (\n report ? (\n <DailyReportEditForm report={report} onCancel={() => setIsEditing(false)} onPublishSuccess={() => setIsEditing(false)} />\n ) : (\n <p className=\"rounded-lg border border-slate-300 border-dashed bg-slate-50 p-4 text-slate-600 dark:border-slate-600 dark:bg-slate-900/40 dark:text-slate-300\">読み込み中...</p>\n )\n ) : (\n selectedItem && (\n <DailyReportDisplayPane\n report={report}\n isLoading={isLoading}\n error={error}\n selectedItem={selectedItem}\n autoMarkRead={autoMarkRead}\n isActive={isActive}\n onCommentAdded={scrollToBottom}\n userId={userId}\n onDoubleClick={isMine ? () => setIsEditing(true) : undefined}\n />\n )\n )}\n </TabsContent>\n <TabsContent value=\"relations\" className=\"scrollbar-thin flex-1 touch-pan-y overflow-y-auto pt-3 text-sm\">\n <p className=\"rounded-lg border border-slate-300 border-dashed bg-slate-50 p-4 text-slate-600 dark:border-slate-600 dark:bg-slate-900/40 dark:text-slate-300\">{fieldLabels.relationsEmpty}</p>\n </TabsContent>\n </Tabs>\n </>\n )\n\n // If rendered as a standalone pane (desktop)\n if (dailyReportItems) {\n return (\n <aside data-report-id={selectedItem?.reportHubId} className=\"test-daily-report-side-pane hidden min-h-0 min-w-0 flex-1 flex-col gap-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:flex dark:border-slate-700 dark:bg-slate-900/70\" style={heightStyle}>\n {content}\n </aside>\n )\n }\n // If rendered inside mobile overlay\n return (\n <div data-report-id={selectedItem?.reportHubId} className=\"test-daily-report-side-pane flex min-h-0 flex-1 flex-col gap-3 overflow-hidden\">\n {content}\n </div>\n )\n}\n\n/**\n * Shows the selected report details in the display tab, including content and metadata.\n * 表示タブ内で選択した日報の詳細とメタデータを表示するコンポーネント。\n */\nconst DailyReportDisplayPane = ({\n report,\n isLoading,\n error,\n autoMarkRead = false,\n isActive = true,\n onCommentAdded,\n userId,\n onDoubleClick,\n}: {\n report: DailyReportDetail | null\n isLoading: boolean\n error: Error | null\n selectedItem: DailyReportItem\n autoMarkRead?: boolean\n isActive?: boolean\n onCommentAdded?: () => void\n userId?: string | null\n onDoubleClick?: () => void\n}) => {\n // 表示中ユーザーはアクションコンテキストから取得する (root loader 非依存)\n const { toggleStar, toggleRead, deleteReport, user } = useDailyReportActionContext()\n const { fieldLabels } = useDailyReportConfig()\n\n // Use custom hook for comments\n const { uiComments, handleAddComment, handleDeleteComment, pendingDeleteId, setPendingDeleteId, resolvedIdMap } = useDailyReportComments(report, user, userId)\n const { handleToggleRead } = useAutoRead(report, isLoading, autoMarkRead, isActive, toggleRead)\n\n // Auto-scroll to bottom when comments are added (e.g. via SSE)\n const prevReportHubIdRef = useRef<number | null>(null)\n const prevCommentsLengthRef = useRef(uiComments.length)\n\n useEffect(() => {\n const currentReportHubId = report?.reportHubId ?? null\n\n // Reset tracking when switching reports\n if (currentReportHubId !== prevReportHubIdRef.current) {\n prevReportHubIdRef.current = currentReportHubId\n prevCommentsLengthRef.current = uiComments.length\n return\n }\n\n // Scroll if comments increased\n if (uiComments.length > prevCommentsLengthRef.current) {\n onCommentAdded?.()\n }\n prevCommentsLengthRef.current = uiComments.length\n }, [uiComments.length, report?.reportHubId, onCommentAdded])\n\n const handleAddCommentWrapper = async (content: string) => {\n await handleAddComment(content)\n }\n\n const displayContent = useMemo(() => {\n if (!report?.content) return null\n return report.content\n .replace(/<br\\s*\\/?\\s*>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\")\n .replace(/<[^>]+>/g, \"\")\n }, [report])\n\n const handleToggleStar = async () => {\n if (!report) return\n const nextValue = !report.isStarred\n try {\n await toggleStar(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n }\n\n const handleDelete = async () => {\n if (!report) return\n if (!confirm(\"本当に削除しますか?\")) return\n try {\n await deleteReport(report.reportHubId, report.date ?? \"\")\n } catch (_e) {\n alert(\"削除に失敗しました\")\n }\n }\n\n if (error) return <p className=\"text-red-500\">{error.message}</p>\n\n const showSkeleton = !report\n const isStarred = report?.isStarred ?? false\n const isRead = report?.isRead ?? false\n const isMine = !!(userId && report?.userId === userId)\n const hasComments = uiComments.length > 0\n\n const SkeletonText = ({ className = \"w-32\" }: { className?: string }) => <div className={`h-4 animate-pulse rounded bg-slate-200 dark:bg-slate-700 ${className}`} />\n\n return (\n // biome-ignore lint/a11y/noStaticElementInteractions: Double click to edit feature\n <div className=\"relative space-y-4 text-slate-700 dark:text-slate-200\" onDoubleClick={onDoubleClick}>\n {!showSkeleton && report && (\n <div className=\"absolute top-0 right-2 z-10 flex items-center justify-end gap-3\">\n <button type=\"button\" onClick={() => handleToggleRead(false)} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-110\" title={isRead ? \"未読にする\" : \"既読にする\"} data-detail-read-button={report.reportHubId}>\n {isRead ? <span className=\"text-slate-300 hover:text-blue-500\">✓</span> : <UnreadIndicator className=\"hover:text-blue-600\" />}\n </button>\n <button type=\"button\" onClick={handleToggleStar} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-110\" title={isStarred ? \"スターを外す\" : \"スターを付ける\"} data-detail-star-button={report.reportHubId}>\n {isStarred ? <span className=\"text-yellow-400\">★</span> : <span className=\"text-slate-300 hover:text-yellow-400\">☆</span>}\n </button>\n {isMine && (\n <button type=\"button\" onClick={handleDelete} data-detail-delete-button={report.reportHubId} className=\"text-lg leading-none transition-transform focus:outline-none active:scale-110\" title=\"削除する\">\n <Trash2 className=\"h-4 w-4 text-slate-300 hover:text-red-500\" />\n </button>\n )}\n </div>\n )}\n\n <div className=\"flex items-baseline\">\n <span className=\"w-24 shrink-0 font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.subject}</span>\n <div className=\"flex-1 pr-20\">{showSkeleton ? <SkeletonText className=\"w-3/4\" /> : <span className=\"fade-in animate-in text-sm duration-500\">{fallbackText(report?.subject)}</span>}</div>\n </div>\n <div className=\"flex items-baseline\">\n <span className=\"w-24 shrink-0 font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.customer}</span>\n <div className=\"flex-1\">{showSkeleton ? <SkeletonText className=\"w-1/2\" /> : <span className=\"fade-in animate-in text-sm duration-500\">{fallbackText(report?.customerName)}</span>}</div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.content}</div>\n <div className=\"min-h-22 rounded-lg bg-slate-50 p-3 text-sm leading-relaxed dark:bg-slate-800/40\">\n {showSkeleton ? (\n <div className=\"space-y-2\">\n <SkeletonText className=\"w-full\" />\n <SkeletonText className=\"w-11/12\" />\n <SkeletonText className=\"w-4/5\" />\n </div>\n ) : displayContent ? (\n <div className=\"fade-in animate-in whitespace-pre-wrap duration-500\">{displayContent}</div>\n ) : (\n <p className=\"fade-in animate-in text-slate-500 duration-500 dark:text-slate-400\">内容は登録されていません。</p>\n )}\n </div>\n </div>\n <div className=\"flex items-baseline\">\n <span className=\"w-24 shrink-0 font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.visitTime}</span>\n <div className=\"flex-1\">\n {showSkeleton ? (\n <SkeletonText className=\"w-1/3\" />\n ) : (\n <span className=\"fade-in animate-in text-sm duration-500\">\n {fallbackText(report?.visitTimeFrom)} {report?.visitTimeFrom && report?.visitTimeTo ? \"-\" : \"\"} {fallbackText(report?.visitTimeTo)}\n </span>\n )}\n </div>\n </div>\n {report && (\n <div className=\"fade-in animate-in space-y-2 border-slate-100 border-t pt-4 duration-500 dark:border-slate-800\">\n <div className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.comments}</div>\n {hasComments && (\n <DailyReportCommentList\n comments={uiComments}\n reportHubId={report.reportHubId}\n businessDate={report.date ?? \"\"}\n onDeleteComment={handleDeleteComment}\n pendingDeleteId={pendingDeleteId}\n onPendingDeleteIdChange={setPendingDeleteId}\n resolvedIdMap={resolvedIdMap}\n />\n )}\n <DailyReportCommentForm reportHubId={report.reportHubId} businessDate={report.date ?? \"\"} onAddComment={handleAddCommentWrapper} />\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Mobile-only overlay that slides in with the selected daily report details.\n * 選択された日報の詳細をモバイル向けにスライド表示し選択ガードを切り替え可能なオーバーレイ。\n */\nconst DailyReportMobileOverlay = ({ isOpen, selectedItem, onClose, autoMarkRead = false, userId }: { isOpen: boolean; selectedItem: DailyReportItem | null; onClose: () => void; autoMarkRead?: boolean; userId?: string | null }) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const closeButtonRef = useRef<HTMLButtonElement | null>(null)\n\n const { isBlocked: isGuardBlocked, requestBlock, reset: resetGuard } = useSwipeGuardState()\n\n const { report, error, isLoading } = useDailyReportDetail(selectedItem?.reportHubId ?? -1, selectedItem?.businessDate ?? null)\n const displayData = useTransitionDisplay(isOpen, selectedItem ? { item: selectedItem, detail: { report, error, isLoading } } : null)\n const displayItem = displayData?.item ?? null\n const _displayDetail = displayData?.detail ?? EMPTY_DETAIL\n _displayDetail\n const { overlayRef, overlayStyle, attemptClose, isSwiping } = useSwipeToDismissOverlay({\n isOpen,\n onClose,\n isCloseBlocked: useCallback(() => isGuardBlocked, [isGuardBlocked]),\n skipWhenTextSelected: true,\n })\n\n const tapHandlers = useTapClick(() => attemptClose())\n\n /**\n * Manages focus and guard state when overlay opens/closes.\n * オーバーレイの開閉時にフォーカスとガード状態を管理する副作用。\n *\n * Purpose: To set focus on close button for accessibility and reset swipe guard.\n * Dependencies: [isOpen, resetGuard]\n * Cleanup: None.\n */\n useEffect(() => {\n if (isOpen) {\n requestAnimationFrame(() => closeButtonRef.current?.focus())\n } else {\n if (containerRef.current?.contains(document.activeElement)) {\n ;(document.activeElement as HTMLElement).blur()\n }\n resetGuard()\n }\n }, [isOpen, resetGuard])\n\n if (!displayItem) return null\n\n return (\n <div ref={containerRef} className={`fixed inset-0 z-50 flex touch-manipulation transition-[visibility] duration-200 sm:hidden ${isOpen ? \"visible\" : \"invisible\"} ${isOpen && !isSwiping ? \"pointer-events-auto\" : \"pointer-events-none\"}`} aria-hidden={!isOpen}>\n <button type=\"button\" aria-label=\"背景をタップして閉じる\" className={`absolute inset-0 touch-manipulation bg-slate-900/50 transition-opacity duration-200 focus:outline-none ${isOpen ? \"opacity-100\" : \"opacity-0\"}`} {...tapHandlers}></button>\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"日報詳細\"\n className={`relative ml-auto flex h-full w-full max-w-full transform touch-none select-none bg-white shadow-xl transition-transform duration-200 ease-out dark:bg-slate-900/95 ${isOpen ? \"pointer-events-auto\" : \"pointer-events-none\"}`}\n ref={overlayRef}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") {\n e.preventDefault()\n attemptClose()\n }\n }}\n style={overlayStyle}>\n <div className=\"flex h-full w-full flex-col gap-3 p-4\">\n <SwipeCloseGuardContext.Provider value={{ isBlocked: isGuardBlocked, requestBlock }}>\n <DailyReportSidePane selectedItem={displayItem} autoMarkRead={autoMarkRead} isActive={isOpen} userId={userId} />\n </SwipeCloseGuardContext.Provider>\n <div className=\"mt-2 flex justify-start\">\n <button\n type=\"button\"\n ref={closeButtonRef}\n data-overlay-interactive=\"true\"\n className=\"touch-manipulation rounded-full border border-slate-300 px-4 py-2 font-medium text-slate-600 text-sm shadow-sm transition-colors hover:bg-slate-100 dark:border-slate-600 dark:text-slate-200 dark:hover:bg-slate-800\"\n onClick={() => attemptClose()}>\n 閉じる\n </button>\n </div>\n </div>\n </aside>\n </div>\n )\n}\n\n/**\n * Up to three non-empty plain-text lines extracted from HTML content.\n * HTML コンテンツから抽出する最大三つの非空プレーンテキスト行。\n */\nconst extractPreviewLines = (content: string | null): string[] => {\n if (!content) return []\n return content\n .replace(/<br\\s*\\/?\\s*>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\")\n .replace(/<[^>]+>/g, \"\")\n .split(/\\r?\\n/)\n .map((l) => l.replace(/\\u3000/g, \" \").trim())\n .filter((l) => l.length > 0)\n .slice(0, MAX_PREVIEW_LINES)\n}\n\n/**\n * Daily report summary card rendered inside the list tab.\n * List タブ内で表示する日報サマリーカード。\n */\nconst ListReportItem = ({ item, isActive, onSelect }: { item: DailyReportItem; isActive: boolean; onSelect: (id: number) => void }) => {\n const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)\n const { toggleStar, toggleRead, removeStaleItem } = useDailyReportActionContext()\n\n // フェッチが一度でも試行されたかを追跡する ref\n const hasEverFetched = useRef(false)\n if (isRefetching) hasEverFetched.current = true\n\n // フェッチ完了後に report が null → stale アイテムとしてリストから除去する安全弁\n // 外部リソース同期: useDailyReportDetail のフェッチ結果に反応する\n useEffect(() => {\n if (!(report || isLoading || isRefetching || error) && hasEverFetched.current && item.reportHubId > 0) {\n removeStaleItem(item.reportHubId)\n }\n }, [report, isLoading, isRefetching, error, item.reportHubId, removeStaleItem])\n\n const tapHandlers = useTapClick(() => onSelect(item.reportHubId))\n\n if (error) {\n return (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n <div className=\"flex h-full items-center justify-center rounded-2xl border border-red-200 bg-red-50 p-4 text-red-700 text-sm\">\n <p>日報 {item.reportHubId.toLocaleString()} の概要読み込みに失敗しました。</p>\n </div>\n </div>\n )\n }\n\n if (isLoading) return <ListSkeletonItem />\n\n if (!report) {\n return (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n <div className=\"flex h-full items-center justify-center rounded-2xl border border-slate-200 bg-slate-50 p-4 text-slate-500 text-sm dark:border-slate-700 dark:bg-slate-900/40 dark:text-slate-400\">\n <p>日報が見つかりません</p>\n </div>\n </div>\n )\n }\n\n const previewLines = extractPreviewLines(report.content).map((line, i) => ({\n line,\n key: `${item.reportHubId}-line-${i}`,\n }))\n const creator = fallbackText(report.employeeName ?? report.author)\n const date = formatToIsoDate(report.date)\n\n const handleToggleStar = async (e: React.MouseEvent) => {\n e.stopPropagation()\n const nextValue = !report.isStarred\n try {\n await toggleStar(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n }\n\n const handleToggleRead = async (e: React.MouseEvent) => {\n e.stopPropagation()\n const nextValue = !report.isRead\n try {\n await toggleRead(report.reportHubId, nextValue, report.date ?? \"\")\n } catch (_e) {\n // Error handled in context\n }\n }\n\n return (\n <div className=\"px-2 pb-3\" style={{ height: ITEM_CONTAINER_HEIGHT }}>\n {/* biome-ignore lint/a11y/useSemanticElements: Card contains nested interactive elements (Star/Read buttons) */}\n <div\n data-daily-report-button={item.reportHubId}\n className={`flex h-full w-full in-[.is-scrolling]:transform-none cursor-pointer select-none flex-col rounded-2xl border in-[.is-scrolling]:border-slate-200 bg-white p-4 text-left in-[.is-scrolling]:shadow-sm shadow-sm in-[.is-scrolling]:transition-none transition-all duration-200 hover:-translate-y-0.5 hover:border-blue-300 hover:shadow-lg dark:bg-slate-900/70 ${\n isActive ? \"border-blue-300 shadow-md ring-2 ring-blue-200 dark:border-blue-500/70 dark:ring-blue-500/40\" : \"border-slate-200 dark:border-slate-700\"\n }`}\n style={{ touchAction: \"manipulation\" }}\n role=\"button\"\n tabIndex={0}\n aria-pressed={isActive}\n {...tapHandlers}>\n <div className=\"mb-3 flex items-center gap-3 text-slate-600 text-sm\">\n <span className=\"inline-flex shrink-0 items-center rounded-full bg-slate-100 px-3 py-1 font-semibold text-xs dark:bg-slate-800 dark:text-slate-200\">\n <span className=\"whitespace-nowrap font-medium text-slate-900 dark:text-slate-100\">{date}</span>\n </span>\n <span className=\"inline-flex min-w-0 max-w-full items-center rounded-full bg-slate-100 px-3 py-1 font-semibold text-xs dark:bg-slate-800 dark:text-slate-200\">\n <span className=\"truncate font-medium text-slate-900 dark:text-slate-100\">{creator}</span>\n </span>\n <div className=\"ml-auto flex items-center gap-1.5\">\n <button\n type=\"button\"\n title={report.isStarred ? \"スターを外す\" : \"スターを付ける\"}\n data-list-star-button={item.reportHubId}\n onClick={handleToggleStar}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 dark:hover:bg-slate-700\">\n {report.isStarred ? <Star className=\"h-3.5 w-3.5 fill-yellow-400 text-yellow-400\" /> : <Star className=\"h-3.5 w-3.5 text-slate-300 dark:text-slate-500\" />}\n </button>\n <button\n type=\"button\"\n title={report.isRead ? \"未読にする\" : \"既読にする\"}\n data-list-read-button={item.reportHubId}\n onClick={handleToggleRead}\n className=\"flex h-6 w-6 items-center justify-center rounded-full transition-colors hover:bg-slate-200 dark:hover:bg-slate-700\">\n {report.isRead ? <Check className=\"h-3.5 w-3.5 text-blue-500\" /> : <UnreadIndicator className=\"text-xs\" />}\n </button>\n </div>\n </div>\n <div className=\"space-y-1 text-slate-700 text-sm leading-relaxed dark:text-slate-200\">\n {previewLines.length > 0 ? (\n previewLines.map(({ line, key }) => (\n <p key={key} className=\"wrap-break-word line-clamp-1\">\n {line}\n </p>\n ))\n ) : (\n <p>内容は登録されていません</p>\n )}\n </div>\n </div>\n </div>\n )\n}\n","import * as TabsPrimitive from \"@radix-ui/react-tabs\"\nimport * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nconst Tabs = TabsPrimitive.Root\n\nconst TabsList = React.forwardRef<React.ElementRef<typeof TabsPrimitive.List>, React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>>(({ className, ...props }, ref) => (\n <TabsPrimitive.List ref={ref} className={cn(\"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground\", className)} {...props} />\n))\nTabsList.displayName = TabsPrimitive.List.displayName\n\nconst TabsTrigger = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Trigger>, React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 font-medium text-sm ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-2xs\",\n className,\n )}\n {...props}\n />\n))\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName\n\n// https://github.com/radix-ui/primitives/issues/2359\nconst TabsContent = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Content>, React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n // forceMount={true} //<=======Add this line\n className={cn(\n \"data-[state=inactive]:hidden\", //<=========Add this line\n \"mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n className,\n )}\n {...props}\n />\n))\nTabsContent.displayName = TabsPrimitive.Content.displayName\n\nexport { Tabs, TabsContent, TabsList, TabsTrigger }\n","/**\n * Top-level daily-report page content (chrome-agnostic).\n * アプリのレイアウトに依存しない日報ページ本体コンポーネント。\n *\n * アプリ側のルートは本コンポーネントを自前のレイアウト (Main 等) で包み、\n * `config` でヘッダー描画・パス・ラベル名を注入する。\n */\nimport { Suspense, useCallback, useEffect, useState } from \"react\"\nimport { Await, useRevalidator } from \"react-router\"\nimport type { DailyReportItem, DailyReportUser } from \"../../shared/types\"\nimport { type DailyReportClientConfigInput, DailyReportConfigProvider, useDailyReportConfig } from \"../config-context\"\nimport { DailyReportActionProvider } from \"../contexts/daily-report-action-context\"\nimport { DAILY_REPORT_FETCH_DEBOUNCE_MS } from \"../utils/constants\"\nimport { DailyReportResolvedContent } from \"./daily-report-resolved-content\"\n\n/**\n * Synchronizes resolved daily report ids into the action context provider state.\n * Also renders the resolved content after synchronization trigger is set.\n *\n * 解決済みの日報 ID を Action Context 側へ同期し、解決済みコンテンツを描画するコンポーネント。\n */\nconst DailyReportResolvedSync = ({ dailyReportItems, userId, onResolved }: { dailyReportItems: DailyReportItem[]; userId?: string | null; onResolved: (items: DailyReportItem[]) => void }) => {\n /**\n * Notifies parent when resolved items change.\n * Dependencies:\n * - dailyReportItems: latest resolved list from Await.\n * - onResolved: parent synchronization callback.\n * Cleanup:\n * - None (pure synchronization side effect).\n *\n * 解決済み items が変化したときに親へ通知します。\n * 依存配列:\n * - dailyReportItems: Await で解決された最新リスト\n * - onResolved: 親への同期コールバック\n * クリーンアップ:\n * - なし(同期のみの副作用)\n */\n useEffect(() => {\n onResolved(dailyReportItems)\n }, [dailyReportItems, onResolved])\n\n return <DailyReportResolvedContent dailyReportItems={dailyReportItems} userId={userId} />\n}\n\n/**\n * Error display component for daily report data fetch failures.\n * 日報データの取得失敗時に表示するエラーコンポーネント。\n */\nexport const DailyReportLoadError = () => {\n const { title, renderHeader } = useDailyReportConfig()\n const { revalidate } = useRevalidator()\n return (\n <>\n {renderHeader({ title, annotation: <span className=\"font-bold text-red-500\">Error</span> })}\n <div className=\"h-full bg-slate-50 p-2\">\n <div className=\"mx-auto flex h-full w-full max-w-6xl flex-1 flex-col gap-6\">\n <div className=\"flex h-full flex-1 flex-col items-center justify-center gap-4 rounded-lg border border-red-200 bg-white p-4 text-slate-500 shadow-sm dark:border-red-900 dark:bg-slate-900/70 dark:text-slate-400\">\n <p className=\"font-medium text-red-600\">日報データの読み込みに失敗しました。</p>\n <button type=\"button\" onClick={() => revalidate()} className=\"rounded bg-slate-100 px-4 py-2 text-slate-700 text-sm hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700\">\n 再読み込み\n </button>\n </div>\n </div>\n </div>\n </>\n )\n}\n\n/**\n * Suspense fallback while daily report identifiers resolve.\n * 日報 ID 解決中のサスペンスフォールバック。\n */\nexport const DailyReportLoading = () => {\n const { title, renderHeader } = useDailyReportConfig()\n return (\n <>\n {renderHeader({\n title,\n annotation: (\n <div className=\"flex flex-wrap gap-2 leading-none\">\n <span>総件数: 読み込み中...</span>\n <span>デバウンス: {DAILY_REPORT_FETCH_DEBOUNCE_MS} ms</span>\n </div>\n ),\n })}\n <div className=\"h-full bg-slate-50 p-2\">\n <div className=\"mx-auto flex h-full w-full max-w-6xl flex-1 flex-col gap-6\">\n <div className=\"flex h-full flex-1 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-500 shadow-sm dark:border-slate-700 dark:bg-slate-900/70 dark:text-slate-300\">日報 ID を読み込み中です...</div>\n </div>\n </div>\n </>\n )\n}\n\nexport type DailyReportPageProps = {\n /** 表示中ユーザー (楽観的コメントの作者名等に使用)。 */\n user?: DailyReportUser\n /** 難読化済みの内部ユーザー ID (未登録ユーザーは null)。 */\n userId?: string | null\n /** clientLoader で defer した日報 ID 一覧 (Promise) または解決済み配列。 */\n dailyReportIds?: Promise<DailyReportItem[]> | DailyReportItem[] | null\n /** クライアント設定の部分上書き (ヘッダー描画・パス・ラベル名・フィールドラベルなど)。 */\n config?: DailyReportClientConfigInput\n}\n\n/**\n * Daily report page content rendering provider, suspense boundary, and resolved views.\n * プロバイダー・サスペンス境界・解決済みビューを描画する日報ページ本体。\n */\nconst DailyReportPageInner = ({ user, userId, dailyReportIds }: Omit<DailyReportPageProps, \"config\">) => {\n const [resolvedInitialItems, setResolvedInitialItems] = useState<DailyReportItem[]>([])\n\n const handleResolvedItems = useCallback((items: DailyReportItem[]) => {\n setResolvedInitialItems(items)\n }, [])\n\n return (\n <DailyReportActionProvider user={user} initialItems={resolvedInitialItems} userId={userId}>\n <Suspense fallback={<DailyReportLoading />}>\n {/* Resolve で非同期取得済み ID を確定表示 */}\n {dailyReportIds ? (\n <Await resolve={dailyReportIds} errorElement={<DailyReportLoadError />}>\n {(resolvedItems) => {\n if (!resolvedItems) {\n throw new Error(\"Daily report ids resolve must not be null\")\n }\n // 非 null を保証して以降の処理で利用\n return <DailyReportResolvedSync dailyReportItems={resolvedItems as DailyReportItem[]} userId={userId} onResolved={handleResolvedItems} />\n }}\n </Await>\n ) : (\n <DailyReportLoading />\n )}\n </Suspense>\n </DailyReportActionProvider>\n )\n}\n\n/**\n * Chrome-agnostic daily report page with client configuration provider.\n * 設定プロバイダー込みの日報ページコンポーネント。\n */\nexport const DailyReportPage = ({ config, ...rest }: DailyReportPageProps) => (\n <DailyReportConfigProvider config={config}>\n <DailyReportPageInner {...rest} />\n </DailyReportConfigProvider>\n)\n","import { Plus } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useEffect, useMemo, useState } from \"react\"\nimport { useSearchParams } from \"react-router\"\nimport type { DailyReportItem } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\nimport { useDailyReportActionContext } from \"../contexts/daily-report-action-context\"\nimport { Button } from \"../ui/button\"\nimport { Label } from \"../ui/label\"\nimport { Switch } from \"../ui/switch\"\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\"\nimport { DAILY_REPORT_FETCH_DEBOUNCE_MS } from \"../utils/constants\"\nimport { DailyReportDetailList } from \"./daily-report-detail-list\"\nimport { DailyReportList } from \"./daily-report-list\"\n\nconst tabTriggerClass = \"min-w-24 p-2 font-medium text-slate-600 text-xs transition-colors data-[state=active]:bg-white data-[state=active]:text-slate-900 sm:min-w-36 sm:text-sm dark:text-slate-300 dark:data-[state=active]:bg-slate-800 dark:data-[state=active]:text-slate-100\"\n\ntype DailyReportResolvedContentProps = {\n dailyReportItems: DailyReportItem[]\n userId?: string | null\n}\n\n/**\n * Displays fetched daily reports with layout and tabbed virtual scroll views.\n * レイアウトとタブ付き仮想スクロールで取得済み日報を表示するコンポーネント。\n */\nexport const DailyReportResolvedContent = ({ dailyReportItems: _initialItems, userId }: DailyReportResolvedContentProps) => {\n // 自動既読設定\n const [autoMarkRead, setAutoMarkRead] = useState(false)\n const { title, renderHeader, showDevControls } = useDailyReportConfig()\n const { isSseEnabled, toggleSse, createReport, items: dailyReportItems } = useDailyReportActionContext()\n const [searchParams] = useSearchParams()\n\n // 選択状態の管理 (タブ間で共有)\n const [selectedItemId, setSelectedItemId] = useState<number | null>(() => {\n if (dailyReportItems.length === 0) return null\n return dailyReportItems[0].reportHubId\n })\n\n const handleCreateReport = async () => {\n const businessDateParam = searchParams.get(\"businessDate\")\n const today = new Date().toISOString().split(\"T\")[0]\n const dateToUse = businessDateParam || today\n const newId = await createReport(dateToUse)\n setSelectedItemId(newId)\n }\n\n /**\n * Effect to load the auto-read setting from local storage on mount.\n * マウント時にローカルストレージから自動既読設定を読み込む副作用。\n *\n * Purpose: To persist user preference for auto-read behavior.\n * Dependencies: [] - Runs only on mount.\n * Cleanup: None.\n */\n useEffect(() => {\n const stored = localStorage.getItem(\"daily-report-auto-read\")\n if (stored === \"true\") {\n setAutoMarkRead(true)\n }\n }, [])\n\n const handleAutoMarkReadChange = (checked: boolean) => {\n setAutoMarkRead(checked)\n localStorage.setItem(\"daily-report-auto-read\", String(checked))\n }\n\n const tabItems = useMemo<{ value: string; label: string; content: ReactNode }[]>(\n () => [\n {\n value: \"list\",\n label: \"📄 List\",\n content: <DailyReportList dailyReportItems={dailyReportItems} autoMarkRead={autoMarkRead} selectedReportHubId={selectedItemId} onSelectItem={setSelectedItemId} userId={userId} />,\n },\n {\n value: \"tree\",\n label: \"🌳 Tree\",\n content: <div className=\"flex flex-1 items-center justify-center rounded-lg border border-slate-300 border-dashed bg-white p-6 text-slate-500 transition-colors dark:border-slate-700 dark:bg-slate-900/70 dark:text-slate-300\">Tree ビューは現在準備中です</div>,\n },\n {\n value: \"detailList\",\n label: \"📋 DetailList\",\n content: <DailyReportDetailList dailyReportItems={dailyReportItems} userId={userId} selectedItemId={selectedItemId} onSelectItem={setSelectedItemId} />,\n },\n ],\n [dailyReportItems, autoMarkRead, selectedItemId, userId],\n )\n\n return (\n <>\n {renderHeader({\n title,\n annotation: (\n <div className=\"flex flex-wrap gap-2 leading-1\">\n <span>総件数: {dailyReportItems.length.toLocaleString()} 件</span>\n <span>デバウンス: {DAILY_REPORT_FETCH_DEBOUNCE_MS} ms</span>\n </div>\n ),\n })}\n <div className=\"h-full bg-slate-50 dark:bg-slate-500\">\n <div className=\"flex h-full w-full max-w-6xl flex-1 flex-col p-1\">\n <Tabs defaultValue=\"list\" className=\"flex h-full flex-1 flex-col\">\n <div className=\"flex items-center justify-between gap-2 rounded-md bg-slate-50 p-1 transition-colors dark:bg-slate-900/70 dark:text-slate-200\">\n <TabsList className=\"scrollbar-none flex w-full touch-pan-x flex-nowrap overflow-x-auto overflow-y-hidden bg-transparent p-0\">\n {tabItems.map(({ value, label }) => (\n <TabsTrigger key={value} value={value} className={tabTriggerClass}>\n {label}\n </TabsTrigger>\n ))}\n </TabsList>\n <div className=\"flex shrink-0 items-center space-x-1 px-2\">\n <Button variant=\"ghost\" size=\"icon\" onClick={handleCreateReport} title=\"日報を作成\">\n <Plus className=\"h-4 w-4\" />\n </Button>\n {showDevControls && (\n <div className=\"hidden items-center space-x-1 sm:flex\">\n <Switch id=\"sse-mode\" checked={isSseEnabled} onCheckedChange={toggleSse} className=\"scale-75\" />\n <Label htmlFor=\"sse-mode\" className=\"cursor-pointer text-slate-500 text-xs\">\n 購読\n </Label>\n </div>\n )}\n <Switch id=\"auto-read-mode\" checked={autoMarkRead} onCheckedChange={handleAutoMarkReadChange} className=\"scale-75\" />\n <Label htmlFor=\"auto-read-mode\" className=\"cursor-pointer text-slate-500 text-xs\">\n 自動既読\n </Label>\n </div>\n </div>\n {tabItems.map(({ value, content }) => (\n <TabsContent key={value} value={value} className=\"mt-0 flex h-full min-h-0 flex-1 flex-col\">\n {content}\n </TabsContent>\n ))}\n </Tabs>\n </div>\n </div>\n </>\n )\n}\n","import * as SwitchPrimitives from \"@radix-ui/react-switch\"\nimport * as React from \"react\"\n\nimport { cn } from \"./cn\"\n\nconst Switch = React.forwardRef<React.ElementRef<typeof SwitchPrimitives.Root>, React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>>(({ className, ...props }, ref) => (\n <SwitchPrimitives.Root\n className={cn(\n \"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n className,\n )}\n {...props}\n ref={ref}>\n <SwitchPrimitives.Thumb className={cn(\"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\")} />\n </SwitchPrimitives.Root>\n))\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n","/**\n * React Router route helpers for the daily-report page (revalidation policy / client loader).\n * 日報ページ向け React Router ルートヘルパー (再検証ポリシー / クライアントローダー)。\n */\nimport type { ShouldRevalidateFunction, ShouldRevalidateFunctionArgs } from \"react-router\"\nimport type { DailyReportItem } from \"../shared/types\"\nimport { defaultDailyReportClientConfig } from \"./config-context\"\n\n/**\n * Prevents loader revalidation when POP navigations keep identical URL state.\n * URL 状態が同一の POP ナビゲーション時にローダー再検証を抑止。\n */\nexport const dailyReportShouldRevalidate: ShouldRevalidateFunction = ({ actionResult, actionStatus, currentUrl, defaultShouldRevalidate, formAction, formData, formEncType, formMethod, json, nextUrl, text }: ShouldRevalidateFunctionArgs) => {\n if (!defaultShouldRevalidate) {\n return false\n }\n\n const isSameUrl = currentUrl.pathname === nextUrl.pathname && currentUrl.search === nextUrl.search && currentUrl.hash === nextUrl.hash\n if (!isSameUrl) {\n return defaultShouldRevalidate\n }\n\n if (formData) {\n const intent = formData.get(\"intent\")\n if (intent === \"toggleRead\" || intent === \"toggleStar\" || intent === \"addComment\" || intent === \"deleteComment\" || intent === \"create\" || intent === \"update\" || intent === \"publish\" || intent === \"delete\") {\n return false\n }\n }\n\n const hasSubmission = actionResult !== undefined || actionStatus !== undefined || formAction !== undefined || formData !== undefined || formEncType !== undefined || formMethod !== undefined || json !== undefined || text !== undefined\n return hasSubmission ? defaultShouldRevalidate : false\n}\n\n/**\n * Fetches the deferred daily report id list from the API endpoint.\n * API エンドポイントから日報 ID 一覧を遅延取得する処理。\n */\nexport const fetchDailyReportIds = (apiBasePath: string = defaultDailyReportClientConfig.apiBasePath): Promise<DailyReportItem[]> =>\n fetch(`${apiBasePath}/ids`, {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" },\n cache: \"no-cache\",\n })\n .then(async (response) => {\n if (!response.ok) {\n throw new Error(`Failed to fetch daily report ids: ${response.status} ${response.statusText}`)\n }\n return response\n })\n .then(async (response) => response.json())\n .then((data) => {\n if (!(data.ids && Array.isArray(data.ids))) {\n throw new Error(\"Invalid daily report ids response\")\n }\n return data.ids as DailyReportItem[]\n })\n\n/**\n * Creates a React Router clientLoader that merges server data with deferred report ids.\n * サーバーデータへ遅延日報 ID を合成する React Router clientLoader を生成する処理。\n *\n * 返す関数へ `hydrate = true as const` を付与済み (初期ハイドレーションでも実行される)。\n */\nexport const createDailyReportClientLoader = <TServerData extends Record<string, unknown>>(options?: { apiBasePath?: string }) => {\n const clientLoader = async (args: { serverLoader: () => Promise<unknown> }): Promise<TServerData & { dailyReportIds: Promise<DailyReportItem[]> }> => {\n // サーバーローダーの解決を待つ間に ID 取得を並行開始する\n const idsPromise = fetchDailyReportIds(options?.apiBasePath)\n const serverData = (await args.serverLoader()) as TServerData\n return {\n ...serverData,\n dailyReportIds: idsPromise,\n }\n }\n clientLoader.hydrate = true as const\n return clientLoader\n}\n"],"mappings":"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","names":["client_exports","__export","BusinessDayThumbOverlay","DAILY_REPORT_FETCH_DEBOUNCE_MS","DEFAULT_ITEM_HEIGHT","DailyReportActionProvider","DailyReportCommentForm","DailyReportCommentItem","DailyReportCommentList","DailyReportConfigProvider","DailyReportDetailList","DailyReportEditForm","DailyReportList","DailyReportLoadError","DailyReportLoading","DailyReportPage","DailyReportResolvedContent","EMPTY_DETAIL","EMPTY_ITEM","FAST_SCROLL_INERTIA_OPTIONS","FAST_SCROLL_WHEEL_MULTIPLIER","ITEM_CONTAINER_HEIGHT","ITEM_VERTICAL_PADDING","MAX_PREVIEW_LINES","PREFETCH_LOOKAHEAD","PREFETCH_MAX_DATES","SCROLL_BAR_WIDTH","TAP_SCROLL_CIRCLE_OPTIONS","UnreadIndicator","VIRTUAL_SCROLL_OVERSCAN_COUNT","WHEEL_RESET_TIMEOUT_MS","acquireMutationLock","applyDailyReportServerUpdates","createDailyReportClientLoader","dailyReportShouldRevalidate","defaultDailyReportClientConfig","defaultDailyReportFieldLabels","deleteDailyReportCache","fetchDailyReportIds","getCachedReport","registerRecentDeletion","releaseMutationLock","subscribeCacheReady","updateDailyReportCache","useDailyReportActionContext","useDailyReportComments","useDailyReportConfig","useDailyReportDetail","useDailyReportPrefetch","useDailyReportSseConnection","useDynamicViewportHeight","writeCache","__toCommonJS","import_jsx_runtime","BusinessDayThumbOverlay","orientation","thumbCenter","label","isDragging","isTapScrollActive","isWheelScrollActive","import_outline","import_react","mergeComments","legacyComments","modernComments","currentUserId","legacyUIComments","c","index","modernUIComments","resolveCommentColorClass","color","normalized","fallbackText","value","trimmed","formatToIsoDate","date","year","month","day","hours","minutes","seconds","import_clsx","import_tailwind_merge","cn","inputs","import_jsx_runtime","checkIsPendingDelete","commentId","pendingDeleteId","resolvedIdMap","DailyReportCommentItem","comment","_reportHubId","_businessDate","onOptimisticDelete","onPendingDeleteIdChange","onTrashClick","isDeleted","setIsDeleted","isPendingDelete","handleConfirmDelete","e","cn","resolveCommentColorClass","fallbackText","formatToIsoDate","import_react","import_jsx_runtime","useAutoResetPendingDelete","pendingDeleteId","onPendingDeleteIdChange","timerRef","handleGlobalClick","e","commentId","DailyReportCommentList","comments","reportHubId","businessDate","onDeleteComment","resolvedIdMap","handleTrashClick","comment","DailyReportCommentItem","DailyReportCommentForm","onAddComment","isSubmitting","setIsSubmitting","formRef","content","input","import_virtualscroll","import_lucide_react","import_react","import_react","import_jsx_runtime","defaultDailyReportFieldLabels","defaultDailyReportClientConfig","title","annotation","DailyReportConfigContext","DailyReportConfigProvider","config","children","merged","useDailyReportConfig","import_react","createLogger","level","prefix","impl","format","message","rest","import_react","ISO_DATE_PATTERN","SLASH_DATE_PATTERN","zeroPad","value","normalizeFromDate","year","month","day","normalizeBusinessDateKey","trimmed","parsed","logger","createLogger","getGlobal","k","d","v","TTL","REVIVE_MS","FETCH_DEBOUNCE_MS","DailyReportCache","now","GC_THRESHOLD","id","entry","key","ex","l","ignoreExpiry","e","dateKey","report","expiresAt","syncList","normalizeBusinessDateKey","listEntry","data","r","updates","strict","date","list","serverReport","cachedReport","c","serverIds","missing","b","reports","fetchStart","t","processed","lastMut","cached","merged","writeCache","exp","sync","getCachedReport","opts","updateDailyReportCache","u","deleteDailyReportCache","acquireMutationLock","releaseMutationLock","registerRecentDeletion","cId","subscribeCacheReady","reportHubId","callback","listener","applyDailyReportServerUpdates","_reportHubIdd","fetchReportById","apiBasePath","pending","start","p","res","fetchReportsByDate","useDailyReportDetail","businessDate","useDailyReportConfig","setReport","error","setError","isLoading","setIsLoading","isRefetching","setIsRefetching","reqRef","onUpdate","uid","token","isStale","found","timer","item","useDailyReportPrefetch","import_react","import_zod","dailyReportInterviewerSchema","dailyReportCommentSchema","dailyReportLabelDefSchema","dailyReportCommentItemSchema","dailyReportDetailSchema","connectedMessageSchema","statusUpdateMessageSchema","commentAddMessageSchema","commentDeleteMessageSchema","reportCreateMessageSchema","reportUpdateMessageSchema","reportPublishMessageSchema","reportDeleteMessageSchema","dailyReportSseMessageSchema","logger","createLogger","useDailyReportSseConnection","isSseEnabled","onMessage","ssePath","useDailyReportConfig","eventSource","reconnectTimer","reconnectAttempt","lastReceivedEventId","connect","event","raw","result","dailyReportSseMessageSchema","e","delayMs","import_jsx_runtime","logger","createLogger","DailyReportActionContext","useDailyReportActionContext","context","getUserDisplayName","user","fullName","DailyReportActionProvider","children","initialItems","userId","apiBasePath","draftLabelName","useDailyReportConfig","pendingAddCommentIds","pendingDeleteCommentIds","pendingStarUpdates","pendingReadUpdates","resolvedIdMap","version","setVersion","items","setItems","isSseEnabled","setIsSseEnabled","pendingAddPromises","manuallyTrackingIds","removedIds","processedClientTempIds","pendingSseQueue","handleSseMessageRef","enqueuePendingSseMessage","reportHubId","message","queue","existing","entry","e","subscribeCacheReady","getCachedReport","msg","addProcessedId","id","isProcessedId","upsertItem","businessDate","prev","nextBusinessDate","item","a","b","left","toggleSse","removeStaleItem","initialIds","prevItems","mergedMap","dateA","handleSseMessage","payload","comment","cached","c","isMyComment","pendingMap","tempId","content","newComment","updatedReport","applyDailyReportServerUpdates","acquireMutationLock","releaseMutationLock","v","commentId","statusType","value","pendingEntry","deleteDailyReportCache","report","useDailyReportSseConnection","executeAction","intent","params","callbacks","options","clientTempId","formData","k","res","error","data","addComment","promise","realId","cachedReport","updateDailyReportCache","deleteComment","resolved","deletedId","registerRecentDeletion","toggleStar","isStarred","toggleReadImpl","isRead","toggleReadRef","toggleRead","createReport","now","dummyReport","writeCache","newId","updateReport","publishReport","oldLabels","newLabels","l","deleteReport","targetItem","i","_data","import_react","useDailyReportComments","report","user","userId","pendingAddCommentIds","pendingDeleteCommentIds","resolvedIdMap","version","addComment","deleteComment","useDailyReportActionContext","pendingDeleteId","setPendingDeleteId","handleDeleteComment","cId","handleAddComment","content","prevUiCommentsRef","comments","mergeComments","deletedIds","c","addedComments","existingIds","authorName","tempId","realId","import_react","useDynamicViewportHeight","containerRef","options","enableMutationObserver","bottomGap","minFallbackHeight","viewportHeight","setViewportHeight","updateViewportHeight","target","toNumber","value","containerTop","parentRect","rootStyle","footerHeight","headerHeight","scrollbarHeight","containerStyle","marginBottom","paddingBottom","viewportBottom","effectiveBottom","layoutTop","effectiveTop","availableHeight","safeAvailableHeight","clampedHeight","previous","frameId","timeoutId","resizeObserver","mutationObserver","scheduleUpdate","containerElement","import_virtualscroll","DAILY_REPORT_FETCH_DEBOUNCE_MS","DEFAULT_ITEM_HEIGHT","ITEM_VERTICAL_PADDING","ITEM_CONTAINER_HEIGHT","FAST_SCROLL_WHEEL_MULTIPLIER","FAST_SCROLL_INERTIA_OPTIONS","PREFETCH_LOOKAHEAD","PREFETCH_MAX_DATES","EMPTY_ITEM","EMPTY_DETAIL","WHEEL_RESET_TIMEOUT_MS","VIRTUAL_SCROLL_OVERSCAN_COUNT","SCROLL_BAR_WIDTH","MAX_PREVIEW_LINES","TAP_SCROLL_CIRCLE_OPTIONS","import_react","import_react_slot","import_class_variance_authority","React","import_jsx_runtime","buttonVariants","Button","className","variant","size","asChild","props","ref","cn","import_jsx_runtime","Input","className","type","props","cn","LabelPrimitive","import_jsx_runtime","Label","className","props","cn","import_jsx_runtime","Textarea","className","props","cn","import_jsx_runtime","DailyReportEditForm","report","onCancel","onPublishSuccess","updateReport","publishReport","useDailyReportActionContext","title","setTitle","content","setContent","isSaving","setIsSaving","syncedReportIdRef","handleSave","handlePublish","Label","Input","e","Textarea","Button","import_jsx_runtime","UnreadIndicator","className","cn","import_jsx_runtime","useReportItemHeightObserver","ref","itemId","onHeightMeasured","element","resizeObserver","DailyReportDetailRow","children","elementRef","formatInterviewerLabel","interviewer","DailyReportDetailList","dailyReportItems","userId","selectedItemId","onSelectItem","itemHeights","setItemHeights","scrollContainerRef","virtualScrollRef","viewportHeight","updateViewportHeight","useDynamicViewportHeight","prefetchDailyReportByDate","useDailyReportPrefetch","overlayLabel","setOverlayLabel","isWheelScrollActive","setIsWheelScrollActive","wheelResetTimerRef","scrollEndTimerRef","dailyReportItemsRef","lastScrolledRef","index","item","onSelectItemRef","el","handle","extendedHandle","id","itemCount","getItem","EMPTY_ITEM","schedulePrefetchForRange","fromIndex","toIndex","boundedFrom","boundedTo","businessDates","candidate","PREFETCH_MAX_DATES","date","handleRangeChange","renderingStartIndex","renderingEndIndex","visibleStartIndex","firstVisibleItem","formatToIsoDate","PREFETCH_LOOKAHEAD","handleScroll","container","renderThumbOverlay","props","BusinessDayThumbOverlay","onWheel","updateItemHeight","height","prev","newMap","getItemHeight","getItemKey","tapScrollCircleOptions","handleCommentAdded","renderItem","DailyReportItemContainer","VIRTUAL_SCROLL_OVERSCAN_COUNT","FAST_SCROLL_WHEEL_MULTIPLIER","FAST_SCROLL_INERTIA_OPTIONS","SkeletonReportItem","onCommentAdded","report","error","isLoading","isRefetching","useDailyReportDetail","removeStaleItem","useDailyReportActionContext","draftLabelName","useDailyReportConfig","isEditing","setIsEditing","hasBypassedAutoEdit","setHasBypassedAutoEdit","autoEditingRef","hasEverFetched","isDraft","l","isMine","DailyReportError","DailyReportEditForm","DailyReportResolvedItem","DailyReportHeader","isRead","isStarred","onToggleRead","onToggleStar","onDelete","visitTimeDisplay","categoryLabel","fieldLabels","UnreadIndicator","fallbackText","MetadataPanel","onDoubleClick","formContainerRef","toggleStar","toggleRead","pendingStarUpdates","pendingReadUpdates","deleteReport","user","uiComments","handleAddComment","handleDeleteComment","pendingDeleteId","setPendingDeleteId","resolvedIdMap","useDailyReportComments","handleAddCommentWrapper","content","handleToggleRead","handleToggleStar","handleDelete","normalizedContent","from","to","labels","DailyReportCommentList","DailyReportCommentForm","import_swipe_overlay","import_virtualscroll","import_lucide_react","import_react","TabsPrimitive","React","import_jsx_runtime","Tabs","TabsList","className","props","ref","cn","TabsTrigger","TabsContent","import_jsx_runtime","useMobileOverlayManager","isMobileLayout","selectedReportHubId","dailyReportItems","onSelectItem","isMobileDetailOpen","setIsMobileDetailOpen","isOverlayMounted","setIsOverlayMounted","overlayHistoryKeyRef","lastClosedItemIdRef","bodyOverflowRef","selectedItem","e","shouldShowOverlay","timer","key","onPopState","handleOverlayClose","useAutoRead","report","isLoading","autoMarkRead","isActive","toggleRead","ignoreAutoReadForReportHubId","lastReportHubIdRef","handleToggleRead","isAuto","nextValue","DailyReportList","initialSelectedBusinessDate","initialSelectedReportHubId","userId","scrollContainerRef","virtualScrollRef","viewportHeight","updateViewportHeight","useDynamicViewportHeight","overlayLabel","setOverlayLabel","isWheelScrollActive","setIsWheelScrollActive","setIsMobileLayout","wheelResetTimerRef","appliedInitialSignatureRef","itemCount","scrollEndTimerRef","columnHeightStyle","mq","onMediaChange","onResize","el","onWheel","WHEEL_RESET_TIMEOUT_MS","signature","hasInitial","match","i","targetId","index","item","prev","next","formatToIsoDate","dailyReportItemsRef","id","limit","handleItemSelect","getItem","EMPTY_ITEM","getItemHeight","DEFAULT_ITEM_HEIGHT","handleRangeChange","range","handleItemFocus","handleScroll","container","renderThumbOverlay","props","BusinessDayThumbOverlay","VIRTUAL_SCROLL_OVERSCAN_COUNT","SCROLL_BAR_WIDTH","TAP_SCROLL_CIRCLE_OPTIONS","FAST_SCROLL_INERTIA_OPTIONS","FAST_SCROLL_WHEEL_MULTIPLIER","ITEM_VERTICAL_PADDING","ListReportItem","DailyReportSidePane","DailyReportMobileOverlay","ListSkeletonItem","ITEM_CONTAINER_HEIGHT","heightStyle","activeTab","setActiveTab","isEditing","setIsEditing","draftLabelName","fieldLabels","useDailyReportConfig","error","useDailyReportDetail","lastReportIdRef","autoEditRef","isDraft","l","isMine","scrollToBottom","headerDate","headerCreator","fallbackText","showHeaderSkeleton","content","Tabs","v","TabsList","tab","TabsTrigger","TabsContent","DailyReportEditForm","DailyReportDisplayPane","onCommentAdded","onDoubleClick","toggleStar","deleteReport","user","useDailyReportActionContext","uiComments","handleAddComment","handleDeleteComment","pendingDeleteId","setPendingDeleteId","resolvedIdMap","useDailyReportComments","prevReportHubIdRef","prevCommentsLengthRef","currentReportHubId","handleAddCommentWrapper","displayContent","handleToggleStar","handleDelete","showSkeleton","isStarred","isRead","hasComments","SkeletonText","className","UnreadIndicator","DailyReportCommentList","DailyReportCommentForm","isOpen","onClose","containerRef","closeButtonRef","isGuardBlocked","requestBlock","resetGuard","displayData","displayItem","EMPTY_DETAIL","overlayRef","overlayStyle","attemptClose","isSwiping","tapHandlers","extractPreviewLines","MAX_PREVIEW_LINES","onSelect","isRefetching","removeStaleItem","hasEverFetched","previewLines","line","creator","date","import_react","import_react_router","import_lucide_react","import_react","import_react_router","SwitchPrimitives","React","import_jsx_runtime","Switch","className","props","ref","cn","import_jsx_runtime","tabTriggerClass","DailyReportResolvedContent","_initialItems","userId","autoMarkRead","setAutoMarkRead","title","renderHeader","showDevControls","useDailyReportConfig","isSseEnabled","toggleSse","createReport","dailyReportItems","useDailyReportActionContext","searchParams","selectedItemId","setSelectedItemId","handleCreateReport","businessDateParam","today","newId","handleAutoMarkReadChange","checked","tabItems","DailyReportList","DailyReportDetailList","DAILY_REPORT_FETCH_DEBOUNCE_MS","Tabs","TabsList","value","label","TabsTrigger","Button","Switch","Label","content","TabsContent","import_jsx_runtime","DailyReportResolvedSync","dailyReportItems","userId","onResolved","DailyReportResolvedContent","DailyReportLoadError","title","renderHeader","useDailyReportConfig","revalidate","DailyReportLoading","DAILY_REPORT_FETCH_DEBOUNCE_MS","DailyReportPageInner","user","dailyReportIds","resolvedInitialItems","setResolvedInitialItems","handleResolvedItems","items","DailyReportActionProvider","resolvedItems","DailyReportPage","config","rest","DailyReportConfigProvider","dailyReportShouldRevalidate","actionResult","actionStatus","currentUrl","defaultShouldRevalidate","formAction","formData","formEncType","formMethod","json","nextUrl","text","intent","fetchDailyReportIds","apiBasePath","defaultDailyReportClientConfig","response","data","createDailyReportClientLoader","options","clientLoader","args","idsPromise"]}
|