@aiquants/daily-report 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -21
- package/dist/client.d.mts +3 -10
- package/dist/client.d.ts +3 -10
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +4 -4
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +1 -6
- package/dist/server.d.ts +1 -6
- package/dist/server.js +4 -4
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +4 -4
- package/dist/server.mjs.map +1 -1
- package/dist/{sse-schema-DO9Qe8XF.d.ts → sse-schema-CK7cUnEo.d.ts} +1 -34
- package/dist/{sse-schema-DKUjXMW3.d.mts → sse-schema-yl5AaSsj.d.mts} +1 -34
- package/dist/{types-DrjeK3_G.d.mts → types-CVhwLhSN.d.mts} +0 -1
- package/dist/{types-DrjeK3_G.d.ts → types-CVhwLhSN.d.ts} +0 -1
- package/package.json +8 -8
- package/src/client/components/daily-report-detail-list.tsx +10 -20
- package/src/client/components/daily-report-list.tsx +1 -13
- package/src/client/config-context.tsx +0 -9
- package/src/client/contexts/daily-report-action-context.tsx +0 -1
- package/src/server/cache.ts +0 -18
- package/src/server/service.ts +0 -2
- package/src/shared/sse-schema.ts +0 -1
- package/src/shared/types.ts +0 -1
- package/src/server/cache.spec.ts +0 -47
|
@@ -13,7 +13,6 @@ import { useDailyReportActionContext } from "../contexts/daily-report-action-con
|
|
|
13
13
|
import { useDailyReportDetail } from "../hooks/use-daily-report"
|
|
14
14
|
import { useDailyReportComments } from "../hooks/use-daily-report-comments"
|
|
15
15
|
import { useDynamicViewportHeight } from "../hooks/use-dynamic-viewport-height"
|
|
16
|
-
import { cn } from "../ui/cn"
|
|
17
16
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"
|
|
18
17
|
import {
|
|
19
18
|
DEFAULT_ITEM_HEIGHT,
|
|
@@ -505,7 +504,7 @@ const DailyReportSidePane = ({
|
|
|
505
504
|
}) => {
|
|
506
505
|
const [activeTab, setActiveTab] = useState<DailyReportSidePaneTab>("article")
|
|
507
506
|
const [isEditing, setIsEditing] = useState(false)
|
|
508
|
-
const { draftLabelName, fieldLabels
|
|
507
|
+
const { draftLabelName, fieldLabels } = useDailyReportConfig()
|
|
509
508
|
const { report, error, isLoading } = useDailyReportDetail(selectedItem?.reportHubId ?? -1, selectedItem?.businessDate ?? null)
|
|
510
509
|
|
|
511
510
|
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
|
@@ -571,11 +570,6 @@ const DailyReportSidePane = ({
|
|
|
571
570
|
<div className="flex min-h-4 items-center gap-1 text-slate-500 text-xs dark:text-slate-300">
|
|
572
571
|
<span>{fieldLabels.author}:</span>
|
|
573
572
|
{showHeaderSkeleton ? <div className="h-3 w-24 animate-pulse rounded bg-slate-200 dark:bg-slate-700" /> : <span>{headerCreator}</span>}
|
|
574
|
-
{(() => {
|
|
575
|
-
const sourceType = report?.sourceType
|
|
576
|
-
const sourceConfig = sourceType ? sourceTypeConfigs?.[sourceType] : undefined
|
|
577
|
-
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
|
|
578
|
-
})()}
|
|
579
573
|
</div>
|
|
580
574
|
</div>
|
|
581
575
|
<Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as DailyReportSidePaneTab)} className="flex min-h-0 flex-1 flex-col">
|
|
@@ -910,7 +904,6 @@ const extractPreviewLines = (content: string | null): string[] => {
|
|
|
910
904
|
const ListReportItem = ({ item, isActive, onSelect }: { item: DailyReportItem; isActive: boolean; onSelect: (id: number) => void }) => {
|
|
911
905
|
const { report, error, isLoading, isRefetching } = useDailyReportDetail(item.reportHubId, item.businessDate)
|
|
912
906
|
const { toggleStar, toggleRead, removeStaleItem } = useDailyReportActionContext()
|
|
913
|
-
const { sourceTypeConfigs } = useDailyReportConfig()
|
|
914
907
|
|
|
915
908
|
// フェッチが一度でも試行されたかを追跡する ref
|
|
916
909
|
const hasEverFetched = useRef(false)
|
|
@@ -996,11 +989,6 @@ const ListReportItem = ({ item, isActive, onSelect }: { item: DailyReportItem; i
|
|
|
996
989
|
<span className="truncate font-medium text-slate-900 dark:text-slate-100">{creator}</span>
|
|
997
990
|
</span>
|
|
998
991
|
<div className="ml-auto flex items-center gap-1.5">
|
|
999
|
-
{(() => {
|
|
1000
|
-
const sourceType = report?.sourceType
|
|
1001
|
-
const sourceConfig = sourceType ? sourceTypeConfigs?.[sourceType] : undefined
|
|
1002
|
-
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
|
|
1003
|
-
})()}
|
|
1004
992
|
<button
|
|
1005
993
|
type="button"
|
|
1006
994
|
title={report.isStarred ? "スターを外す" : "スターを付ける"}
|
|
@@ -38,11 +38,6 @@ export type DailyReportFieldLabels = {
|
|
|
38
38
|
relationsEmpty: string
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export type SourceTypeConfig = {
|
|
42
|
-
label: string
|
|
43
|
-
badgeClassName?: string
|
|
44
|
-
}
|
|
45
|
-
|
|
46
41
|
/** クライアント層の設定一式。 */
|
|
47
42
|
export type DailyReportClientConfig = {
|
|
48
43
|
/** 画面タイトル (ヘッダーへ渡す)。 */
|
|
@@ -55,8 +50,6 @@ export type DailyReportClientConfig = {
|
|
|
55
50
|
draftLabelName: string
|
|
56
51
|
/** 詳細/一覧カードのフィールド見出しラベル。既定は中立英語。アプリが自ドメインの表記を注入する。 */
|
|
57
52
|
fieldLabels: DailyReportFieldLabels
|
|
58
|
-
/** 外部ソース区分ごとのバッジ表示設定。 */
|
|
59
|
-
sourceTypeConfigs?: Record<string, SourceTypeConfig>
|
|
60
53
|
/** ヘッダー描画スロット。アプリのヘッダーコンポーネントを差し込む。 */
|
|
61
54
|
renderHeader: (props: DailyReportHeaderProps) => ReactNode
|
|
62
55
|
/** 開発向けコントロール (SSE 購読トグル等) を表示するか。 */
|
|
@@ -69,7 +62,6 @@ export type DailyReportClientConfig = {
|
|
|
69
62
|
*/
|
|
70
63
|
export type DailyReportClientConfigInput = Partial<Omit<DailyReportClientConfig, "fieldLabels">> & {
|
|
71
64
|
fieldLabels?: Partial<DailyReportFieldLabels>
|
|
72
|
-
sourceTypeConfigs?: Record<string, SourceTypeConfig>
|
|
73
65
|
}
|
|
74
66
|
|
|
75
67
|
/** 中立英語のフィールドラベル既定 (ドメイン非依存)。 */
|
|
@@ -102,7 +94,6 @@ export const defaultDailyReportClientConfig: DailyReportClientConfig = {
|
|
|
102
94
|
// 中立プレースホルダー。実運用のラベル名 (アプリ固有) は Provider / config prop で上書きする。
|
|
103
95
|
draftLabelName: "draft",
|
|
104
96
|
fieldLabels: defaultDailyReportFieldLabels,
|
|
105
|
-
sourceTypeConfigs: {},
|
|
106
97
|
renderHeader: ({ title, annotation }) => (
|
|
107
98
|
<header className="flex flex-wrap items-baseline justify-between gap-2 px-2 py-1">
|
|
108
99
|
<span className="text-nowrap text-slate-700 text-sm dark:text-slate-200">{title}</span>
|
|
@@ -703,7 +703,6 @@ export const DailyReportActionProvider = ({ children, user, initialItems = [], u
|
|
|
703
703
|
createdAt: now,
|
|
704
704
|
author: getUserDisplayName(user),
|
|
705
705
|
userId: user?.id ?? "unknown",
|
|
706
|
-
sourceType: "Internal",
|
|
707
706
|
employeeName: getUserDisplayName(user),
|
|
708
707
|
updatedBy: getUserDisplayName(user),
|
|
709
708
|
updatedAt: now,
|
package/src/server/cache.ts
CHANGED
|
@@ -123,10 +123,6 @@ export class SqlResultCache {
|
|
|
123
123
|
// キャッシュ保存時に現在の epoch を記録
|
|
124
124
|
const epoch = opts.epochKey ? await this.epochStore.getEpoch(opts.epochKey) : 0
|
|
125
125
|
this.buckets.set(cacheKey, { records, expireAt, epoch })
|
|
126
|
-
// 蓄積防止のため、サイズが一定値を超えたら期限切れキャッシュを一括クリーンアップ
|
|
127
|
-
if (this.buckets.size > 500) {
|
|
128
|
-
this.cleanExpired()
|
|
129
|
-
}
|
|
130
126
|
}
|
|
131
127
|
return records
|
|
132
128
|
})()
|
|
@@ -166,18 +162,4 @@ export class SqlResultCache {
|
|
|
166
162
|
}
|
|
167
163
|
}
|
|
168
164
|
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Cleans up all expired cache buckets to prevent memory accumulation.
|
|
172
|
-
* メモリー蓄積を防ぐため、期限切れのキャッシュバケットをすべてクリーンアップする処理。
|
|
173
|
-
*/
|
|
174
|
-
private cleanExpired(): void {
|
|
175
|
-
const now = Date.now()
|
|
176
|
-
// 期限切れのキーを削除
|
|
177
|
-
for (const [key, bucket] of this.buckets.entries()) {
|
|
178
|
-
if (bucket.expireAt <= now) {
|
|
179
|
-
this.buckets.delete(key)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
165
|
}
|
package/src/server/service.ts
CHANGED
|
@@ -326,7 +326,6 @@ export function createDailyReportService(config: DailyReportServiceConfig) {
|
|
|
326
326
|
date: formatDateValue(hub.businessDate, "YYYY-MM-DD"),
|
|
327
327
|
author: row.creatorName ?? maskAuditActor(hub.createdBy) ?? "",
|
|
328
328
|
userId: hub.userId ? encodeUserId(hub.userId) : "",
|
|
329
|
-
sourceType: hub.sourceType ?? "Internal",
|
|
330
329
|
createdAt: formatDateValue(hub.createdAt, "YYYY-MM-DD HH:mm:ss"),
|
|
331
330
|
updatedAt: formatDateValue(hub.updatedAt, "YYYY-MM-DD HH:mm:ss"),
|
|
332
331
|
updatedBy: maskAuditActor(hub.updatedBy),
|
|
@@ -1035,7 +1034,6 @@ export function createDailyReportService(config: DailyReportServiceConfig) {
|
|
|
1035
1034
|
createdAt: formatDateValue(hub.createdAt, "YYYY-MM-DD HH:mm:ss"),
|
|
1036
1035
|
author: userName,
|
|
1037
1036
|
userId: encodeUserId(userId),
|
|
1038
|
-
sourceType: "Internal",
|
|
1039
1037
|
employeeName: userName,
|
|
1040
1038
|
// 監査列と同様、クライアント公開時は生内部 ID を難読化する (mapHubRecord と整合)
|
|
1041
1039
|
updatedBy: encodeUserId(userId),
|
package/src/shared/sse-schema.ts
CHANGED
|
@@ -39,7 +39,6 @@ export const dailyReportDetailSchema = z.object({
|
|
|
39
39
|
createdAt: z.string().nullable(),
|
|
40
40
|
author: z.string(),
|
|
41
41
|
userId: z.string(),
|
|
42
|
-
sourceType: z.string().nullable(),
|
|
43
42
|
employeeName: z.string().nullable(),
|
|
44
43
|
updatedBy: z.string().nullable(),
|
|
45
44
|
updatedAt: z.string().nullable(),
|
package/src/shared/types.ts
CHANGED
package/src/server/cache.spec.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest"
|
|
2
|
-
import { createEpochStore, SqlResultCache } from "./cache"
|
|
3
|
-
|
|
4
|
-
describe("SqlResultCache Memory Cleanup", () => {
|
|
5
|
-
it("should clean up expired cache entries when map size exceeds 500", async () => {
|
|
6
|
-
// Mock epoch store
|
|
7
|
-
const epochStore = createEpochStore(undefined)
|
|
8
|
-
vi.spyOn(epochStore, "getEpoch").mockResolvedValue(0)
|
|
9
|
-
|
|
10
|
-
const cache = new SqlResultCache({ defaultTtlMs: 1000 }, epochStore)
|
|
11
|
-
|
|
12
|
-
// 1. 期限切れのキャッシュを 500 件作成 (expireAt を過去に設定)
|
|
13
|
-
// 直接プライベートフィールド buckets に流し込むことでモック状態を作る
|
|
14
|
-
const buckets = (cache as any).buckets
|
|
15
|
-
for (let i = 0; i < 505; i++) {
|
|
16
|
-
buckets.set(`expired-key-${i}`, {
|
|
17
|
-
records: [i],
|
|
18
|
-
expireAt: Date.now() - 1000, // 過去時間
|
|
19
|
-
epoch: 0,
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// 2. 有効なキャッシュを 10 件作成 (expireAt を未来に設定)
|
|
24
|
-
for (let i = 0; i < 10; i++) {
|
|
25
|
-
buckets.set(`valid-key-${i}`, {
|
|
26
|
-
records: [i],
|
|
27
|
-
expireAt: Date.now() + 100000, // 未来時間
|
|
28
|
-
epoch: 0,
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
expect(buckets.size).toBe(515)
|
|
33
|
-
|
|
34
|
-
// 3. getOrFetch を実行して 500 件超えの閾値トリガーを引く
|
|
35
|
-
const fetcher = vi.fn().mockResolvedValue(["new-value"])
|
|
36
|
-
await cache.getOrFetch({
|
|
37
|
-
cacheKey: "trigger-key",
|
|
38
|
-
fetcher,
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
// 4. 検証: 期限切れの 505 件のデータが cleanExpired で削除され、有効な 10 件 + 新規の 1 件のみ残っていること
|
|
42
|
-
expect(buckets.size).toBe(11) // 10 (valid) + 1 (trigger-key)
|
|
43
|
-
expect(buckets.has("trigger-key")).toBe(true)
|
|
44
|
-
expect(buckets.has("valid-key-0")).toBe(true)
|
|
45
|
-
expect(buckets.has("expired-key-0")).toBe(false)
|
|
46
|
-
})
|
|
47
|
-
})
|