@aiquants/daily-report 0.6.2 → 0.7.0
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/dist/client.d.mts +81 -3
- package/dist/client.d.ts +81 -3
- 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 +434 -6
- package/dist/server.d.ts +434 -6
- package/dist/server.js +6 -6
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +6 -6
- package/dist/server.mjs.map +1 -1
- package/dist/{sse-schema-rbG114od.d.mts → sse-schema-Df49KA7B.d.mts} +643 -249
- package/dist/{sse-schema-eXcMG-Ej.d.ts → sse-schema-RHckD7TS.d.ts} +643 -249
- package/dist/styles/daily-report.standalone.css +1 -1
- package/dist/{types-D1PKubyo.d.mts → types-Ct1ggzy-.d.mts} +30 -1
- package/dist/{types-D1PKubyo.d.ts → types-Ct1ggzy-.d.ts} +30 -1
- package/package.json +3 -3
- package/src/client/components/daily-report-attachment-indicator.spec.tsx +51 -0
- package/src/client/components/daily-report-attachment-indicator.tsx +45 -0
- package/src/client/components/daily-report-attachment-list.spec.tsx +94 -0
- package/src/client/components/daily-report-attachment-list.tsx +112 -0
- package/src/client/components/daily-report-detail-list.tsx +14 -7
- package/src/client/components/daily-report-list.tsx +19 -11
- package/src/client/components/report-views.spec.tsx +340 -0
- package/src/client/config-context.tsx +8 -0
- package/src/client/contexts/daily-report-action-context.tsx +2 -0
- package/src/client/hooks/use-responsive-layout.spec.ts +74 -0
- package/src/client/hooks/use-responsive-layout.ts +56 -0
- package/src/client/utils/constants.spec.ts +96 -0
- package/src/client/utils/constants.ts +47 -2
- package/src/client.ts +2 -0
- package/src/server/handlers.attachment.spec.ts +470 -0
- package/src/server/handlers.ts +426 -1
- package/src/server/ports.ts +74 -0
- package/src/server/schema.ts +80 -5
- package/src/server/service.spec.ts +280 -14
- package/src/server/service.ts +308 -25
- package/src/server/test-helpers/handlers-config.ts +142 -0
- package/src/server.ts +16 -1
- package/src/shared/sse-schema.spec.ts +50 -0
- package/src/shared/sse-schema.ts +27 -0
- package/src/shared/types.ts +31 -0
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\n data-comment-id={comment.id}\n data-deleted={isDeleted ? \"true\" : undefined}\n 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, resolveSourceTypeConfig, 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 initialScrollOffset?: number\n onScrollOffsetChange?: (offset: number) => void\n}\n\n/**\n * Daily report detail cards rendered with dynamic height virtual scroll.\n * 動的高さの仮想スクロールで日報詳細カードを描画するコンポーネント。\n */\nexport const DailyReportDetailList = ({ dailyReportItems, userId, selectedItemId, onSelectItem, initialScrollOffset, onScrollOffsetChange }: 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('[data-testid=\"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 if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n\n const container = scrollContainerRef.current\n if (!container) return\n\n if (container.dataset.scrolling !== \"true\") {\n container.dataset.scrolling = \"true\"\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n delete container.dataset.scrolling\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [onScrollOffsetChange])\n\n useEffect(() => {\n return () => {\n if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n }\n }, [onScrollOffsetChange])\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 initialScrollOffset={initialScrollOffset}\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 testId=\"daily-report-detail-list\"\n className=\"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 itemSourceType,\n visitTimeDisplay,\n categoryLabel,\n fieldLabels,\n}: {\n report: DailyReportDetail\n itemSourceType?: string\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 const { sourceTypeConfigs } = useDailyReportConfig()\n return (\n <div className=\"flex flex-wrap items-center gap-2 font-medium text-slate-600 text-xs\">\n {(() => {\n const sourceType = itemSourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\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 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 <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/**\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 sourceType = item.sourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\n const isExternalSource = sourceType ? sourceType.toLowerCase() !== \"internal\" : false\n const canComment = sourceConfig?.allowComment ?? !isExternalSource\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 itemSourceType={item.sourceType}\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 {canComment && (\n <div className=\"mt-auto\" ref={formContainerRef}>\n <DailyReportCommentForm reportHubId={report.reportHubId} businessDate={report.date ?? \"\"} onAddComment={handleAddCommentWrapper} />\n </div>\n )}\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 SourceTypeConfig = {\n label: string\n badgeClassName?: string\n /** コメント投稿を許可するかどうか (既定 true)。false の場合、該当ソースの日報でコメント投稿フォームを非表示にする。 */\n allowComment?: boolean\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 /** 詳細/一覧カード of field 見出しラベル。既定は中立英語。アプリが自ドメインの表記を注入する。 */\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 Internal: {\n label: \"オリジナル\",\n badgeClassName: \"bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-400\",\n },\n },\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/**\n * Resolves source type badge configuration with case-insensitive fallback.\n * ケースインセンシティブなフォールバック付きでデータソースバッジ設定を解決する。\n */\nexport const resolveSourceTypeConfig = (sourceType?: string | null, sourceTypeConfigs?: Record<string, SourceTypeConfig>): SourceTypeConfig | undefined => {\n if (!(sourceType && sourceTypeConfigs)) return undefined\n if (sourceTypeConfigs[sourceType]) return sourceTypeConfigs[sourceType]\n const lower = sourceType.toLowerCase()\n const matchedKey = Object.keys(sourceTypeConfigs).find((k) => k.toLowerCase() === lower)\n return matchedKey ? sourceTypeConfigs[matchedKey] : undefined\n}\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 { useRevalidator } from \"react-router\"\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, clearDailyReportCache, DailyReportCache, 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 refetchData: () => Promise<void>\n /** キャッシュを破棄してデータのみ再読み込み (ページリロードなし) */\n clearCacheAndRefetch: () => Promise<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 const revalidator = useRevalidator()\n\n const refetchData = useCallback(async () => {\n logger.info(\"[DailyReportActionProvider] Refetching data without page reload...\")\n if (revalidator.state === \"idle\") {\n revalidator.revalidate()\n }\n setVersion((v) => v + 1)\n DailyReportCache.listeners.forEach((listener: (id: number) => void) => {\n listener(-1)\n })\n }, [revalidator])\n\n const clearCacheAndRefetch = useCallback(async () => {\n logger.info(\"[DailyReportActionProvider] Clearing client & server/DB cache and refetching data without page reload...\")\n // 1. クライアント側キャッシュ破棄\n clearDailyReportCache()\n removedIds.current.clear()\n try {\n sessionStorage.clear()\n } catch {}\n\n // 2. サーバー/DB側キャッシュ破棄 API 呼び出し\n try {\n const formData = new FormData()\n formData.append(\"intent\", \"clearCache\")\n const res = await fetch(`${apiBasePath}/action`, {\n method: \"POST\",\n body: formData,\n })\n if (!res.ok) {\n const errData = await res.json().catch(() => null)\n logger.warn(\"[DailyReportActionProvider] Failed to clear server-side cache:\", res.status, errData)\n }\n } catch (e) {\n logger.warn(\"[DailyReportActionProvider] Failed to clear server-side cache:\", e)\n }\n\n // 3. ルートローダーの再フェッチ & UI 再描画\n if (revalidator.state === \"idle\") {\n revalidator.revalidate()\n }\n setVersion((v) => v + 1)\n DailyReportCache.listeners.forEach((listener: (id: number) => void) => {\n listener(-1)\n })\n }, [apiBasePath, revalidator])\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 || 0).getTime() - new Date(b.createdAt || 0).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 refetchData,\n clearCacheAndRefetch,\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\nexport const 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 * Clear all cached entries and pending requests.\n * すべてのキャッシュエントリおよび待機中リクエストをクリアする。\n */\n clear() {\n this.reports.clear()\n this.lists.clear()\n this.pendingLists.clear()\n this.pendingReports.clear()\n this.locks.clear()\n this.lastMutations.clear()\n this.recentDeletes.clear()\n this.listeners.forEach((listener) => {\n listener(-1)\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 || 0).getTime() < 10000)\n if (missing.length > 0) {\n report = {\n ...report,\n commentItems: [...report.commentItems, ...missing].sort((a, b) => new Date(a.createdAt || 0).getTime() - new Date(b.createdAt || 0).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/**\n * Clear all daily report cache store entries.\n * 日報の全キャッシュストアを消去する。\n */\nexport const clearDailyReportCache = () => DailyReportCache.clear()\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.flatten(), \"raw message:\", raw)\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().nullish(),\n affiliation: z.string().nullish(),\n})\n\nexport const dailyReportCommentSchema = z.object({\n name: z.string().nullish(),\n text: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportLabelDefSchema = z.object({\n id: z.number(),\n name: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportCommentItemSchema = z.object({\n id: z.number(),\n userId: z.string().nullish(),\n userName: z.string().nullish(),\n content: z.string().nullish(),\n createdAt: z.string().nullish(),\n isMine: z.boolean(),\n})\n\nexport const dailyReportDetailSchema = z.object({\n reportHubId: z.number(),\n date: z.string().nullish(),\n createdAt: z.string().nullish(),\n author: z.string().nullish(),\n userId: z.string().nullish(),\n sourceType: z.string().nullish(),\n employeeName: z.string().nullish(),\n updatedBy: z.string().nullish(),\n updatedAt: z.string().nullish(),\n category: z.string().nullish(),\n creationCategory: z.string().nullish(),\n visitTimeFrom: z.string().nullish(),\n visitTimeTo: z.string().nullish(),\n customerName: z.string().nullish(),\n interviewers: z.array(dailyReportInterviewerSchema),\n subject: z.string().nullish(),\n content: z.string().nullish(),\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 recipientRawUserId: z.number().optional(),\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 { resolveSourceTypeConfig, 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 initialScrollOffset?: number\n onScrollOffsetChange?: (offset: number) => void\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, initialScrollOffset, onScrollOffsetChange }: 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('[data-testid=\"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 if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n\n const container = scrollContainerRef.current\n if (!container) return\n\n if (container.dataset.scrolling !== \"true\") {\n container.dataset.scrolling = \"true\"\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n delete container.dataset.scrolling\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [onScrollOffsetChange])\n\n useEffect(() => {\n return () => {\n if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n }\n }, [onScrollOffsetChange])\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 initialScrollOffset={initialScrollOffset}\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 testId=\"daily-report-list\"\n className=\"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 data-skeleton 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 data-skeleton 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\n data-testid=\"daily-report-side-pane\"\n data-report-id={selectedItem?.reportHubId}\n className=\"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\"\n style={heightStyle}>\n {content}\n </aside>\n )\n }\n // If rendered inside mobile overlay\n return (\n <div data-testid=\"daily-report-side-pane\" data-report-id={selectedItem?.reportHubId} className=\"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 selectedItem,\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, sourceTypeConfigs } = 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 const sourceType = selectedItem.sourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\n const isExternalSource = sourceType ? sourceType.toLowerCase() !== \"internal\" : false\n const canComment = sourceConfig?.allowComment ?? !isExternalSource\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 data-skeleton 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 {canComment && <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 { sourceTypeConfigs } = useDailyReportConfig()\n const { toggleStar, toggleRead, removeStaleItem } = useDailyReportActionContext()\n const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)\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 ?? null).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-[[data-scrolling]]:transform-none cursor-pointer select-none flex-col rounded-2xl border in-[[data-scrolling]]:border-slate-200 bg-white p-4 text-left in-[[data-scrolling]]:shadow-sm shadow-sm in-[[data-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 = item.sourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\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, RefreshCw, RotateCw } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useCallback, useEffect, useMemo, useRef, 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, refetchData, clearCacheAndRefetch } = useDailyReportActionContext()\n const [searchParams] = useSearchParams()\n\n // 各タブのスクロール位置をアンマウント後も保持する Ref\n const listScrollOffsetRef = useRef<number>(0)\n const detailListScrollOffsetRef = useRef<number>(0)\n\n const handleListScrollOffsetChange = useCallback((offset: number) => {\n listScrollOffsetRef.current = offset\n }, [])\n\n const handleDetailListScrollOffsetChange = useCallback((offset: number) => {\n detailListScrollOffsetRef.current = offset\n }, [])\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 handleReload = async () => {\n await refetchData()\n }\n\n const handleReloadClearCache = async () => {\n await clearCacheAndRefetch()\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: (\n <DailyReportList\n dailyReportItems={dailyReportItems}\n autoMarkRead={autoMarkRead}\n selectedReportHubId={selectedItemId}\n onSelectItem={setSelectedItemId}\n userId={userId}\n initialScrollOffset={listScrollOffsetRef.current}\n onScrollOffsetChange={handleListScrollOffsetChange}\n />\n ),\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: (\n <DailyReportDetailList dailyReportItems={dailyReportItems} userId={userId} selectedItemId={selectedItemId} onSelectItem={setSelectedItemId} initialScrollOffset={detailListScrollOffsetRef.current} onScrollOffsetChange={handleDetailListScrollOffsetChange} />\n ),\n },\n ],\n [dailyReportItems, autoMarkRead, selectedItemId, userId, handleListScrollOffsetChange, handleDetailListScrollOffsetChange],\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.5 rounded-md border border-amber-300 bg-amber-50 px-2 py-0.5 text-xs sm:flex dark:border-amber-700/50 dark:bg-amber-950/40\">\n <span className=\"rounded bg-amber-200 px-1 py-0.2 font-bold font-mono text-[10px] text-amber-900 dark:bg-amber-800 dark:text-amber-100\" title=\"開発環境限定のコントロールです\">\n DEV\n </span>\n <div className=\"flex items-center space-x-1\">\n <Switch id=\"sse-mode\" checked={isSseEnabled} onCheckedChange={toggleSse} className=\"scale-75\" />\n <Label htmlFor=\"sse-mode\" className=\"cursor-pointer text-slate-600 text-xs dark:text-slate-300\" title=\"開発環境のみ: Real-time SSE 購読切り替え\">\n 購読\n </Label>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={handleReload} className=\"h-6 px-1.5 text-slate-700 text-xs hover:bg-amber-200 dark:text-slate-200 dark:hover:bg-amber-800\" title=\"開発環境のみ: ページリロードなしでデータのみ再取得\">\n <RotateCw className=\"mr-1 h-3 w-3\" />\n リロード\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleReloadClearCache}\n className=\"h-6 px-1.5 text-amber-800 text-xs hover:bg-amber-200 dark:text-amber-300 dark:hover:bg-amber-800\"\n title=\"開発環境のみ: サーバー/DB & クライアントのキャッシュを破棄してデータ再取得\">\n <RefreshCw className=\"mr-1 h-3 w-3\" />\n キャッシュクリア\n </Button>\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, forceRefresh = false): Promise<DailyReportItem[]> => {\n const url = new URL(`${apiBasePath}/ids`, typeof window !== \"undefined\" ? window.location.origin : \"http://localhost\")\n if (forceRefresh) {\n url.searchParams.set(\"forceRefresh\", \"true\")\n url.searchParams.set(\"_t\", Date.now().toString())\n }\n return fetch(url.toString(), {\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/**\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","DailyReportCache","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","clearDailyReportCache","createDailyReportClientLoader","dailyReportShouldRevalidate","defaultDailyReportClientConfig","defaultDailyReportFieldLabels","deleteDailyReportCache","fetchDailyReportIds","getCachedReport","registerRecentDeletion","releaseMutationLock","resolveSourceTypeConfig","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","resolveSourceTypeConfig","sourceType","sourceTypeConfigs","lower","matchedKey","k","import_react","import_react_router","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","listener","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","clearDailyReportCache","acquireMutationLock","releaseMutationLock","registerRecentDeletion","cId","subscribeCacheReady","reportHubId","callback","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","revalidator","refetchData","v","DailyReportCache","listener","clearCacheAndRefetch","clearDailyReportCache","formData","res","errData","e","pendingSseQueue","handleSseMessageRef","enqueuePendingSseMessage","reportHubId","message","queue","existing","entry","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","commentId","statusType","value","pendingEntry","deleteDailyReportCache","report","useDailyReportSseConnection","executeAction","intent","params","callbacks","options","clientTempId","k","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","initialScrollOffset","onScrollOffsetChange","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","itemSourceType","visitTimeDisplay","categoryLabel","fieldLabels","sourceTypeConfigs","sourceType","sourceConfig","resolveSourceTypeConfig","cn","UnreadIndicator","fallbackText","MetadataPanel","onDoubleClick","formContainerRef","toggleStar","toggleRead","pendingStarUpdates","pendingReadUpdates","deleteReport","user","uiComments","handleAddComment","handleDeleteComment","pendingDeleteId","setPendingDeleteId","resolvedIdMap","useDailyReportComments","isExternalSource","canComment","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","initialScrollOffset","onScrollOffsetChange","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","resolveSourceTypeConfig","isExternalSource","canComment","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","removeStaleItem","isRefetching","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","refetchData","clearCacheAndRefetch","useDailyReportActionContext","searchParams","listScrollOffsetRef","detailListScrollOffsetRef","handleListScrollOffsetChange","offset","handleDetailListScrollOffsetChange","selectedItemId","setSelectedItemId","handleReload","handleReloadClearCache","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","forceRefresh","url","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-attachment-indicator.tsx","../src/client/config-context.tsx","../src/client/components/daily-report-attachment-list.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/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/hooks/use-responsive-layout.ts","../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-attachment-indicator\"\nexport * from \"./client/components/daily-report-attachment-list\"\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","/**\n * Compact \"has attachments\" marker for summary rows.\n * 一覧の要約行に出す「添付あり」の小さな目印。\n *\n * 一覧をスクロールしながら添付の有無を一目で判断できるようにするためのもの。\n * 詳細カードの添付節 (`DailyReportAttachmentList`) とは役割が異なり、\n * **ファイル名も配信 URL も出さない**。行に出すのは有無と件数だけであり、\n * リンクを置くと一覧のタップ領域と競合するうえ、配信エンドポイントを\n * 一覧から誘発しやすくなる (原寸をサーバーのヒープへ載せる経路である)。\n *\n * DOM フックは `data-*` 属性のみを使う (AGENTS.md)。\n */\nimport { Paperclip } from \"lucide-react\"\n\n/** 件数を併記し始める閾値。1 件のときは記号だけで足りる。 */\nconst COUNT_VISIBLE_FROM = 2\n\ntype DailyReportAttachmentIndicatorProps = {\n /** 添付の件数。0 なら何も描画しない。 */\n count: number\n /** 読み上げ・ツールチップ用の見出し。消費アプリの `fieldLabels.attachments` を渡す。 */\n label: string\n}\n\n/**\n * Renders the attachment marker, or nothing when the report has no attachment.\n * 添付の目印を描画する (添付が無ければ何も描画しない)。\n *\n * @param props Attachment count and the label. 添付件数と見出し。\n * @returns The marker element, or null. 目印の要素 (添付が無ければ null)。\n */\nexport const DailyReportAttachmentIndicator = ({ count, label }: DailyReportAttachmentIndicatorProps) => {\n if (count <= 0) {\n return null\n }\n\n // role=\"img\" を付けるのは aria-label を有効にするため (role の無い span では無効になる)。\n // 中身は記号 1 つで、読み上げ上も 1 つの図像として扱うのが適切。\n return (\n <span className=\"inline-flex shrink-0 items-center gap-0.5 rounded px-1 py-0.5 text-slate-500 dark:text-slate-400\" title={`${label}: ${count}`} role=\"img\" aria-label={`${label}: ${count}`} data-testid=\"daily-report-attachment-indicator\" data-attachment-count={count}>\n <Paperclip className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {count >= COUNT_VISIBLE_FROM && <span className=\"font-semibold text-[10px] leading-none\">{count}</span>}\n </span>\n )\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 attachments: string\n /** 実体の消失が確認された添付に添える注記。 */\n attachmentMissing: string\n /** 添付をダウンロードするリンクのラベル。 */\n attachmentDownload: string\n tabArticle: string\n tabRelations: string\n relationsEmpty: string\n}\n\n/** データソースごとのバッジ表示設定。 */\nexport type SourceTypeConfig = {\n label: string\n badgeClassName?: string\n /** コメント投稿を許可するかどうか (既定 true)。false の場合、該当ソースの日報でコメント投稿フォームを非表示にする。 */\n allowComment?: boolean\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 /** 詳細/一覧カード of field 見出しラベル。既定は中立英語。アプリが自ドメインの表記を注入する。 */\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 attachments: \"Attachments\",\n attachmentMissing: \"File is missing\",\n attachmentDownload: \"Download\",\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 Internal: {\n label: \"オリジナル\",\n badgeClassName: \"bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-400\",\n },\n },\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/**\n * Resolves source type badge configuration with case-insensitive fallback.\n * ケースインセンシティブなフォールバック付きでデータソースバッジ設定を解決する。\n */\nexport const resolveSourceTypeConfig = (sourceType?: string | null, sourceTypeConfigs?: Record<string, SourceTypeConfig>): SourceTypeConfig | undefined => {\n if (!(sourceType && sourceTypeConfigs)) return undefined\n if (sourceTypeConfigs[sourceType]) return sourceTypeConfigs[sourceType]\n const lower = sourceType.toLowerCase()\n const matchedKey = Object.keys(sourceTypeConfigs).find((k) => k.toLowerCase() === lower)\n return matchedKey ? sourceTypeConfigs[matchedKey] : undefined\n}\n","/**\n * Attachment list rendered inside a daily report detail card.\n * 日報詳細カード内に描画する添付ファイル一覧。\n *\n * このコンポーネントは詳細カードの本体 (`DailyReportDetailRow` の children) の内側に置くこと。\n * 行の高さは行ごとの `ResizeObserver` が測って仮想スクロールへ通知するため、\n * portal や absolute 配置で測定コンテナの外へ出すと行高が壊れ、\n * タブ切替時のスクロール位置保存 E2E も落ちる。\n *\n * **ネットワーク要求を自動発火させないこと。** 配信エンドポイントは 1 リクエストごとに\n * 実体全体をサーバーのヒープへ載せる経路で、レート上限と同時実行ゲートを持つ。\n * ここで `<img src={配信URL}>` のような自動取得を置くと、仮想スクロールの再マウントの\n * たびに実体 (数十 MiB) を取り直し、利用者自身のダウンロードを 429 / 503 で潰す。\n * サムネイルを出すなら縮小画像を返す専用経路を用意してからにする。\n *\n * DOM フックは `data-*` 属性のみを使う。クラス名はスタイリング契約であり\n * 選択子として使ってはならない (AGENTS.md)。\n */\nimport type { DailyReportAttachmentSummary } from \"../../shared/types\"\nimport { useDailyReportConfig } from \"../config-context\"\n\n/** 1 KiB。サイズ表記の基数。 */\nconst BYTES_PER_UNIT = 1024\n\n/** サイズ表記の単位列。 */\nconst SIZE_UNITS = [\"B\", \"KB\", \"MB\", \"GB\"] as const\n\n/**\n * Formats a byte count for display, or returns null when unknown.\n * バイト数を表示用に整形する。不明なら null を返す。\n *\n * サイズを報告しない取込元の添付は null になるため、0 と null を区別する\n * (0 は「空ファイル」、null は「サイズ不明」)。\n *\n * @param bytes Size in bytes, or null when unknown. バイト数 (不明なら null)。\n * @returns Human-readable size, or null. 可読なサイズ表記 (不明なら null)。\n */\nconst formatFileSize = (bytes: number | null): string | null => {\n if (bytes === null || !Number.isFinite(bytes) || bytes < 0) return null\n let value = bytes\n let unitIndex = 0\n while (value >= BYTES_PER_UNIT && unitIndex < SIZE_UNITS.length - 1) {\n value /= BYTES_PER_UNIT\n unitIndex += 1\n }\n // 1 KB 未満は小数を出さない\n return unitIndex === 0 ? `${value} ${SIZE_UNITS[unitIndex]}` : `${value.toFixed(1)} ${SIZE_UNITS[unitIndex]}`\n}\n\ntype DailyReportAttachmentListProps = {\n /** 表示する添付一覧。空配列ならこのコンポーネントは何も描画しない。 */\n attachments: DailyReportAttachmentSummary[]\n}\n\n/**\n * Renders the attachment section of a daily report detail card.\n * 日報詳細カードの添付ファイル節を描画するコンポーネント。\n *\n * 表示文言はすべて `fieldLabels` から取る (パッケージは業務ドメインの語彙も言語も持たない)。\n *\n * @param props Attachments to render. 描画する添付一覧。\n * @returns The section element, or null when there is nothing to show. 節の要素 (表示対象が無ければ null)。\n */\nexport const DailyReportAttachmentList = ({ attachments }: DailyReportAttachmentListProps) => {\n const { apiBasePath, fieldLabels } = useDailyReportConfig()\n\n if (attachments.length === 0) {\n return null\n }\n\n return (\n <div className=\"flex flex-col gap-2\" data-testid=\"daily-report-attachment-section\">\n <span className=\"font-semibold text-slate-500 text-xs uppercase tracking-wide dark:text-slate-300\">{fieldLabels.attachments}</span>\n <ul className=\"flex flex-col gap-1.5\">\n {attachments.map((attachment) => {\n // URL は必ず apiBasePath から組む。パッケージ内にルートを焼き込まない\n const href = `${apiBasePath}/attachment/${attachment.id}`\n const size = formatFileSize(attachment.fileSize)\n const isMissing = attachment.state === \"absent\"\n\n return (\n <li key={attachment.id} className=\"flex items-center gap-2 rounded-lg bg-slate-50 px-3 py-2 dark:bg-slate-800/40\" data-testid=\"daily-report-attachment-item\" data-attachment-state={attachment.state}>\n <div className=\"flex min-w-0 flex-col\">\n {isMissing ? (\n // 消失が確認された添付はリンクにしない (押しても 404 になるだけ)\n <span className=\"truncate text-slate-400 text-sm line-through dark:text-slate-500\" data-testid=\"daily-report-attachment-name\">\n {attachment.fileName}\n </span>\n ) : (\n <a href={href} className=\"truncate text-blue-600 text-sm hover:underline dark:text-blue-400\" data-testid=\"daily-report-attachment-link\">\n {attachment.fileName}\n </a>\n )}\n <span className=\"text-[10px] text-slate-400 dark:text-slate-500\">\n {size ?? \"—\"}\n {/* 実体の消失を確認できた場合のみ注記する。unknown は「未検証」であって\n 「消失」ではないため、通常表示のままにする (判定不能を偽の真偽で埋めない) */}\n {isMissing && ` · ${fieldLabels.attachmentMissing}`}\n </span>\n </div>\n {!isMissing && (\n <a href={`${href}?download=1`} className=\"ml-auto shrink-0 text-[10px] text-slate-500 hover:text-slate-700 hover:underline dark:text-slate-400 dark:hover:text-slate-200\" data-testid=\"daily-report-attachment-download\">\n {fieldLabels.attachmentDownload}\n </a>\n )}\n </li>\n )\n })}\n </ul>\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\n data-comment-id={comment.id}\n data-deleted={isDeleted ? \"true\" : undefined}\n 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 ScrollBarThumbOverlayRenderProps, 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, resolveSourceTypeConfig, 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 { CONTENT_DRAG_POINTER_INPUTS, EMPTY_ITEM, FAST_SCROLL_INERTIA_OPTIONS, FAST_SCROLL_WHEEL_MULTIPLIER, PREFETCH_LOOKAHEAD, PREFETCH_MAX_DATES, SCROLL_BAR_WIDTH, TAP_SCROLL_CIRCLE_OPTIONS, VIRTUAL_SCROLL_OVERSCAN_COUNT, WHEEL_RESET_TIMEOUT_MS } from \"../utils/constants\"\nimport { BusinessDayThumbOverlay } from \"./business-day-thumb-overlay\"\nimport { DailyReportAttachmentIndicator } from \"./daily-report-attachment-indicator\"\nimport { DailyReportAttachmentList } from \"./daily-report-attachment-list\"\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 initialScrollOffset?: number\n onScrollOffsetChange?: (offset: number) => void\n}\n\n/**\n * Daily report detail cards rendered with dynamic height virtual scroll.\n * 動的高さの仮想スクロールで日報詳細カードを描画するコンポーネント。\n */\nexport const DailyReportDetailList = ({ dailyReportItems, userId, selectedItemId, onSelectItem, initialScrollOffset, onScrollOffsetChange }: 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('[data-testid=\"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 if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n\n const container = scrollContainerRef.current\n if (!container) return\n\n if (container.dataset.scrolling !== \"true\") {\n container.dataset.scrolling = \"true\"\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n delete container.dataset.scrolling\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [onScrollOffsetChange])\n\n useEffect(() => {\n return () => {\n if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n }\n }, [onScrollOffsetChange])\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 }, WHEEL_RESET_TIMEOUT_MS)\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 /**\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 initialScrollOffset={initialScrollOffset}\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 // ドラッグスクロールは無効化せず、**ポインタ種別で絞る**。\n // 丸ごと切るとタッチ端末からスクロール手段が消える (定数の注記参照)\n pointerDragInputs: CONTENT_DRAG_POINTER_INPUTS,\n wheelSpeedMultiplier: FAST_SCROLL_WHEEL_MULTIPLIER,\n inertiaOptions: FAST_SCROLL_INERTIA_OPTIONS,\n }}\n onRangeChange={handleRangeChange}\n onScroll={handleScroll}\n testId=\"daily-report-detail-list\"\n className=\"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 itemSourceType,\n visitTimeDisplay,\n categoryLabel,\n fieldLabels,\n}: {\n report: DailyReportDetail\n itemSourceType?: string\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 const { sourceTypeConfigs } = useDailyReportConfig()\n return (\n <div className=\"flex flex-wrap items-center gap-2 font-medium text-slate-600 text-xs\">\n {/* 添付の有無を一目判別できるようにする。仕様書が定める\n [sourceType] [Star] [Read] の相対順は崩さず、その手前に置く */}\n <DailyReportAttachmentIndicator count={report.attachments.length} label={fieldLabels.attachments} />\n {(() => {\n const sourceType = itemSourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\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 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 <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/**\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 sourceType = item.sourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\n const isExternalSource = sourceType ? sourceType.toLowerCase() !== \"internal\" : false\n const canComment = sourceConfig?.allowComment ?? !isExternalSource\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 itemSourceType={item.sourceType}\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 <DailyReportAttachmentList attachments={report.attachments} />\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 {canComment && (\n <div className=\"mt-auto\" ref={formContainerRef}>\n <DailyReportCommentForm reportHubId={report.reportHubId} businessDate={report.date ?? \"\"} onAddComment={handleAddCommentWrapper} />\n </div>\n )}\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 * 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 { useRevalidator } from \"react-router\"\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, clearDailyReportCache, DailyReportCache, 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 refetchData: () => Promise<void>\n /** キャッシュを破棄してデータのみ再読み込み (ページリロードなし) */\n clearCacheAndRefetch: () => Promise<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 const revalidator = useRevalidator()\n\n const refetchData = useCallback(async () => {\n logger.info(\"[DailyReportActionProvider] Refetching data without page reload...\")\n if (revalidator.state === \"idle\") {\n revalidator.revalidate()\n }\n setVersion((v) => v + 1)\n DailyReportCache.listeners.forEach((listener: (id: number) => void) => {\n listener(-1)\n })\n }, [revalidator])\n\n const clearCacheAndRefetch = useCallback(async () => {\n logger.info(\"[DailyReportActionProvider] Clearing client & server/DB cache and refetching data without page reload...\")\n // 1. クライアント側キャッシュ破棄\n clearDailyReportCache()\n removedIds.current.clear()\n try {\n sessionStorage.clear()\n } catch {}\n\n // 2. サーバー/DB側キャッシュ破棄 API 呼び出し\n try {\n const formData = new FormData()\n formData.append(\"intent\", \"clearCache\")\n const res = await fetch(`${apiBasePath}/action`, {\n method: \"POST\",\n body: formData,\n })\n if (!res.ok) {\n const errData = await res.json().catch(() => null)\n logger.warn(\"[DailyReportActionProvider] Failed to clear server-side cache:\", res.status, errData)\n }\n } catch (e) {\n logger.warn(\"[DailyReportActionProvider] Failed to clear server-side cache:\", e)\n }\n\n // 3. ルートローダーの再フェッチ & UI 再描画\n if (revalidator.state === \"idle\") {\n revalidator.revalidate()\n }\n setVersion((v) => v + 1)\n DailyReportCache.listeners.forEach((listener: (id: number) => void) => {\n listener(-1)\n })\n }, [apiBasePath, revalidator])\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 || 0).getTime() - new Date(b.createdAt || 0).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 attachments: [],\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 refetchData,\n clearCacheAndRefetch,\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\nexport const 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 * Clear all cached entries and pending requests.\n * すべてのキャッシュエントリおよび待機中リクエストをクリアする。\n */\n clear() {\n this.reports.clear()\n this.lists.clear()\n this.pendingLists.clear()\n this.pendingReports.clear()\n this.locks.clear()\n this.lastMutations.clear()\n this.recentDeletes.clear()\n this.listeners.forEach((listener) => {\n listener(-1)\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 || 0).getTime() < 10000)\n if (missing.length > 0) {\n report = {\n ...report,\n commentItems: [...report.commentItems, ...missing].sort((a, b) => new Date(a.createdAt || 0).getTime() - new Date(b.createdAt || 0).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/**\n * Clear all daily report cache store entries.\n * 日報の全キャッシュストアを消去する。\n */\nexport const clearDailyReportCache = () => DailyReportCache.clear()\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.flatten(), \"raw message:\", raw)\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().nullish(),\n affiliation: z.string().nullish(),\n})\n\nexport const dailyReportCommentSchema = z.object({\n name: z.string().nullish(),\n text: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportLabelDefSchema = z.object({\n id: z.number(),\n name: z.string().nullish(),\n color: z.string().nullish(),\n})\n\nexport const dailyReportCommentItemSchema = z.object({\n id: z.number(),\n userId: z.string().nullish(),\n userName: z.string().nullish(),\n content: z.string().nullish(),\n createdAt: z.string().nullish(),\n isMine: z.boolean(),\n})\n\n/**\n * 添付ファイル 1 件のスキーマ。\n *\n * 周囲のフィールドは `.nullish()` だが、ここは意図して `.nullable()` を使う。\n * `.nullish()` は推論キーを任意 (`?`) にするため、`types.ts` 側の必須 `fileType: string | null`\n * へ代入できず、ファイル末尾の `_AssertDetailCompat` が `never` に落ちてコンパイルエラーになる。\n * 既存の `.nullish()` フィールドはいずれも `types.ts` 側が `?` 付きの任意キーとペアになっており、\n * 「`.nullish()` ↔ 任意キー」「`.nullable()` ↔ 必須 `| null`」の対応で揃っている。\n * `| undefined` を混ぜないことは、詳細型を消費する DuckDB ステージ行の要請でもある。\n */\nexport const dailyReportAttachmentItemSchema = z.object({\n id: z.string(),\n fileName: z.string(),\n fileType: z.string().nullable(),\n fileSize: z.number().nullable(),\n createdAt: z.string().nullable(),\n state: z.enum([\"present\", \"absent\", \"unknown\"]),\n})\n\nexport const dailyReportDetailSchema = z.object({\n reportHubId: z.number(),\n date: z.string().nullish(),\n createdAt: z.string().nullish(),\n author: z.string().nullish(),\n userId: z.string().nullish(),\n sourceType: z.string().nullish(),\n employeeName: z.string().nullish(),\n updatedBy: z.string().nullish(),\n updatedAt: z.string().nullish(),\n category: z.string().nullish(),\n creationCategory: z.string().nullish(),\n visitTimeFrom: z.string().nullish(),\n visitTimeTo: z.string().nullish(),\n customerName: z.string().nullish(),\n interviewers: z.array(dailyReportInterviewerSchema),\n subject: z.string().nullish(),\n content: z.string().nullish(),\n comments: z.array(dailyReportCommentSchema),\n isRead: z.boolean(),\n isStarred: z.boolean(),\n labels: z.array(dailyReportLabelDefSchema),\n commentItems: z.array(dailyReportCommentItemSchema),\n // ❗ `.default([])` は必須。SSE の実体は永続 Redis Stream (MAXLEN 保持) であり、\n // 配備をまたいで**旧形式のエントリが残る**。共有リーダーはプロセス起動時に先頭から\n // 再生し、再接続クライアントの catch-up も同じエントリを読み直すため、\n // このキーを必須にすると添付機能を入れる前に publish された全エントリが\n // fail-closed で捨てられ、再接続タブへ更新が永久に届かなくなる。\n // 送信側の詰め忘れ検知は types.ts の必須フィールドと `_AssertDetailCompat`\n // (出力型は既定値適用後なので必須のまま) が担っており、ここを緩めても失われない。\n attachments: z.array(dailyReportAttachmentItemSchema).default([]),\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 recipientRawUserId: z.number().optional(),\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\n\n/**\n * Tap-scroll circle tuning shared by every daily-report list.\n * 日報のすべての一覧が共有するタップスクロールサークルの調整値。\n *\n * ❗ **List タブと DetailList タブで同じ値を使うこと。** 片方だけ独自定義にすると、\n * 同じ指の位置でタブごとに速度が何十倍も違う (実測で最大 77 倍) という事故になる。\n *\n * 値の根拠 (ビューポート 800px で実測):\n *\n * | 引き距離 | 速度 | List (行 160px) |\n * | --- | --- | --- |\n * | 0 | 160 px/s | 1 行/秒 |\n * | 1/4 | 525 px/s | 3.3 行/秒 |\n * | 1/2 | 1,298 px/s | 8 行/秒 |\n * | 最大 | 6,400 px/s | 40 行/秒 (0.67 行/フレーム) |\n *\n * - `exponentialSteepness: 3` — 引き幅の前半をゆっくりにして微速の分解能を確保する。\n * 大きくしすぎると低速域が最小速度へ張り付いて**引いても動かない死角**になる\n * (旧値 7.5 では引き幅の 43.85% が死角だった)。\n * - `exponentialScale: 8` — 最高速の上限 (ビューポート比)。`maxSpeedMultiplier` と揃えて\n * 上限がどちらで決まるかを一意にする。1 フレームあたりの移動をオーバースキャン\n * (`VIRTUAL_SCROLL_OVERSCAN_COUNT`) の行数より十分小さく保つ値にすること。\n * - `easedOffset` は使わない。最小速度 160 px/s で十分に遅く、下駄を履かせると\n * 低速域の分解能がその分だけ失われる。\n *\n * 長距離の移動はタップスクロールではなくスクロールバーのつまみ (と\n * `BusinessDayThumbOverlay` の日付表示) が担う。ここを速くして解こうとしない。\n */\nexport const TAP_SCROLL_CIRCLE_OPTIONS: ScrollBarTapCircleOptions = {\n enabled: true,\n renderVisual: tapScrollCircleSampleVisual,\n maxSpeedCurve: { exponentialSteepness: 3, exponentialScale: 8 },\n maxSpeedMultiplier: 8,\n}\n\n/**\n * Pointer types allowed to drag-scroll the list content.\n * 一覧のコンテンツ領域をドラッグしてスクロールできるポインタ種別。\n *\n * ❗ **`enablePointerDrag` を丸ごと切ってはならない。** `ScrollPane` はコンテンツを\n * transform で動かしておりネイティブのスクロール領域を持たない\n * (`.aqvs-scroll-pane-content` は `overflow: hidden`) ため、切るとタッチ端末から\n * スクロール手段が消える (一度そうしてしまい、モバイルで一切スクロールできなくなった)。\n *\n * マウスのドラッグだけを外す。行のタップ判定 (`useTapClick`) ・テキスト選択と競合するため。\n * 画面幅のメディアクエリで切り替える案は採らなかった。画面幅は入力手段を表さないので\n * 「主入力がマウスのタッチ対応ノート」のような端末を取りこぼす。判定は**イベントの\n * ポインタ種別**で行うため、同一端末でマウスと指を使い分けても常に正しく振る舞う。\n */\nexport const CONTENT_DRAG_POINTER_INPUTS = [\"pen\", \"touch\"] as const\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 { resolveSourceTypeConfig, 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 { useIsMobileLayout } from \"../hooks/use-responsive-layout\"\nimport { cn } from \"../ui/cn\"\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\"\nimport {\n CONTENT_DRAG_POINTER_INPUTS,\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 { DailyReportAttachmentIndicator } from \"./daily-report-attachment-indicator\"\nimport { DailyReportAttachmentList } from \"./daily-report-attachment-list\"\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 initialScrollOffset?: number\n onScrollOffsetChange?: (offset: number) => void\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, initialScrollOffset, onScrollOffsetChange }: 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 = useIsMobileLayout()\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 (Wheel, Resize).\n * ホイール・リサイズのグローバルイベントリスナーを管理する副作用。\n *\n * Purpose: To keep the viewport height in sync and to detect wheel scrolling.\n * (レスポンシブ判定は use-responsive-layout のフックが担当する。)\n * Dependencies: [updateViewportHeight]\n * Cleanup: Removes all event listeners and clears timers.\n */\n useEffect(() => {\n if (typeof window === \"undefined\") return\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 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('[data-testid=\"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 if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n\n const container = scrollContainerRef.current\n if (!container) return\n\n if (container.dataset.scrolling !== \"true\") {\n container.dataset.scrolling = \"true\"\n }\n\n if (scrollEndTimerRef.current) {\n clearTimeout(scrollEndTimerRef.current)\n }\n\n scrollEndTimerRef.current = setTimeout(() => {\n if (container) {\n delete container.dataset.scrolling\n }\n scrollEndTimerRef.current = null\n }, 150)\n }, [onScrollOffsetChange])\n\n useEffect(() => {\n return () => {\n if (virtualScrollRef.current && onScrollOffsetChange) {\n onScrollOffsetChange(virtualScrollRef.current.getScrollPosition())\n }\n }\n }, [onScrollOffsetChange])\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 initialScrollOffset={initialScrollOffset}\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 // ドラッグスクロールは無効化せず、**ポインタ種別で絞る**。\n // 丸ごと切るとタッチ端末からスクロール手段が消える (定数の注記参照)\n pointerDragInputs: CONTENT_DRAG_POINTER_INPUTS,\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 testId=\"daily-report-list\"\n className=\"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 data-skeleton 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 data-skeleton 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\n data-testid=\"daily-report-side-pane\"\n data-report-id={selectedItem?.reportHubId}\n className=\"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\"\n style={heightStyle}>\n {content}\n </aside>\n )\n }\n // If rendered inside mobile overlay\n return (\n <div data-testid=\"daily-report-side-pane\" data-report-id={selectedItem?.reportHubId} className=\"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 selectedItem,\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, sourceTypeConfigs } = 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 const sourceType = selectedItem.sourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\n const isExternalSource = sourceType ? sourceType.toLowerCase() !== \"internal\" : false\n const canComment = sourceConfig?.allowComment ?? !isExternalSource\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 data-skeleton 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 {/* 添付節。DetailList タブと同じ位置 (面談者・本文の後、コメントの前) に置く。\n 片方のタブにしか出さないと、利用者は「添付が無い日報」と誤認する */}\n {report && <DailyReportAttachmentList attachments={report.attachments} />}\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 {canComment && <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 { fieldLabels, sourceTypeConfigs } = useDailyReportConfig()\n const { toggleStar, toggleRead, removeStaleItem } = useDailyReportActionContext()\n const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)\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 ?? null).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-[[data-scrolling]]:transform-none cursor-pointer select-none flex-col rounded-2xl border in-[[data-scrolling]]:border-slate-200 bg-white p-4 text-left in-[[data-scrolling]]:shadow-sm shadow-sm in-[[data-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 仕様書が定める [sourceType] [Star] [Read] の相対順は崩さず、その手前に置く */}\n <DailyReportAttachmentIndicator count={report.attachments.length} label={fieldLabels.attachments} />\n {(() => {\n const sourceType = item.sourceType ?? report?.sourceType\n const sourceConfig = resolveSourceTypeConfig(sourceType, sourceTypeConfigs)\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","/**\n * Responsive layout detection shared by the daily-report views.\n * 日報ビューが共有するレスポンシブ判定。\n *\n * 閾値を 1 箇所に集約する。各ビューが独自に `matchMedia` を持つと、片方だけ変更されて\n * レイアウト分岐が食い違う。\n *\n * ❗ **スクロール操作の可否をここで決めてはならない。** 画面幅で判定すると\n * 「主入力がマウスのタッチ対応ノート」のような端末を取りこぼす。ドラッグスクロールは\n * `ScrollPane` の `pointerDragInputs` で**イベントのポインタ種別ごとに**制御する\n * (`CONTENT_DRAG_POINTER_INPUTS` 参照)。\n */\nimport { useEffect, useState } from \"react\"\n\n/**\n * Viewport width at which the daily-report views switch to the mobile layout.\n * 日報ビューがモバイルレイアウトへ切り替わるビューポート幅。\n *\n * Tailwind の `sm` (640px) の直下に合わせてある。リスト + サイドペインの 2 カラムを\n * 畳んでオーバーレイ表示にする閾値であり、**この値は 1 箇所でだけ定義する**。\n */\nexport const MOBILE_LAYOUT_MEDIA_QUERY = \"(max-width: 639px)\"\n\n/**\n * Reports whether the viewport is narrow enough for the mobile layout.\n * ビューポートがモバイルレイアウトの幅かどうかを返す処理。\n *\n * SSR と初回描画では `false` を返し、マウント後に実環境で更新する\n * (サーバーとクライアントで初期値を揃えてハイドレーション不一致を避ける)。\n *\n * @returns True on the mobile layout. モバイルレイアウトなら true。\n */\nexport const useIsMobileLayout = (): boolean => {\n const [isMobileLayout, setIsMobileLayout] = useState(false)\n\n /**\n * Tracks the viewport width.\n * ビューポート幅を追跡する副作用。\n *\n * 目的: 画面回転・ウィンドウリサイズに追随する。\n * 依存関係: [] - 監視対象は固定の定数なので購読は 1 度だけ張る。\n * クリーンアップ: リスナーを解除する。\n */\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return\n\n const query = window.matchMedia(MOBILE_LAYOUT_MEDIA_QUERY)\n const sync = () => setIsMobileLayout(query.matches)\n\n sync()\n query.addEventListener(\"change\", sync)\n return () => query.removeEventListener(\"change\", sync)\n }, [])\n\n return isMobileLayout\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, RefreshCw, RotateCw } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useCallback, useEffect, useMemo, useRef, 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, refetchData, clearCacheAndRefetch } = useDailyReportActionContext()\n const [searchParams] = useSearchParams()\n\n // 各タブのスクロール位置をアンマウント後も保持する Ref\n const listScrollOffsetRef = useRef<number>(0)\n const detailListScrollOffsetRef = useRef<number>(0)\n\n const handleListScrollOffsetChange = useCallback((offset: number) => {\n listScrollOffsetRef.current = offset\n }, [])\n\n const handleDetailListScrollOffsetChange = useCallback((offset: number) => {\n detailListScrollOffsetRef.current = offset\n }, [])\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 handleReload = async () => {\n await refetchData()\n }\n\n const handleReloadClearCache = async () => {\n await clearCacheAndRefetch()\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: (\n <DailyReportList\n dailyReportItems={dailyReportItems}\n autoMarkRead={autoMarkRead}\n selectedReportHubId={selectedItemId}\n onSelectItem={setSelectedItemId}\n userId={userId}\n initialScrollOffset={listScrollOffsetRef.current}\n onScrollOffsetChange={handleListScrollOffsetChange}\n />\n ),\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: (\n <DailyReportDetailList dailyReportItems={dailyReportItems} userId={userId} selectedItemId={selectedItemId} onSelectItem={setSelectedItemId} initialScrollOffset={detailListScrollOffsetRef.current} onScrollOffsetChange={handleDetailListScrollOffsetChange} />\n ),\n },\n ],\n [dailyReportItems, autoMarkRead, selectedItemId, userId, handleListScrollOffsetChange, handleDetailListScrollOffsetChange],\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.5 rounded-md border border-amber-300 bg-amber-50 px-2 py-0.5 text-xs sm:flex dark:border-amber-700/50 dark:bg-amber-950/40\">\n <span className=\"rounded bg-amber-200 px-1 py-0.2 font-bold font-mono text-[10px] text-amber-900 dark:bg-amber-800 dark:text-amber-100\" title=\"開発環境限定のコントロールです\">\n DEV\n </span>\n <div className=\"flex items-center space-x-1\">\n <Switch id=\"sse-mode\" checked={isSseEnabled} onCheckedChange={toggleSse} className=\"scale-75\" />\n <Label htmlFor=\"sse-mode\" className=\"cursor-pointer text-slate-600 text-xs dark:text-slate-300\" title=\"開発環境のみ: Real-time SSE 購読切り替え\">\n 購読\n </Label>\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={handleReload} className=\"h-6 px-1.5 text-slate-700 text-xs hover:bg-amber-200 dark:text-slate-200 dark:hover:bg-amber-800\" title=\"開発環境のみ: ページリロードなしでデータのみ再取得\">\n <RotateCw className=\"mr-1 h-3 w-3\" />\n リロード\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleReloadClearCache}\n className=\"h-6 px-1.5 text-amber-800 text-xs hover:bg-amber-200 dark:text-amber-300 dark:hover:bg-amber-800\"\n title=\"開発環境のみ: サーバー/DB & クライアントのキャッシュを破棄してデータ再取得\">\n <RefreshCw className=\"mr-1 h-3 w-3\" />\n キャッシュクリア\n </Button>\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, forceRefresh = false): Promise<DailyReportItem[]> => {\n const url = new URL(`${apiBasePath}/ids`, typeof window !== \"undefined\" ? window.location.origin : \"http://localhost\")\n if (forceRefresh) {\n url.searchParams.set(\"forceRefresh\", \"true\")\n url.searchParams.set(\"_t\", Date.now().toString())\n }\n return fetch(url.toString(), {\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/**\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","CONTENT_DRAG_POINTER_INPUTS","DAILY_REPORT_FETCH_DEBOUNCE_MS","DEFAULT_ITEM_HEIGHT","DailyReportActionProvider","DailyReportAttachmentIndicator","DailyReportAttachmentList","DailyReportCache","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","clearDailyReportCache","createDailyReportClientLoader","dailyReportShouldRevalidate","defaultDailyReportClientConfig","defaultDailyReportFieldLabels","deleteDailyReportCache","fetchDailyReportIds","getCachedReport","registerRecentDeletion","releaseMutationLock","resolveSourceTypeConfig","subscribeCacheReady","updateDailyReportCache","useDailyReportActionContext","useDailyReportComments","useDailyReportConfig","useDailyReportDetail","useDailyReportPrefetch","useDailyReportSseConnection","useDynamicViewportHeight","writeCache","__toCommonJS","import_jsx_runtime","BusinessDayThumbOverlay","orientation","thumbCenter","label","isDragging","isTapScrollActive","isWheelScrollActive","import_lucide_react","import_jsx_runtime","COUNT_VISIBLE_FROM","DailyReportAttachmentIndicator","count","label","import_react","import_jsx_runtime","defaultDailyReportFieldLabels","defaultDailyReportClientConfig","title","annotation","DailyReportConfigContext","DailyReportConfigProvider","config","children","merged","useDailyReportConfig","resolveSourceTypeConfig","sourceType","sourceTypeConfigs","lower","matchedKey","k","import_jsx_runtime","BYTES_PER_UNIT","SIZE_UNITS","formatFileSize","bytes","value","unitIndex","DailyReportAttachmentList","attachments","apiBasePath","fieldLabels","useDailyReportConfig","attachment","href","size","isMissing","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_react_router","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","listener","ex","l","ignoreExpiry","e","dateKey","report","expiresAt","syncList","normalizeBusinessDateKey","listEntry","data","r","updates","strict","date","list","serverReport","cachedReport","c","serverIds","missing","a","b","reports","fetchStart","t","processed","lastMut","cached","merged","writeCache","exp","sync","getCachedReport","opts","updateDailyReportCache","u","deleteDailyReportCache","clearDailyReportCache","acquireMutationLock","releaseMutationLock","registerRecentDeletion","cId","subscribeCacheReady","reportHubId","callback","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","dailyReportAttachmentItemSchema","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","revalidator","refetchData","v","DailyReportCache","listener","clearCacheAndRefetch","clearDailyReportCache","formData","res","errData","e","pendingSseQueue","handleSseMessageRef","enqueuePendingSseMessage","reportHubId","message","queue","existing","entry","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","commentId","statusType","value","pendingEntry","deleteDailyReportCache","report","useDailyReportSseConnection","executeAction","intent","params","callbacks","options","clientTempId","k","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","CONTENT_DRAG_POINTER_INPUTS","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","initialScrollOffset","onScrollOffsetChange","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","WHEEL_RESET_TIMEOUT_MS","updateItemHeight","height","prev","newMap","getItemHeight","getItemKey","handleCommentAdded","renderItem","DailyReportItemContainer","VIRTUAL_SCROLL_OVERSCAN_COUNT","SCROLL_BAR_WIDTH","TAP_SCROLL_CIRCLE_OPTIONS","CONTENT_DRAG_POINTER_INPUTS","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","itemSourceType","visitTimeDisplay","categoryLabel","fieldLabels","sourceTypeConfigs","DailyReportAttachmentIndicator","sourceType","sourceConfig","resolveSourceTypeConfig","cn","UnreadIndicator","fallbackText","MetadataPanel","onDoubleClick","formContainerRef","toggleStar","toggleRead","pendingStarUpdates","pendingReadUpdates","deleteReport","user","uiComments","handleAddComment","handleDeleteComment","pendingDeleteId","setPendingDeleteId","resolvedIdMap","useDailyReportComments","isExternalSource","canComment","handleAddCommentWrapper","content","handleToggleRead","handleToggleStar","handleDelete","normalizedContent","from","to","labels","DailyReportAttachmentList","DailyReportCommentList","DailyReportCommentForm","import_swipe_overlay","import_virtualscroll","import_lucide_react","import_react","import_react","MOBILE_LAYOUT_MEDIA_QUERY","useIsMobileLayout","isMobileLayout","setIsMobileLayout","query","sync","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","initialScrollOffset","onScrollOffsetChange","scrollContainerRef","virtualScrollRef","viewportHeight","updateViewportHeight","useDynamicViewportHeight","overlayLabel","setOverlayLabel","isWheelScrollActive","setIsWheelScrollActive","useIsMobileLayout","wheelResetTimerRef","appliedInitialSignatureRef","itemCount","scrollEndTimerRef","columnHeightStyle","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","CONTENT_DRAG_POINTER_INPUTS","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","resolveSourceTypeConfig","isExternalSource","canComment","prevReportHubIdRef","prevCommentsLengthRef","currentReportHubId","handleAddCommentWrapper","displayContent","handleToggleStar","handleDelete","showSkeleton","isStarred","isRead","hasComments","SkeletonText","className","UnreadIndicator","DailyReportAttachmentList","DailyReportCommentList","DailyReportCommentForm","isOpen","onClose","containerRef","closeButtonRef","isGuardBlocked","requestBlock","resetGuard","displayData","displayItem","EMPTY_DETAIL","overlayRef","overlayStyle","attemptClose","isSwiping","tapHandlers","extractPreviewLines","MAX_PREVIEW_LINES","onSelect","removeStaleItem","isRefetching","hasEverFetched","previewLines","line","creator","date","DailyReportAttachmentIndicator","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","refetchData","clearCacheAndRefetch","useDailyReportActionContext","searchParams","listScrollOffsetRef","detailListScrollOffsetRef","handleListScrollOffsetChange","offset","handleDetailListScrollOffsetChange","selectedItemId","setSelectedItemId","handleReload","handleReloadClearCache","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","forceRefresh","url","response","data","createDailyReportClientLoader","options","clientLoader","args","idsPromise"]}
|