@squadbase/vantage 0.6.1 → 0.6.2-dev1.0017a4d

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.
Files changed (319) hide show
  1. package/dist/add-4GHF2TX2.js +6 -0
  2. package/dist/add-4GHF2TX2.js.map +1 -0
  3. package/dist/{build-O5TVNH4E.js → build-X5NVCQKT.js} +9 -9
  4. package/dist/{build-O5TVNH4E.js.map → build-X5NVCQKT.js.map} +1 -1
  5. package/dist/{check-WFBEDHDR.js → check-ZBIH422T.js} +7 -7
  6. package/dist/{check-WFBEDHDR.js.map → check-ZBIH422T.js.map} +1 -1
  7. package/dist/chunk-2IAPIOPS.js +152 -0
  8. package/dist/chunk-2IAPIOPS.js.map +1 -0
  9. package/dist/chunk-2ICNHUG6.js +1820 -0
  10. package/dist/chunk-2ICNHUG6.js.map +1 -0
  11. package/dist/{chunk-5ZOP4SQR.js → chunk-2N72FKLB.js} +12 -7
  12. package/dist/chunk-2N72FKLB.js.map +1 -0
  13. package/dist/chunk-57GHR5LN.js +163 -0
  14. package/dist/chunk-57GHR5LN.js.map +1 -0
  15. package/dist/{chunk-BCE22JSM.js → chunk-7DZGJQ3P.js} +33 -14
  16. package/dist/chunk-7DZGJQ3P.js.map +1 -0
  17. package/dist/{chunk-MXI3PZFT.js → chunk-7S52M2SL.js} +257 -210
  18. package/dist/chunk-7S52M2SL.js.map +1 -0
  19. package/dist/{chunk-QDZA5JSL.js → chunk-AABMKHEU.js} +4 -4
  20. package/dist/{chunk-QDZA5JSL.js.map → chunk-AABMKHEU.js.map} +1 -1
  21. package/dist/{chunk-KLEIVDUV.js → chunk-CEXAQQ5W.js} +150 -11
  22. package/dist/chunk-CEXAQQ5W.js.map +1 -0
  23. package/dist/{chunk-3G43C6OL.js → chunk-GNXCX4WE.js} +46 -3
  24. package/dist/chunk-GNXCX4WE.js.map +1 -0
  25. package/dist/{chunk-JGRMRXXZ.js → chunk-JKLLFVEU.js} +3 -3
  26. package/dist/{chunk-JGRMRXXZ.js.map → chunk-JKLLFVEU.js.map} +1 -1
  27. package/dist/chunk-JXFCSF3K.js +673 -0
  28. package/dist/chunk-JXFCSF3K.js.map +1 -0
  29. package/dist/chunk-PDOBHMUQ.js +54 -0
  30. package/dist/chunk-PDOBHMUQ.js.map +1 -0
  31. package/dist/{chunk-ZVANIULZ.js → chunk-RNTDBX6S.js} +4 -4
  32. package/dist/{chunk-ZVANIULZ.js.map → chunk-RNTDBX6S.js.map} +1 -1
  33. package/dist/chunk-RU6WKEOG.js +7048 -0
  34. package/dist/chunk-RU6WKEOG.js.map +1 -0
  35. package/dist/{chunk-A2UUGASH.js → chunk-U736PVHY.js} +2 -2
  36. package/dist/chunk-U736PVHY.js.map +1 -0
  37. package/dist/{chunk-PVTHHV7X.js → chunk-V3LF7BE7.js} +27 -5
  38. package/dist/chunk-V3LF7BE7.js.map +1 -0
  39. package/dist/{chunk-EP6JHXSR.js → chunk-WAMZWZJB.js} +54 -9
  40. package/dist/chunk-WAMZWZJB.js.map +1 -0
  41. package/dist/{chunk-DTDVSFRY.js → chunk-XQUSGIDG.js} +17 -3
  42. package/dist/chunk-XQUSGIDG.js.map +1 -0
  43. package/dist/cli.js +15 -11
  44. package/dist/cli.js.map +1 -1
  45. package/dist/client/index.d.ts +32 -3
  46. package/dist/client/index.js +205 -12
  47. package/dist/client/index.js.map +1 -1
  48. package/dist/components/index.d.ts +1743 -29
  49. package/dist/components/index.js +7 -3
  50. package/dist/{dev-NZRLOQCL.js → dev-4XEDTELV.js} +24 -13
  51. package/dist/dev-4XEDTELV.js.map +1 -0
  52. package/dist/doc/index.d.ts +198 -2
  53. package/dist/doc/index.js +438 -11
  54. package/dist/doc/index.js.map +1 -1
  55. package/dist/{docs-ZGDJUHJI.js → docs-JVP4RPKT.js} +3 -3
  56. package/dist/{docs-ZGDJUHJI.js.map → docs-JVP4RPKT.js.map} +1 -1
  57. package/dist/{doctor-VFKEMR54.js → doctor-5E2VEZPK.js} +8 -8
  58. package/dist/doctor-5E2VEZPK.js.map +1 -0
  59. package/dist/format-U0cQl069.d.ts +243 -0
  60. package/dist/markdown/index.js +1 -1
  61. package/dist/markdown/index.js.map +1 -1
  62. package/dist/{preview-QDCIVVSR.js → preview-T6CLSQVX.js} +12 -4
  63. package/dist/preview-T6CLSQVX.js.map +1 -0
  64. package/dist/query/index.d.ts +0 -11
  65. package/dist/query/index.js +2 -2
  66. package/dist/{routes-RHP5FVGD.js → routes-3SGAFDMC.js} +6 -6
  67. package/dist/{routes-RHP5FVGD.js.map → routes-3SGAFDMC.js.map} +1 -1
  68. package/dist/{search-RFBELGY7.js → search-5LC4XPB5.js} +3 -3
  69. package/dist/{search-RFBELGY7.js.map → search-5LC4XPB5.js.map} +1 -1
  70. package/dist/slide/index.d.ts +177 -1
  71. package/dist/slide/index.js +917 -18
  72. package/dist/slide/index.js.map +1 -1
  73. package/dist/table/index.js +14 -9
  74. package/dist/table/index.js.map +1 -1
  75. package/dist/ui/index.d.ts +148 -12
  76. package/dist/ui/index.js +1340 -87
  77. package/dist/ui/index.js.map +1 -1
  78. package/dist/{upgrade-GF2P67M6.js → upgrade-JHXZBGQS.js} +7 -7
  79. package/dist/{upgrade-GF2P67M6.js.map → upgrade-JHXZBGQS.js.map} +1 -1
  80. package/dist/vite/index.d.ts +10 -0
  81. package/dist/vite/index.js +4 -4
  82. package/docs/en/changelog.md +24 -0
  83. package/docs/en/cli-reference.md +30 -0
  84. package/docs/en/components.md +40 -17
  85. package/docs/en/doc/doc-action-items.md +49 -0
  86. package/docs/en/doc/doc-callout.md +38 -0
  87. package/docs/en/doc/doc-data-table.md +60 -0
  88. package/docs/en/doc/doc-executive-summary.md +64 -0
  89. package/docs/en/doc/doc-figure.md +56 -0
  90. package/docs/en/doc/doc-key-findings.md +61 -0
  91. package/docs/en/doc/doc-kpi-scorecard.md +80 -0
  92. package/docs/en/doc/doc-report-header.md +46 -0
  93. package/docs/en/parts/area-chart.md +99 -0
  94. package/docs/en/parts/bar-chart.md +91 -0
  95. package/docs/en/parts/bar-list.md +50 -0
  96. package/docs/en/parts/category-bar.md +37 -0
  97. package/docs/en/parts/chart-frame.md +39 -0
  98. package/docs/en/parts/data-table.md +22 -0
  99. package/docs/en/parts/delta.md +50 -0
  100. package/docs/en/parts/donut-chart.md +72 -0
  101. package/docs/en/parts/format.md +43 -0
  102. package/docs/en/parts/funnel-steps.md +2 -1
  103. package/docs/en/parts/heatmap.md +53 -0
  104. package/docs/en/parts/kpi-card.md +57 -0
  105. package/docs/en/parts/legend.md +35 -0
  106. package/docs/en/parts/line-chart.md +94 -0
  107. package/docs/en/parts/metric-value.md +3 -2
  108. package/docs/en/parts/page-shell.md +1 -1
  109. package/docs/en/parts/progress-circle.md +33 -0
  110. package/docs/en/parts/sample-data.md +39 -0
  111. package/docs/en/parts/scatter-chart.md +56 -0
  112. package/docs/en/parts/sparkline.md +1 -1
  113. package/docs/en/parts/tracker.md +36 -0
  114. package/docs/en/parts/trend-indicator.md +17 -1
  115. package/docs/en/parts/updated-at.md +30 -0
  116. package/docs/en/parts/waterfall-chart.md +56 -0
  117. package/docs/en/routing.md +4 -2
  118. package/docs/en/slide/slide-chart-insight.md +52 -0
  119. package/docs/en/slide/slide-cover.md +39 -0
  120. package/docs/en/slide/slide-exec-summary.md +47 -0
  121. package/docs/en/slide/slide-kpi-grid.md +55 -0
  122. package/docs/en/slide/slide-kpi-hero.md +58 -0
  123. package/docs/en/slide/slide-next-actions.md +49 -0
  124. package/docs/en/slide/slide-scorecard-table.md +54 -0
  125. package/docs/en/ui/alert-dialog.md +42 -0
  126. package/docs/en/ui/avatar.md +31 -0
  127. package/docs/en/ui/combobox.md +74 -0
  128. package/docs/en/ui/drawer.md +41 -0
  129. package/docs/en/ui/empty.md +1 -1
  130. package/docs/en/ui/error-state.md +4 -3
  131. package/docs/en/ui/field.md +42 -0
  132. package/docs/en/ui/hover-card.md +21 -0
  133. package/docs/en/ui/kbd.md +17 -0
  134. package/docs/en/ui/loading.md +1 -1
  135. package/docs/en/ui/pagination.md +36 -0
  136. package/docs/en/ui/radio-group.md +21 -0
  137. package/docs/en/ui/toast.md +44 -0
  138. package/docs/en/ui/toggle-group.md +4 -3
  139. package/docs/en/ui-and-theming.md +1 -1
  140. package/docs/index.json +1024 -114
  141. package/docs/ja/changelog.md +55 -0
  142. package/docs/ja/cli-reference.md +29 -0
  143. package/docs/ja/components.md +66 -101
  144. package/docs/ja/doc/doc-action-items.md +49 -0
  145. package/docs/ja/doc/doc-callout.md +38 -0
  146. package/docs/ja/doc/doc-data-table.md +60 -0
  147. package/docs/ja/doc/doc-executive-summary.md +64 -0
  148. package/docs/ja/doc/doc-figure.md +56 -0
  149. package/docs/ja/doc/doc-key-findings.md +61 -0
  150. package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
  151. package/docs/ja/doc/doc-report-header.md +46 -0
  152. package/docs/ja/page-types/csv.md +83 -0
  153. package/docs/ja/page-types/doc.md +83 -0
  154. package/docs/ja/page-types/excel.md +82 -0
  155. package/docs/ja/page-types/slide.md +132 -0
  156. package/docs/ja/page-types.md +47 -0
  157. package/docs/ja/parts/area-chart.md +85 -0
  158. package/docs/ja/parts/bar-chart.md +95 -0
  159. package/docs/ja/parts/bar-list.md +58 -0
  160. package/docs/ja/parts/category-bar.md +36 -0
  161. package/docs/ja/parts/chart-frame.md +36 -0
  162. package/docs/ja/parts/data-table.md +22 -0
  163. package/docs/ja/parts/delta.md +50 -0
  164. package/docs/ja/parts/donut-chart.md +66 -0
  165. package/docs/ja/parts/echart.md +3 -0
  166. package/docs/ja/parts/format.md +44 -0
  167. package/docs/ja/parts/funnel-steps.md +27 -8
  168. package/docs/ja/parts/heatmap.md +45 -0
  169. package/docs/ja/parts/kpi-card.md +55 -0
  170. package/docs/ja/parts/legend.md +36 -0
  171. package/docs/ja/parts/line-chart.md +84 -0
  172. package/docs/ja/parts/metric-value.md +3 -2
  173. package/docs/ja/parts/page-shell.md +1 -1
  174. package/docs/ja/parts/progress-circle.md +34 -0
  175. package/docs/ja/parts/sample-data.md +61 -0
  176. package/docs/ja/parts/scatter-chart.md +69 -0
  177. package/docs/ja/parts/sparkline.md +2 -2
  178. package/docs/ja/parts/tracker.md +32 -0
  179. package/docs/ja/parts/trend-indicator.md +17 -1
  180. package/docs/ja/parts/updated-at.md +27 -0
  181. package/docs/ja/parts/waterfall-chart.md +61 -0
  182. package/docs/ja/routing.md +8 -84
  183. package/docs/ja/slide/slide-chart-insight.md +52 -0
  184. package/docs/ja/slide/slide-cover.md +39 -0
  185. package/docs/ja/slide/slide-exec-summary.md +47 -0
  186. package/docs/ja/slide/slide-kpi-grid.md +55 -0
  187. package/docs/ja/slide/slide-kpi-hero.md +58 -0
  188. package/docs/ja/slide/slide-next-actions.md +49 -0
  189. package/docs/ja/slide/slide-scorecard-table.md +54 -0
  190. package/docs/ja/ui/alert-dialog.md +41 -0
  191. package/docs/ja/ui/avatar.md +31 -0
  192. package/docs/ja/ui/combobox.md +74 -0
  193. package/docs/ja/ui/drawer.md +41 -0
  194. package/docs/ja/ui/empty.md +1 -1
  195. package/docs/ja/ui/error-state.md +4 -3
  196. package/docs/ja/ui/field.md +42 -0
  197. package/docs/ja/ui/hover-card.md +21 -0
  198. package/docs/ja/ui/kbd.md +16 -0
  199. package/docs/ja/ui/loading.md +1 -1
  200. package/docs/ja/ui/pagination.md +36 -0
  201. package/docs/ja/ui/radio-group.md +21 -0
  202. package/docs/ja/ui/toast.md +46 -0
  203. package/docs/ja/ui/toggle-group.md +4 -3
  204. package/docs/ja/ui-and-theming.md +14 -7
  205. package/package.json +43 -29
  206. package/registry/ui/accordion.tsx +76 -0
  207. package/registry/ui/alert-dialog.tsx +189 -0
  208. package/registry/ui/alert.tsx +80 -0
  209. package/registry/ui/app-shell.tsx +287 -0
  210. package/registry/ui/area-chart.tsx +3701 -0
  211. package/registry/ui/attention-list.tsx +303 -0
  212. package/registry/ui/avatar.tsx +110 -0
  213. package/registry/ui/badge.tsx +56 -0
  214. package/registry/ui/bar-chart.tsx +3698 -0
  215. package/registry/ui/bar-list.tsx +3743 -0
  216. package/registry/ui/box-plot-chart.tsx +3906 -0
  217. package/registry/ui/breadcrumb.tsx +128 -0
  218. package/registry/ui/button.tsx +62 -0
  219. package/registry/ui/calendar.tsx +223 -0
  220. package/registry/ui/card.tsx +107 -0
  221. package/registry/ui/category-bar.tsx +1290 -0
  222. package/registry/ui/chart-frame.tsx +1393 -0
  223. package/registry/ui/checkbox.tsx +30 -0
  224. package/registry/ui/collapsible.tsx +23 -0
  225. package/registry/ui/combobox.tsx +310 -0
  226. package/registry/ui/command.tsx +198 -0
  227. package/registry/ui/dashboard-card.tsx +217 -0
  228. package/registry/ui/data-table.tsx +693 -146
  229. package/registry/ui/date-range-picker.tsx +390 -0
  230. package/registry/ui/delta.tsx +188 -0
  231. package/registry/ui/dialog.tsx +161 -0
  232. package/registry/ui/donut-chart.tsx +3751 -0
  233. package/registry/ui/drawer.tsx +229 -0
  234. package/registry/ui/dropdown-menu.tsx +274 -0
  235. package/registry/ui/echart.tsx +1040 -0
  236. package/registry/ui/field.tsx +240 -0
  237. package/registry/ui/filter-bar.tsx +392 -0
  238. package/registry/ui/funnel-steps.tsx +3569 -0
  239. package/registry/ui/heatmap.tsx +1786 -0
  240. package/registry/ui/histogram-chart.tsx +3986 -0
  241. package/registry/ui/hover-card.tsx +52 -0
  242. package/registry/ui/input-group.tsx +160 -0
  243. package/registry/ui/input.tsx +24 -0
  244. package/registry/ui/kbd.tsx +30 -0
  245. package/registry/ui/kpi-card.tsx +699 -0
  246. package/registry/ui/label.tsx +22 -0
  247. package/registry/ui/legend.tsx +3563 -0
  248. package/registry/ui/line-chart.tsx +3699 -0
  249. package/registry/ui/metric-value.tsx +62 -0
  250. package/registry/ui/multi-select.tsx +232 -0
  251. package/registry/ui/page-shell.tsx +282 -0
  252. package/registry/ui/pagination.tsx +140 -0
  253. package/registry/ui/paired-chart.tsx +3827 -0
  254. package/registry/ui/placeholder.tsx +36 -0
  255. package/registry/ui/popover.tsx +92 -0
  256. package/registry/ui/progress-circle.tsx +867 -0
  257. package/registry/ui/progress.tsx +85 -0
  258. package/registry/ui/radio-group.tsx +39 -0
  259. package/registry/ui/sample-data.tsx +86 -0
  260. package/registry/ui/scatter-chart.tsx +4212 -0
  261. package/registry/ui/scroll-area.tsx +56 -0
  262. package/registry/ui/searchable-select.tsx +214 -0
  263. package/registry/ui/section-header.tsx +43 -0
  264. package/registry/ui/segmented-control.tsx +76 -0
  265. package/registry/ui/select.tsx +255 -0
  266. package/registry/ui/separator.tsx +27 -0
  267. package/registry/ui/sheet.tsx +139 -0
  268. package/registry/ui/sidebar.tsx +725 -0
  269. package/registry/ui/skeleton.tsx +17 -0
  270. package/registry/ui/slider.tsx +56 -0
  271. package/registry/ui/sparkline.tsx +230 -0
  272. package/registry/ui/spinner.tsx +14 -0
  273. package/registry/ui/states.tsx +90 -0
  274. package/registry/ui/status-badge.tsx +121 -0
  275. package/registry/ui/switch.tsx +34 -0
  276. package/registry/ui/table.tsx +118 -0
  277. package/registry/ui/tabs.tsx +84 -0
  278. package/registry/ui/textarea.tsx +22 -0
  279. package/registry/ui/toast.tsx +250 -0
  280. package/registry/ui/toggle-group.tsx +91 -0
  281. package/registry/ui/toggle.tsx +47 -0
  282. package/registry/ui/tooltip.tsx +72 -0
  283. package/registry/ui/tracker.tsx +849 -0
  284. package/registry/ui/trend-indicator.tsx +74 -0
  285. package/registry/ui/updated-at.tsx +122 -0
  286. package/registry/ui/waterfall-chart.tsx +4248 -0
  287. package/skills/vantage-add-feature/SKILL.md +9 -7
  288. package/skills/vantage-design/SKILL.md +36 -24
  289. package/skills/vantage-design/references/extract.md +16 -14
  290. package/skills/vantage-design/references/format.md +6 -5
  291. package/skills/vantage-design/references/presets.md +60 -110
  292. package/skills/vantage-pitfalls/SKILL.md +5 -1
  293. package/skills/vantage-slide/SKILL.md +71 -36
  294. package/templates/AGENTS.md +24 -3
  295. package/templates/agents/api.md +29 -0
  296. package/templates/agents/csv.md +2 -1
  297. package/templates/agents/doc.md +51 -3
  298. package/templates/agents/page.md +54 -9
  299. package/templates/agents/slide.md +68 -3
  300. package/templates/agents/xlsx.md +2 -1
  301. package/theme.css +284 -128
  302. package/dist/add-EISGSZAH.js +0 -6
  303. package/dist/add-EISGSZAH.js.map +0 -1
  304. package/dist/chunk-3G43C6OL.js.map +0 -1
  305. package/dist/chunk-5ZOP4SQR.js.map +0 -1
  306. package/dist/chunk-A2UUGASH.js.map +0 -1
  307. package/dist/chunk-BCE22JSM.js.map +0 -1
  308. package/dist/chunk-DTDVSFRY.js.map +0 -1
  309. package/dist/chunk-EP6JHXSR.js.map +0 -1
  310. package/dist/chunk-KLEIVDUV.js.map +0 -1
  311. package/dist/chunk-MXI3PZFT.js.map +0 -1
  312. package/dist/chunk-PLST5Q7T.js +0 -2221
  313. package/dist/chunk-PLST5Q7T.js.map +0 -1
  314. package/dist/chunk-PVTHHV7X.js.map +0 -1
  315. package/dist/chunk-T3FLAGI6.js +0 -316
  316. package/dist/chunk-T3FLAGI6.js.map +0 -1
  317. package/dist/dev-NZRLOQCL.js.map +0 -1
  318. package/dist/doctor-VFKEMR54.js.map +0 -1
  319. package/dist/preview-QDCIVVSR.js.map +0 -1
@@ -0,0 +1,303 @@
1
+ // Copied by `vantage add ui attention-list`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/attention-list.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * AttentionList — 期日超過・期限間近・リスク・滞留に当たる行を上に寄せ、理由と次の一手を添えて並べる要対応一覧。
7
+ * 判定は `judgeAttention`(純関数)が行い、部品は並べ替えと描画だけをする。区分の文言としきい値
8
+ * (`dueSoonDays` / `staleDays`)は props で渡す。業務の意味(請求・商談・契約など)は呼び元が決める。
9
+ *
10
+ * - 区分は 1 行に 1 つ。複数に当たるときは 期日超過 > リスク > 期限間近 > 滞留 の順で強いほうを採る。
11
+ * - 並びは区分の強さ、同じ区分では日数の大きい(遅れている・止まっている)行が先。日数が同じなら元の順。
12
+ * - 区分の色は StatusBadge の意味色(negative / warning / info)。色だけでなく区分名と日数の文で伝える。
13
+ * - 判定に当たらない行は既定で出さない(`includeUnflagged` で末尾に出す)。当たる行がなければ Empty を出す。
14
+ * - 行の項目は関数(`getDueDate` など)か列名(`dueDateKey` など)で渡す。列名は JSON で書けるので、下書きはこちらを使う。
15
+ * 両方あれば関数が先。どちらも無ければ、期日・動き・リスクは「無い」として判定せず、行の ID は行の順番、見出しは空になる。
16
+ * - 行を押すと `onRowClick(row)` を呼ぶ。渡さなければ押せない行(button ではなく li)になる。
17
+ *
18
+ * @example
19
+ * import { AttentionList } from "@/components/attention-list"
20
+ * // Vantage: import { AttentionList } from "@squadbase/vantage/components"
21
+ *
22
+ * <AttentionList
23
+ * rows={receivables}
24
+ * asOf="2026-10-23"
25
+ * dueSoonDays={7}
26
+ * getRowId={(r) => r.id}
27
+ * getTitle={(r) => r.customer}
28
+ * getDueDate={(r) => r.dueDate}
29
+ * getNextAction={(r) => r.nextAction}
30
+ * onRowClick={(r) => openDetail(r.id)}
31
+ * />
32
+ */
33
+ import { differenceInCalendarDays, parseISO } from "date-fns"
34
+ import { useMemo, type HTMLAttributes, type ReactNode } from "react"
35
+
36
+ import { formatDate } from "@squadbase/vantage/components"
37
+ import { cn } from "@squadbase/vantage/ui"
38
+ import { Empty } from "@squadbase/vantage/ui"
39
+ import { StatusBadge } from "@squadbase/vantage/components"
40
+
41
+ export type AttentionKind = "overdue" | "risk" | "dueSoon" | "stale"
42
+
43
+ /** 強い順。並びと、複数に当たったときの採用に使う。 */
44
+ const KIND_ORDER: readonly AttentionKind[] = ["overdue", "risk", "dueSoon", "stale"]
45
+
46
+ const KIND_TONES: Record<AttentionKind, string> = {
47
+ overdue: "negative",
48
+ risk: "negative",
49
+ dueSoon: "warning",
50
+ stale: "info",
51
+ }
52
+
53
+ export const DEFAULT_ATTENTION_LABELS: Record<AttentionKind, string> = {
54
+ overdue: "期日超過",
55
+ risk: "リスク",
56
+ dueSoon: "期限間近",
57
+ stale: "滞留",
58
+ }
59
+
60
+ type DateInput = Date | string | null | undefined
61
+
62
+ export interface AttentionJudgement {
63
+ kind: AttentionKind
64
+ /**
65
+ * overdue: 期日を過ぎた日数(1 以上)/ dueSoon: 期日までの日数(0 = 当日)/
66
+ * stale: 最終の動きからの日数/ risk: 0。
67
+ */
68
+ days: number
69
+ }
70
+
71
+ export interface JudgeAttentionInput {
72
+ /** 期日。無ければ期日の判定(超過・間近)はしない。 */
73
+ dueDate?: DateInput
74
+ /** 最後に動きがあった日。無ければ滞留の判定はしない。 */
75
+ lastActivityAt?: DateInput
76
+ /** 呼び元がリスクと判定した行。 */
77
+ risk?: boolean
78
+ /** 基準日。必ず渡す(部品の内で「今日」を決めない)。 */
79
+ asOf: Date | string
80
+ /** 期日の何日前から「期限間近」にするか。当日を含み、この日数ちょうども含む。既定 7。 */
81
+ dueSoonDays?: number
82
+ /** 何日動きがなければ「滞留」にするか。この日数ちょうどから滞留。未指定なら滞留は判定しない。 */
83
+ staleDays?: number
84
+ }
85
+
86
+ function toDate(value: DateInput): Date | null {
87
+ if (value === null || value === undefined || value === "") return null
88
+ const date = typeof value === "string" ? parseISO(value) : value
89
+ return Number.isFinite(date.getTime()) ? date : null
90
+ }
91
+
92
+ /**
93
+ * 1 行が要対応かを判定する。当たらなければ `null`。日数は暦日(時刻は無視)で数える。
94
+ * 期日の当日は「超過」ではなく「期限間近(0 日)」。
95
+ */
96
+ export function judgeAttention(input: JudgeAttentionInput): AttentionJudgement | null {
97
+ const asOf = toDate(input.asOf)
98
+ if (!asOf) return null
99
+ const dueSoonDays = input.dueSoonDays ?? 7
100
+
101
+ const due = toDate(input.dueDate)
102
+ if (due) {
103
+ const untilDue = differenceInCalendarDays(due, asOf)
104
+ if (untilDue < 0) return { kind: "overdue", days: -untilDue }
105
+ }
106
+ if (input.risk) return { kind: "risk", days: 0 }
107
+ if (due) {
108
+ const untilDue = differenceInCalendarDays(due, asOf)
109
+ if (untilDue <= dueSoonDays) return { kind: "dueSoon", days: untilDue }
110
+ }
111
+ const last = toDate(input.lastActivityAt)
112
+ if (last && input.staleDays !== undefined) {
113
+ const idle = differenceInCalendarDays(asOf, last)
114
+ if (idle >= input.staleDays) return { kind: "stale", days: idle }
115
+ }
116
+ return null
117
+ }
118
+
119
+ /** 区分の強さ → 日数の大きい順(dueSoon は期日が近い順)。同順位は 0 を返し、元の順を保つ。 */
120
+ export function compareAttention(a: AttentionJudgement | null, b: AttentionJudgement | null): number {
121
+ if (!a || !b) return a ? -1 : b ? 1 : 0
122
+ const byKind = KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind)
123
+ if (byKind !== 0) return byKind
124
+ return a.kind === "dueSoon" ? a.days - b.days : b.days - a.days
125
+ }
126
+
127
+ function defaultReason(j: AttentionJudgement, labels: Record<AttentionKind, string>): string {
128
+ switch (j.kind) {
129
+ case "overdue":
130
+ return `期日を ${j.days} 日過ぎています`
131
+ case "dueSoon":
132
+ return j.days === 0 ? "期日は今日です" : `期日まであと ${j.days} 日です`
133
+ case "stale":
134
+ return `${j.days} 日間動きがありません`
135
+ case "risk":
136
+ return labels.risk
137
+ }
138
+ }
139
+
140
+ export interface AttentionListProps<T> {
141
+ rows: T[]
142
+ /** 基準日(`"2026-10-23"` か Date)。 */
143
+ asOf: Date | string
144
+ /** 期日の何日前から「期限間近」にするか。既定 7。 */
145
+ dueSoonDays?: number
146
+ /** この日数動きがなければ「滞留」。未指定なら滞留は判定しない。 */
147
+ staleDays?: number
148
+ /** 区分名の差し替え(例: `{ overdue: "入金遅れ" }`)。 */
149
+ labels?: Partial<Record<AttentionKind, string>>
150
+ /** 行の ID。省略時は `idKey` の列、それも無ければ行の順番。 */
151
+ getRowId?: (row: T) => string
152
+ /** 行の見出し(顧客名・案件名など)。省略時は `titleKey` の列、それも無ければ空。 */
153
+ getTitle?: (row: T) => ReactNode
154
+ getDueDate?: (row: T) => DateInput
155
+ getLastActivityAt?: (row: T) => DateInput
156
+ /** リスクと判定する行。期日・滞留より先に出す。 */
157
+ isRisk?: (row: T) => boolean
158
+ /** 行の ID の列名(JSON で書ける `getRowId`)。 */
159
+ idKey?: string
160
+ /** 見出しの列名(JSON で書ける `getTitle`)。 */
161
+ titleKey?: string
162
+ /** 期日の列名(JSON で書ける `getDueDate`)。値は `"2026-10-23"` 形式の日付か Date。 */
163
+ dueDateKey?: string
164
+ /** 最後に動きがあった日の列名(JSON で書ける `getLastActivityAt`)。 */
165
+ lastActivityAtKey?: string
166
+ /** リスクの列名(JSON で書ける `isRisk`)。値が真の行をリストにする。 */
167
+ riskKey?: string
168
+ /** 次の一手の列名(JSON で書ける `getNextAction`)。値が空の行には添えない。 */
169
+ nextActionKey?: string
170
+ /** 理由の文。省略すると区分と日数から作る。 */
171
+ getReason?: (row: T, judgement: AttentionJudgement) => ReactNode
172
+ /** 次の一手。判定に当たる行に添える。 */
173
+ getNextAction?: (row: T, judgement: AttentionJudgement) => ReactNode
174
+ /** 見出しの右に出す補足(金額・担当者など)。 */
175
+ getMeta?: (row: T) => ReactNode
176
+ /** 行を押したとき。渡すと行が button になる。 */
177
+ onRowClick?: (row: T) => void
178
+ /** 判定に当たらない行も末尾に出す。既定 false。 */
179
+ includeUnflagged?: boolean
180
+ /** 該当なしのときの見出し・説明。 */
181
+ emptyTitle?: string
182
+ emptyDescription?: string
183
+ className?: string
184
+ }
185
+
186
+ function cell(row: unknown, key: string | undefined): unknown {
187
+ if (!key || row === null || typeof row !== "object") return undefined
188
+ return (row as Record<string, unknown>)[key]
189
+ }
190
+
191
+ function asDateInput(value: unknown): DateInput {
192
+ return typeof value === "string" || value instanceof Date ? value : undefined
193
+ }
194
+
195
+ function asNode(value: unknown): ReactNode {
196
+ return typeof value === "string" || typeof value === "number" ? value : null
197
+ }
198
+
199
+ export function AttentionList<T>({
200
+ rows,
201
+ asOf,
202
+ dueSoonDays,
203
+ staleDays,
204
+ labels,
205
+ getRowId,
206
+ getTitle,
207
+ getDueDate,
208
+ getLastActivityAt,
209
+ isRisk,
210
+ idKey,
211
+ titleKey,
212
+ dueDateKey,
213
+ lastActivityAtKey,
214
+ riskKey,
215
+ nextActionKey,
216
+ getReason,
217
+ getNextAction,
218
+ getMeta,
219
+ onRowClick,
220
+ includeUnflagged = false,
221
+ emptyTitle = "対応が必要な項目はありません",
222
+ emptyDescription,
223
+ className,
224
+ ...rest
225
+ }: AttentionListProps<T> & Omit<HTMLAttributes<HTMLUListElement>, keyof AttentionListProps<T>>) {
226
+ const mergedLabels = useMemo(() => ({ ...DEFAULT_ATTENTION_LABELS, ...labels }), [labels])
227
+ const colorMap = KIND_TONES
228
+ const dueOf = (row: T) => (getDueDate ? getDueDate(row) : asDateInput(cell(row, dueDateKey)))
229
+ const nextActionOf = (row: T, j: AttentionJudgement): ReactNode =>
230
+ getNextAction ? getNextAction(row, j) : asNode(cell(row, nextActionKey))
231
+
232
+ // アクセサは呼び元の判定そのものなので、rows が同じ参照でも毎回かけ直す(メモ化すると古い判定が残る)
233
+ const items = (rows ?? [])
234
+ .map((row, index) => ({
235
+ row,
236
+ index,
237
+ judgement: judgeAttention({
238
+ dueDate: dueOf(row),
239
+ lastActivityAt: getLastActivityAt ? getLastActivityAt(row) : asDateInput(cell(row, lastActivityAtKey)),
240
+ risk: isRisk ? isRisk(row) : Boolean(cell(row, riskKey)),
241
+ asOf,
242
+ dueSoonDays,
243
+ staleDays,
244
+ }),
245
+ }))
246
+ .filter((item) => includeUnflagged || item.judgement)
247
+ .sort((a, b) => compareAttention(a.judgement, b.judgement) || a.index - b.index)
248
+
249
+ if (items.length === 0) {
250
+ return <Empty title={emptyTitle} description={emptyDescription} className={className} />
251
+ }
252
+
253
+ return (
254
+ <ul data-slot="attention-list" className={cn("divide-y divide-border", className)} {...rest}>
255
+ {items.map(({ row, index, judgement }) => {
256
+ const due = dueOf(row)
257
+ const body = (
258
+ <>
259
+ <div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1">
260
+ {judgement && (
261
+ <StatusBadge status={judgement.kind} label={mergedLabels[judgement.kind]} colorMap={colorMap} />
262
+ )}
263
+ <span className="min-w-0 font-medium">{getTitle ? getTitle(row) : asNode(cell(row, titleKey))}</span>
264
+ {getMeta && <span className="text-sm tabular-nums text-muted-foreground">{getMeta(row)}</span>}
265
+ {due ? (
266
+ <span className="ml-auto text-sm tabular-nums text-muted-foreground">
267
+ 期日 {formatDate(due)}
268
+ </span>
269
+ ) : null}
270
+ </div>
271
+ {judgement && (
272
+ <p className="mt-1 text-sm text-muted-foreground">
273
+ {getReason ? getReason(row, judgement) : defaultReason(judgement, mergedLabels)}
274
+ </p>
275
+ )}
276
+ {judgement && (getNextAction || nextActionKey) && nextActionOf(row, judgement) ? (
277
+ <p className="mt-1 text-sm">
278
+ <span className="text-muted-foreground">次の一手: </span>
279
+ {nextActionOf(row, judgement)}
280
+ </p>
281
+ ) : null}
282
+ </>
283
+ )
284
+ return (
285
+ <li key={getRowId ? getRowId(row) : String(cell(row, idKey) ?? index)} data-slot="attention-list-row">
286
+ {onRowClick ? (
287
+ <button
288
+ type="button"
289
+ onClick={() => onRowClick(row)}
290
+ className="block w-full px-3 py-3 text-left transition-colors hover:bg-muted/50 focus-visible:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
291
+ >
292
+ {body}
293
+ </button>
294
+ ) : (
295
+ <div className="px-3 py-3">{body}</div>
296
+ )}
297
+ </li>
298
+ )
299
+ })}
300
+ </ul>
301
+ )
302
+ }
303
+ AttentionList.displayName = "AttentionList"
@@ -0,0 +1,110 @@
1
+ // Copied by `vantage add ui avatar`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/avatar.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import * as React from "react"
6
+ import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
7
+ import { cn } from "@squadbase/vantage/ui"
8
+
9
+ function Avatar({
10
+ className,
11
+ size = "default",
12
+ ...props
13
+ }: AvatarPrimitive.Root.Props & {
14
+ size?: "default" | "sm" | "lg"
15
+ }) {
16
+ return (
17
+ <AvatarPrimitive.Root
18
+ data-slot="avatar"
19
+ data-size={size}
20
+ className={cn(
21
+ "group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
22
+ className
23
+ )}
24
+ {...props}
25
+ />
26
+ )
27
+ }
28
+
29
+ function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
30
+ return (
31
+ <AvatarPrimitive.Image
32
+ data-slot="avatar-image"
33
+ className={cn(
34
+ "aspect-square size-full rounded-full object-cover",
35
+ className
36
+ )}
37
+ {...props}
38
+ />
39
+ )
40
+ }
41
+
42
+ function AvatarFallback({
43
+ className,
44
+ ...props
45
+ }: AvatarPrimitive.Fallback.Props) {
46
+ return (
47
+ <AvatarPrimitive.Fallback
48
+ data-slot="avatar-fallback"
49
+ className={cn(
50
+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
51
+ className
52
+ )}
53
+ {...props}
54
+ />
55
+ )
56
+ }
57
+
58
+ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
59
+ return (
60
+ <span
61
+ data-slot="avatar-badge"
62
+ className={cn(
63
+ "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
64
+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
65
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
66
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
67
+ className
68
+ )}
69
+ {...props}
70
+ />
71
+ )
72
+ }
73
+
74
+ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="avatar-group"
78
+ className={cn(
79
+ "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
80
+ className
81
+ )}
82
+ {...props}
83
+ />
84
+ )
85
+ }
86
+
87
+ function AvatarGroupCount({
88
+ className,
89
+ ...props
90
+ }: React.ComponentProps<"div">) {
91
+ return (
92
+ <div
93
+ data-slot="avatar-group-count"
94
+ className={cn(
95
+ "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
96
+ className
97
+ )}
98
+ {...props}
99
+ />
100
+ )
101
+ }
102
+
103
+ export {
104
+ Avatar,
105
+ AvatarImage,
106
+ AvatarFallback,
107
+ AvatarGroup,
108
+ AvatarGroupCount,
109
+ AvatarBadge,
110
+ }
@@ -0,0 +1,56 @@
1
+ // Copied by `vantage add ui badge`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/ui/badge.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ import { mergeProps } from "@base-ui/react/merge-props"
6
+ import { useRender } from "@base-ui/react/use-render"
7
+ import { cva, type VariantProps } from "class-variance-authority"
8
+
9
+ import { cn } from "@squadbase/vantage/ui"
10
+
11
+ const badgeVariants = cva(
12
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
13
+ {
14
+ variants: {
15
+ variant: {
16
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
19
+ destructive:
20
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
21
+ outline:
22
+ "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
23
+ ghost:
24
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
25
+ link: "text-primary underline-offset-4 hover:underline",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ },
31
+ }
32
+ )
33
+
34
+ function Badge({
35
+ className,
36
+ variant = "default",
37
+ render,
38
+ ...props
39
+ }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
40
+ return useRender({
41
+ defaultTagName: "span",
42
+ props: mergeProps<"span">(
43
+ {
44
+ className: cn(badgeVariants({ variant }), className),
45
+ },
46
+ props
47
+ ),
48
+ render,
49
+ state: {
50
+ slot: "badge",
51
+ variant,
52
+ },
53
+ })
54
+ }
55
+
56
+ export { Badge, badgeVariants }