@tipp/ui 2.9.0 → 2.10.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.
Files changed (198) hide show
  1. package/dist/app/index.js +17 -17
  2. package/dist/app/platform/edit-coaching-time.js +17 -17
  3. package/dist/app/platform/edit-service-type.js +17 -17
  4. package/dist/app/platform/goal-manage-card-edit.js +17 -17
  5. package/dist/app/platform/on-offline-radio-card.js +17 -17
  6. package/dist/app/platform/reservation-card.js +17 -17
  7. package/dist/app/platform/session-card.js +17 -17
  8. package/dist/app/roleplay/chart/session-chart.cjs +351 -0
  9. package/dist/app/roleplay/chart/session-chart.cjs.map +1 -0
  10. package/dist/app/roleplay/chart/session-chart.d.cts +16 -0
  11. package/dist/app/roleplay/chart/session-chart.d.ts +16 -0
  12. package/dist/app/roleplay/chart/session-chart.js +14 -0
  13. package/dist/app/roleplay/chart/session-chart.js.map +1 -0
  14. package/dist/app/roleplay/index.cjs +5706 -0
  15. package/dist/app/roleplay/index.cjs.map +1 -0
  16. package/dist/app/roleplay/index.d.cts +4 -0
  17. package/dist/app/roleplay/index.d.ts +4 -0
  18. package/dist/app/roleplay/index.js +182 -0
  19. package/dist/app/roleplay/index.js.map +1 -0
  20. package/dist/app/roleplay/parts/analysis-section.cjs +2178 -0
  21. package/dist/app/roleplay/parts/analysis-section.cjs.map +1 -0
  22. package/dist/app/roleplay/parts/analysis-section.d.cts +17 -0
  23. package/dist/app/roleplay/parts/analysis-section.d.ts +17 -0
  24. package/dist/app/roleplay/parts/analysis-section.js +97 -0
  25. package/dist/app/roleplay/parts/analysis-section.js.map +1 -0
  26. package/dist/app/roleplay/parts/coaching-panel.cjs +2191 -0
  27. package/dist/app/roleplay/parts/coaching-panel.cjs.map +1 -0
  28. package/dist/app/roleplay/parts/coaching-panel.d.cts +8 -0
  29. package/dist/app/roleplay/parts/coaching-panel.d.ts +8 -0
  30. package/dist/app/roleplay/parts/coaching-panel.js +95 -0
  31. package/dist/app/roleplay/parts/coaching-panel.js.map +1 -0
  32. package/dist/app/roleplay/parts/content.cjs +4683 -0
  33. package/dist/app/roleplay/parts/content.cjs.map +1 -0
  34. package/dist/app/roleplay/parts/content.d.cts +15 -0
  35. package/dist/app/roleplay/parts/content.d.ts +15 -0
  36. package/dist/app/roleplay/parts/content.js +174 -0
  37. package/dist/app/roleplay/parts/content.js.map +1 -0
  38. package/dist/app/roleplay/parts/header.cjs +2300 -0
  39. package/dist/app/roleplay/parts/header.cjs.map +1 -0
  40. package/dist/app/roleplay/parts/header.d.cts +13 -0
  41. package/dist/app/roleplay/parts/header.d.ts +13 -0
  42. package/dist/app/roleplay/parts/header.js +96 -0
  43. package/dist/app/roleplay/parts/header.js.map +1 -0
  44. package/dist/app/roleplay/parts/score-summary.cjs +2276 -0
  45. package/dist/app/roleplay/parts/score-summary.cjs.map +1 -0
  46. package/dist/app/roleplay/parts/score-summary.d.cts +12 -0
  47. package/dist/app/roleplay/parts/score-summary.d.ts +12 -0
  48. package/dist/app/roleplay/parts/score-summary.js +96 -0
  49. package/dist/app/roleplay/parts/score-summary.js.map +1 -0
  50. package/dist/app/roleplay/parts/score-trend-section.cjs +3686 -0
  51. package/dist/app/roleplay/parts/score-trend-section.cjs.map +1 -0
  52. package/dist/app/roleplay/parts/score-trend-section.d.cts +14 -0
  53. package/dist/app/roleplay/parts/score-trend-section.d.ts +14 -0
  54. package/dist/app/roleplay/parts/score-trend-section.js +168 -0
  55. package/dist/app/roleplay/parts/score-trend-section.js.map +1 -0
  56. package/dist/app/roleplay/parts/script-transcript.cjs +2288 -0
  57. package/dist/app/roleplay/parts/script-transcript.cjs.map +1 -0
  58. package/dist/app/roleplay/parts/script-transcript.d.cts +15 -0
  59. package/dist/app/roleplay/parts/script-transcript.d.ts +15 -0
  60. package/dist/app/roleplay/parts/script-transcript.js +96 -0
  61. package/dist/app/roleplay/parts/script-transcript.js.map +1 -0
  62. package/dist/app/roleplay/parts/section-nav.cjs +2433 -0
  63. package/dist/app/roleplay/parts/section-nav.cjs.map +1 -0
  64. package/dist/app/roleplay/parts/section-nav.d.cts +15 -0
  65. package/dist/app/roleplay/parts/section-nav.d.ts +15 -0
  66. package/dist/app/roleplay/parts/section-nav.js +96 -0
  67. package/dist/app/roleplay/parts/section-nav.js.map +1 -0
  68. package/dist/app/roleplay/parts/session-replay-dialog.cjs +2598 -0
  69. package/dist/app/roleplay/parts/session-replay-dialog.cjs.map +1 -0
  70. package/dist/app/roleplay/parts/session-replay-dialog.d.cts +14 -0
  71. package/dist/app/roleplay/parts/session-replay-dialog.d.ts +14 -0
  72. package/dist/app/roleplay/parts/session-replay-dialog.js +97 -0
  73. package/dist/app/roleplay/parts/session-replay-dialog.js.map +1 -0
  74. package/dist/app/roleplay/parts/step-detail.cjs +2311 -0
  75. package/dist/app/roleplay/parts/step-detail.cjs.map +1 -0
  76. package/dist/app/roleplay/parts/step-detail.d.cts +9 -0
  77. package/dist/app/roleplay/parts/step-detail.d.ts +9 -0
  78. package/dist/app/roleplay/parts/step-detail.js +96 -0
  79. package/dist/app/roleplay/parts/step-detail.js.map +1 -0
  80. package/dist/app/roleplay/parts/step-radar.cjs +463 -0
  81. package/dist/app/roleplay/parts/step-radar.cjs.map +1 -0
  82. package/dist/app/roleplay/parts/step-radar.d.cts +8 -0
  83. package/dist/app/roleplay/parts/step-radar.d.ts +8 -0
  84. package/dist/app/roleplay/parts/step-radar.js +14 -0
  85. package/dist/app/roleplay/parts/step-radar.js.map +1 -0
  86. package/dist/app/roleplay/parts/step-summary.cjs +2651 -0
  87. package/dist/app/roleplay/parts/step-summary.cjs.map +1 -0
  88. package/dist/app/roleplay/parts/step-summary.d.cts +8 -0
  89. package/dist/app/roleplay/parts/step-summary.d.ts +8 -0
  90. package/dist/app/roleplay/parts/step-summary.js +101 -0
  91. package/dist/app/roleplay/parts/step-summary.js.map +1 -0
  92. package/dist/app/roleplay/rp-session-detail.cjs +5703 -0
  93. package/dist/app/roleplay/rp-session-detail.cjs.map +1 -0
  94. package/dist/app/roleplay/rp-session-detail.d.cts +17 -0
  95. package/dist/app/roleplay/rp-session-detail.d.ts +17 -0
  96. package/dist/app/roleplay/rp-session-detail.js +179 -0
  97. package/dist/app/roleplay/rp-session-detail.js.map +1 -0
  98. package/dist/app/roleplay/types.cjs +19 -0
  99. package/dist/app/roleplay/types.cjs.map +1 -0
  100. package/dist/app/roleplay/types.d.cts +42 -0
  101. package/dist/app/roleplay/types.d.ts +42 -0
  102. package/dist/app/roleplay/types.js +2 -0
  103. package/dist/app/roleplay/types.js.map +1 -0
  104. package/dist/app/roleplay/utils/report-parse.cjs +285 -0
  105. package/dist/app/roleplay/utils/report-parse.cjs.map +1 -0
  106. package/dist/app/roleplay/utils/report-parse.d.cts +57 -0
  107. package/dist/app/roleplay/utils/report-parse.d.ts +57 -0
  108. package/dist/app/roleplay/utils/report-parse.js +8 -0
  109. package/dist/app/roleplay/utils/report-parse.js.map +1 -0
  110. package/dist/app/roleplay/utils/report-parse.test.cjs +491 -0
  111. package/dist/app/roleplay/utils/report-parse.test.cjs.map +1 -0
  112. package/dist/app/roleplay/utils/report-parse.test.d.cts +2 -0
  113. package/dist/app/roleplay/utils/report-parse.test.d.ts +2 -0
  114. package/dist/app/roleplay/utils/report-parse.test.js +237 -0
  115. package/dist/app/roleplay/utils/report-parse.test.js.map +1 -0
  116. package/dist/app/roleplay/utils/session-utils.cjs +204 -0
  117. package/dist/app/roleplay/utils/session-utils.cjs.map +1 -0
  118. package/dist/app/roleplay/utils/session-utils.d.cts +38 -0
  119. package/dist/app/roleplay/utils/session-utils.d.ts +38 -0
  120. package/dist/app/roleplay/utils/session-utils.js +46 -0
  121. package/dist/app/roleplay/utils/session-utils.js.map +1 -0
  122. package/dist/chunk-2TOASXPK.js +131 -0
  123. package/dist/chunk-2TOASXPK.js.map +1 -0
  124. package/dist/chunk-3UGBMKSQ.js +166 -0
  125. package/dist/chunk-3UGBMKSQ.js.map +1 -0
  126. package/dist/chunk-4YAA25AV.js +161 -0
  127. package/dist/chunk-4YAA25AV.js.map +1 -0
  128. package/dist/chunk-5QIT4URV.js +87 -0
  129. package/dist/chunk-5QIT4URV.js.map +1 -0
  130. package/dist/chunk-6YIKOYBQ.js +185 -0
  131. package/dist/chunk-6YIKOYBQ.js.map +1 -0
  132. package/dist/chunk-7MXY3D3Z.js +84 -0
  133. package/dist/chunk-7MXY3D3Z.js.map +1 -0
  134. package/dist/chunk-EBFQFOUQ.js +138 -0
  135. package/dist/chunk-EBFQFOUQ.js.map +1 -0
  136. package/dist/chunk-EHDUKUML.js +183 -0
  137. package/dist/chunk-EHDUKUML.js.map +1 -0
  138. package/dist/{chunk-NEJCZABQ.js → chunk-FJEDSUGU.js} +7 -7
  139. package/dist/chunk-GA6W62DO.js +101 -0
  140. package/dist/chunk-GA6W62DO.js.map +1 -0
  141. package/dist/chunk-KJPBBBVK.js +142 -0
  142. package/dist/chunk-KJPBBBVK.js.map +1 -0
  143. package/dist/{chunk-TQTCT7DC.js → chunk-LRNO4W2P.js} +5 -5
  144. package/dist/{chunk-6DWUGXSY.js → chunk-N242VJOP.js} +2 -2
  145. package/dist/chunk-NKW6BO5R.js +264 -0
  146. package/dist/chunk-NKW6BO5R.js.map +1 -0
  147. package/dist/{chunk-R5UDNKV2.js → chunk-O332K3SW.js} +2 -2
  148. package/dist/chunk-OUP3VFXG.js +1 -0
  149. package/dist/chunk-OUP3VFXG.js.map +1 -0
  150. package/dist/chunk-QMAINJ4G.js +190 -0
  151. package/dist/chunk-QMAINJ4G.js.map +1 -0
  152. package/dist/{chunk-KLJVIEFW.js → chunk-SOKGYD5B.js} +7 -7
  153. package/dist/chunk-T7LKMJVA.js +142 -0
  154. package/dist/chunk-T7LKMJVA.js.map +1 -0
  155. package/dist/chunk-TFJJKM3W.js +261 -0
  156. package/dist/chunk-TFJJKM3W.js.map +1 -0
  157. package/dist/{chunk-KQ3ME6XY.js → chunk-U2EAXN3Y.js} +4 -4
  158. package/dist/chunk-UXAEOCOH.js +150 -0
  159. package/dist/chunk-UXAEOCOH.js.map +1 -0
  160. package/dist/chunk-XM4GWPXC.js +236 -0
  161. package/dist/chunk-XM4GWPXC.js.map +1 -0
  162. package/dist/index.css +76 -0
  163. package/dist/index.js +32 -32
  164. package/dist/molecules/curriculumV2/CurriculumContents/CurriculumReviewSteps/ReviewQuestion/index.js +5 -5
  165. package/dist/molecules/curriculumV2/CurriculumContents/CurriculumReviewSteps/ReviewQuestion/review-question-body.js +2 -2
  166. package/dist/molecules/curriculumV2/CurriculumContents/CurriculumReviewSteps/index.js +7 -7
  167. package/dist/molecules/curriculumV2/CurriculumContents/curriculum-review.js +8 -8
  168. package/dist/molecules/curriculumV2/CurriculumContents/index.js +9 -9
  169. package/dist/molecules/curriculumV2/index.js +11 -11
  170. package/dist/molecules/dynamic-form-editor.js +2 -2
  171. package/dist/molecules/index.js +29 -29
  172. package/package.json +28 -2
  173. package/src/app/roleplay/chart/session-chart.tsx +185 -0
  174. package/src/app/roleplay/index.ts +8 -0
  175. package/src/app/roleplay/parts/analysis-section.tsx +98 -0
  176. package/src/app/roleplay/parts/coaching-panel.tsx +101 -0
  177. package/src/app/roleplay/parts/content.tsx +146 -0
  178. package/src/app/roleplay/parts/header.tsx +168 -0
  179. package/src/app/roleplay/parts/score-summary.tsx +158 -0
  180. package/src/app/roleplay/parts/score-trend-section.tsx +255 -0
  181. package/src/app/roleplay/parts/script-transcript.tsx +190 -0
  182. package/src/app/roleplay/parts/section-nav.tsx +299 -0
  183. package/src/app/roleplay/parts/session-replay-dialog.tsx +168 -0
  184. package/src/app/roleplay/parts/step-detail.tsx +173 -0
  185. package/src/app/roleplay/parts/step-radar.tsx +80 -0
  186. package/src/app/roleplay/parts/step-summary.tsx +149 -0
  187. package/src/app/roleplay/rp-session-detail.tsx +176 -0
  188. package/src/app/roleplay/types.ts +61 -0
  189. package/src/app/roleplay/utils/report-parse.test.ts +268 -0
  190. package/src/app/roleplay/utils/report-parse.ts +443 -0
  191. package/src/app/roleplay/utils/session-utils.ts +215 -0
  192. package/src/constrant/trainingLog.dummy.json +1 -0
  193. /package/dist/{chunk-NEJCZABQ.js.map → chunk-FJEDSUGU.js.map} +0 -0
  194. /package/dist/{chunk-TQTCT7DC.js.map → chunk-LRNO4W2P.js.map} +0 -0
  195. /package/dist/{chunk-6DWUGXSY.js.map → chunk-N242VJOP.js.map} +0 -0
  196. /package/dist/{chunk-R5UDNKV2.js.map → chunk-O332K3SW.js.map} +0 -0
  197. /package/dist/{chunk-KLJVIEFW.js.map → chunk-SOKGYD5B.js.map} +0 -0
  198. /package/dist/{chunk-KQ3ME6XY.js.map → chunk-U2EAXN3Y.js.map} +0 -0
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  DynamicFormEditor
3
- } from "../chunk-NEJCZABQ.js";
3
+ } from "../chunk-FJEDSUGU.js";
4
4
  import "../chunk-3DZIX4CE.js";
5
5
  import "../chunk-CBYIPRND.js";
6
- import "../chunk-Z7WNKARH.js";
7
6
  import "../chunk-D4U2ZV2A.js";
8
7
  import "../chunk-V7KAHLW2.js";
9
8
  import "../chunk-SQ3FYBJT.js";
10
9
  import "../chunk-NK6S7ZMB.js";
10
+ import "../chunk-Z7WNKARH.js";
11
11
  import "../chunk-FAL6S2XK.js";
12
12
  import "../chunk-AELKCD6B.js";
13
13
  import "../chunk-5XYPLDI5.js";
@@ -1,5 +1,8 @@
1
1
  import "../chunk-SYMOHU2B.js";
2
2
  import "../chunk-AS7ID74X.js";
3
+ import {
4
+ CurriculumBody
5
+ } from "../chunk-N242VJOP.js";
3
6
  import {
4
7
  CurriculumSidebar
5
8
  } from "../chunk-RRC4WFG3.js";
@@ -8,27 +11,34 @@ import "../chunk-WS7IOHVK.js";
8
11
  import "../chunk-47QYYEMG.js";
9
12
  import "../chunk-QLOYGKKM.js";
10
13
  import "../chunk-S4MEMFKG.js";
11
- import {
12
- CurriculumBody
13
- } from "../chunk-6DWUGXSY.js";
14
14
  import "../chunk-K4RZTENM.js";
15
15
  import "../chunk-UXH2XIB2.js";
16
- import "../chunk-R5UDNKV2.js";
17
- import "../chunk-TQTCT7DC.js";
18
- import "../chunk-NVRVOTWZ.js";
16
+ import "../chunk-O332K3SW.js";
17
+ import "../chunk-LRNO4W2P.js";
19
18
  import "../chunk-LK2H4QTJ.js";
20
- import "../chunk-KLJVIEFW.js";
19
+ import "../chunk-SOKGYD5B.js";
20
+ import "../chunk-U2EAXN3Y.js";
21
+ import "../chunk-WLKK5L3P.js";
22
+ import "../chunk-GE4M5GOQ.js";
21
23
  import "../chunk-G3KQN32M.js";
22
24
  import "../chunk-VK6INFSB.js";
23
- import "../chunk-GMKRNFH5.js";
24
- import "../chunk-KQ3ME6XY.js";
25
25
  import "../chunk-CGLDFUOY.js";
26
26
  import "../chunk-OUDDTPEN.js";
27
27
  import "../chunk-ZGTGEBQX.js";
28
- import "../chunk-WLKK5L3P.js";
29
28
  import "../chunk-SINRU2A6.js";
30
- import "../chunk-GE4M5GOQ.js";
29
+ import "../chunk-NVRVOTWZ.js";
30
+ import "../chunk-GMKRNFH5.js";
31
31
  import "../chunk-KFUBWOCN.js";
32
+ import {
33
+ CurriculumSubNav
34
+ } from "../chunk-XWHBJY5V.js";
35
+ import {
36
+ CurriculumProvider,
37
+ useCurriculumContext
38
+ } from "../chunk-WA5A2GB4.js";
39
+ import {
40
+ CurriculumV2Layout
41
+ } from "../chunk-K3Q4XLCN.js";
32
42
  import {
33
43
  DatePickerButton
34
44
  } from "../chunk-AT5PAJLW.js";
@@ -40,20 +50,7 @@ import {
40
50
  createColumnHelper
41
51
  } from "../chunk-5RIAIE2J.js";
42
52
  import "../chunk-JCG2AUFJ.js";
43
- import {
44
- CurriculumSubNav
45
- } from "../chunk-XWHBJY5V.js";
46
- import {
47
- CurriculumProvider,
48
- useCurriculumContext
49
- } from "../chunk-WA5A2GB4.js";
50
- import {
51
- CurriculumV2Layout
52
- } from "../chunk-K3Q4XLCN.js";
53
53
  import "../chunk-2ISIAPFS.js";
54
- import {
55
- Navigation
56
- } from "../chunk-6OT5OYGM.js";
57
54
  import {
58
55
  OneOnOneGuideList,
59
56
  oneOnOneNoteTextReplacer
@@ -80,7 +77,7 @@ import {
80
77
  } from "../chunk-AHYOAFOU.js";
81
78
  import {
82
79
  DynamicFormEditor
83
- } from "../chunk-NEJCZABQ.js";
80
+ } from "../chunk-FJEDSUGU.js";
84
81
  import {
85
82
  DynamicFormResponseItem
86
83
  } from "../chunk-77H5BUYO.js";
@@ -95,9 +92,8 @@ import {
95
92
  LearningPost
96
93
  } from "../chunk-CUS3ORMD.js";
97
94
  import {
98
- DescriptionJsonRender
99
- } from "../chunk-N3FMP27I.js";
100
- import "../chunk-BEKSO5JG.js";
95
+ Navigation
96
+ } from "../chunk-6OT5OYGM.js";
101
97
  import {
102
98
  DotNavigation
103
99
  } from "../chunk-IJOOKMKQ.js";
@@ -106,7 +102,6 @@ import {
106
102
  } from "../chunk-C6HO3IGE.js";
107
103
  import "../chunk-3DZIX4CE.js";
108
104
  import "../chunk-CBYIPRND.js";
109
- import "../chunk-Z7WNKARH.js";
110
105
  import "../chunk-D4U2ZV2A.js";
111
106
  import "../chunk-V7KAHLW2.js";
112
107
  import {
@@ -116,9 +111,14 @@ import {
116
111
  import {
117
112
  DynamicFormField
118
113
  } from "../chunk-NK6S7ZMB.js";
114
+ import "../chunk-Z7WNKARH.js";
119
115
  import {
120
116
  AutocompleteTextfield
121
117
  } from "../chunk-OOM522XP.js";
118
+ import {
119
+ DescriptionJsonRender
120
+ } from "../chunk-N3FMP27I.js";
121
+ import "../chunk-BEKSO5JG.js";
122
122
  import "../chunk-FAL6S2XK.js";
123
123
  import "../chunk-AELKCD6B.js";
124
124
  import "../chunk-5XYPLDI5.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tipp/ui",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -14,6 +14,16 @@
14
14
  "default": "./dist/index.js"
15
15
  }
16
16
  },
17
+ "./roleplay": {
18
+ "require": {
19
+ "types": "./dist/app/roleplay/index.d.ts",
20
+ "default": "./dist/app/roleplay/index.cjs"
21
+ },
22
+ "import": {
23
+ "types": "./dist/app/roleplay/index.d.ts",
24
+ "default": "./dist/app/roleplay/index.js"
25
+ }
26
+ },
17
27
  "./*": "./*"
18
28
  },
19
29
  "main": "dist/index.js",
@@ -39,8 +49,10 @@
39
49
  "tailwind-merge": "^3.3.1"
40
50
  },
41
51
  "devDependencies": {
42
- "@types/echarts": "^4.9.22",
43
52
  "@types/jest": "^29.5.14",
53
+ "echarts": "^6.0.0",
54
+ "echarts-for-react": "^3.0.6",
55
+ "react-resize-detector": "^12.3.0",
44
56
  "@types/lodash-es": "^4.17.12",
45
57
  "autoprefixer": "10.4.19",
46
58
  "eslint": "^8.57.0",
@@ -65,12 +77,26 @@
65
77
  "@types/react": "*",
66
78
  "@types/react-dom": "*",
67
79
  "dompurify": "^3.2.6",
80
+ "echarts": ">=5.0.0",
81
+ "echarts-for-react": "^3.0.0",
68
82
  "formik": "^2.4.6",
69
83
  "react": "^19.0 || ^19.0.0-rc",
70
84
  "react-datepicker": "^8.0",
71
85
  "react-dom": "^19.0 || ^19.0.0-rc",
86
+ "react-resize-detector": ">=11.0.0",
72
87
  "yup": "^1.4.0"
73
88
  },
89
+ "peerDependenciesMeta": {
90
+ "echarts": {
91
+ "optional": true
92
+ },
93
+ "echarts-for-react": {
94
+ "optional": true
95
+ },
96
+ "react-resize-detector": {
97
+ "optional": true
98
+ }
99
+ },
74
100
  "turbo": {
75
101
  "pipeline": {
76
102
  "build": {
@@ -0,0 +1,185 @@
1
+ 'use client';
2
+
3
+ // echarts 기반 Chart 래퍼 + 토큰 색 해석 훅.
4
+ // echarts 본체/echarts-for-react/react-resize-detector 는 @tipp/ui 의 optional peerDependency.
5
+ // 이 모듈은 subpath(@tipp/ui/roleplay)로만 노출되어 메인 번들에서 격리된다.
6
+ // SSR 안전: echarts/ResizeObserver 는 클라이언트 전용이라 마운트 이후에만 렌더한다.
7
+
8
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
9
+ import { useResizeDetector } from 'react-resize-detector';
10
+ import type { EChartsOption } from 'echarts';
11
+ import * as echarts from 'echarts/core';
12
+ import * as ReactEChartsCoreNs from 'echarts-for-react/lib/core';
13
+ import { LineChart, RadarChart } from 'echarts/charts';
14
+ import { SVGRenderer } from 'echarts/renderers';
15
+ import {
16
+ AxisPointerComponent,
17
+ AriaComponent,
18
+ TooltipComponent,
19
+ GridComponent,
20
+ LegendComponent,
21
+ PolarComponent,
22
+ RadarComponent,
23
+ } from 'echarts/components';
24
+ import { EchartDefaultProps } from '../../../charts';
25
+
26
+ // CJS/ESM interop 정규화.
27
+ // echarts-for-react/lib/core 는 CJS(exports.default = 클래스)이고 @tipp/ui 는 strict ESM("type":"module")
28
+ // 이라, 번들러/런타임에 따라 실제 컴포넌트가 default 로 0~2 겹 감싸진다. 특히 webpack 이 __esModule 를
29
+ // 무시하면 module.exports 를 한 번 더 감싸 ReactEChartsCoreNs.default.default 에 클래스가 들어가고,
30
+ // 그대로 렌더하면 React 가 "Element type is invalid (got: object)" 로 던진다.
31
+ // 함수(클래스/함수형 컴포넌트)가 나올 때까지 default 를 풀어 정규화한다.
32
+ type EChartsComponentType = typeof ReactEChartsCoreNs.default;
33
+
34
+ function unwrapDefault(mod: unknown): unknown {
35
+ let current = mod;
36
+ while (current !== null && typeof current === 'object' && 'default' in current) {
37
+ current = current.default;
38
+ }
39
+ return current;
40
+ }
41
+
42
+ function isComponentType(value: unknown): value is EChartsComponentType {
43
+ return typeof value === 'function';
44
+ }
45
+
46
+ const unwrappedChart = unwrapDefault(ReactEChartsCoreNs);
47
+ const ReactEChartsCore: EChartsComponentType = isComponentType(unwrappedChart)
48
+ ? unwrappedChart
49
+ : ReactEChartsCoreNs.default;
50
+
51
+ echarts.use([
52
+ SVGRenderer,
53
+ LineChart,
54
+ RadarChart,
55
+ TooltipComponent,
56
+ GridComponent,
57
+ LegendComponent,
58
+ AriaComponent,
59
+ AxisPointerComponent,
60
+ PolarComponent,
61
+ RadarComponent,
62
+ ]);
63
+
64
+ // base/option 깊은 병합 (배열은 option 우선)
65
+ function deepMerge<T>(base: T, override: unknown): T {
66
+ if (!override) return base;
67
+ if (
68
+ typeof base !== 'object' ||
69
+ !base ||
70
+ Array.isArray(base) ||
71
+ typeof override !== 'object' ||
72
+ Array.isArray(override)
73
+ ) {
74
+ return override as T;
75
+ }
76
+ const out: Record<string, unknown> = { ...(base as Record<string, unknown>) };
77
+ for (const [k, v] of Object.entries(override as Record<string, unknown>)) {
78
+ out[k] = k in out ? deepMerge(out[k], v) : v;
79
+ }
80
+ return out as T;
81
+ }
82
+
83
+ export type ChartProps = Omit<
84
+ React.ComponentProps<typeof ReactEChartsCore>,
85
+ 'option' | 'echarts'
86
+ > & {
87
+ option: EChartsOption;
88
+ };
89
+
90
+ export function Chart({
91
+ option: optionProp,
92
+ ...rest
93
+ }: ChartProps): React.ReactElement {
94
+ const { width, height, ref } = useResizeDetector({
95
+ handleHeight: true,
96
+ refreshMode: 'debounce',
97
+ refreshRate: 100,
98
+ });
99
+ const [mounted, setMounted] = useState(false);
100
+ useEffect(() => {
101
+ setMounted(true);
102
+ }, []);
103
+
104
+ const option = useMemo(() => {
105
+ const baseOption: EChartsOption = {
106
+ textStyle: { fontFamily: "var(--default-font-family, 'Noto Sans KR')" },
107
+ tooltip: { textStyle: { fontSize: 13 } }, // ds-allow: chart-lib
108
+ animation: false,
109
+ };
110
+ return deepMerge(baseOption, optionProp);
111
+ }, [optionProp]);
112
+
113
+ return (
114
+ <div ref={ref} style={{ width: '100%', height: '100%' }}>
115
+ {mounted ? (
116
+ <ReactEChartsCore
117
+ {...EchartDefaultProps}
118
+ echarts={echarts}
119
+ opts={{ renderer: 'svg' }}
120
+ style={{ height, width }}
121
+ {...rest}
122
+ option={option}
123
+ />
124
+ ) : null}
125
+ </div>
126
+ );
127
+ }
128
+
129
+ // 임의의 CSS 색 문자열을 echarts(zrender)가 파싱 가능한 rgb(a)로 정규화한다.
130
+ function normalizeColor(value: string, fallback: string): string {
131
+ const v = value.trim();
132
+ if (!v) return fallback;
133
+ if (/^#|^rgb|^hsl/i.test(v)) return v;
134
+ try {
135
+ const canvas = document.createElement('canvas');
136
+ const ctx = canvas.getContext('2d');
137
+ if (!ctx) return fallback;
138
+ ctx.fillStyle = '#000';
139
+ ctx.fillStyle = v;
140
+ const out = ctx.fillStyle;
141
+ return /color\(|^$/i.test(out) ? fallback : out;
142
+ } catch {
143
+ return fallback;
144
+ }
145
+ }
146
+
147
+ // @tipp/ui 토큰(CSS 변수)을 echarts 가 먹을 수 있는 실제 색 문자열로 해석하는 훅.
148
+ export function useTokenColors<K extends string>(
149
+ vars: Record<K, [cssVar: string, fallback: string]>
150
+ ): {
151
+ colors: Record<K, string>;
152
+ probe: React.ReactNode;
153
+ } {
154
+ const probeRef = useRef<HTMLSpanElement | null>(null);
155
+ const fallbacks = useMemo(() => {
156
+ const f = {} as Record<K, string>;
157
+ for (const k of Object.keys(vars) as K[]) f[k] = vars[k][1];
158
+ return f;
159
+ }, [vars]);
160
+ const [resolved, setResolved] = useState<Record<K, string>>(fallbacks);
161
+ const key = JSON.stringify(vars);
162
+ // vars 객체는 매 렌더 새 참조라 deps에 직접 넣을 수 없어 ref로 최신값을 참조하고,
163
+ // 실제 변경 추적은 직렬화한 key 로 한다.
164
+ const varsRef = useRef(vars);
165
+ varsRef.current = vars;
166
+ useEffect(() => {
167
+ const el = probeRef.current;
168
+ if (!el) return;
169
+ const cs = getComputedStyle(el);
170
+ const next = {} as Record<K, string>;
171
+ for (const k of Object.keys(varsRef.current) as K[]) {
172
+ const [cssVar, fallback] = varsRef.current[k];
173
+ next[k] = normalizeColor(cs.getPropertyValue(cssVar), fallback);
174
+ }
175
+ setResolved(next);
176
+ }, [key]);
177
+ const probe = (
178
+ <span
179
+ aria-hidden
180
+ ref={probeRef}
181
+ style={{ position: 'absolute', width: 0, height: 0, overflow: 'hidden' }}
182
+ />
183
+ );
184
+ return { colors: resolved, probe };
185
+ }
@@ -0,0 +1,8 @@
1
+ // 롤플레잉 "세션 상세" 공개 진입점 (@tipp/ui/roleplay).
2
+ //
3
+ // 주의: 이 배럴은 메인 배럴(src/index.ts → src/app/index.ts)에 re-export 하지 않는다.
4
+ // echarts 의존을 메인 번들에서 격리하기 위해 subpath(@tipp/ui/roleplay)로만 노출한다.
5
+
6
+ export * from './types';
7
+ export * from './utils/report-parse';
8
+ export * from './rp-session-detail';
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { Box, Flex, Heading, Separator, Typo } from '../../../atoms';
5
+ import type { RpReportAnalysisItem } from '../utils/report-parse';
6
+
7
+ /* ───────────────────────── Section ───────────────────────── */
8
+
9
+ export function Section({
10
+ title,
11
+ desc,
12
+ children,
13
+ }: {
14
+ title: string;
15
+ desc?: string;
16
+ children: React.ReactNode;
17
+ }): React.ReactElement {
18
+ return (
19
+ <section>
20
+ <Box mb="3">
21
+ <Heading
22
+ size={{ initial: '4', md: '5' }}
23
+ style={{ textWrap: 'balance' }}
24
+ weight="bold"
25
+ >
26
+ {title}
27
+ </Heading>
28
+ {desc ? (
29
+ <Box mt="1" style={{ maxWidth: '65ch' }}>
30
+ <Typo as="p" color="gray" variant="body">
31
+ {desc}
32
+ </Typo>
33
+ </Box>
34
+ ) : null}
35
+ </Box>
36
+ {children}
37
+ </section>
38
+ );
39
+ }
40
+
41
+ /* ───────────────────────── AnalysisSection ───────────────────────── */
42
+
43
+ export interface AnalysisSectionProps {
44
+ title: string;
45
+ desc?: string;
46
+ items: RpReportAnalysisItem[];
47
+ tone: 'good' | 'bad';
48
+ }
49
+
50
+ export function AnalysisSection({
51
+ title,
52
+ desc,
53
+ items,
54
+ tone,
55
+ }: AnalysisSectionProps): React.ReactElement | null {
56
+ if (items.length === 0) return null;
57
+
58
+ const accentBorder = tone === 'good' ? 'var(--green-a6)' : 'var(--amber-a6)';
59
+ const numColor = tone === 'good' ? 'var(--green-11)' : 'var(--amber-11)';
60
+
61
+ return (
62
+ <Section desc={desc} title={title}>
63
+ <Flex direction="column">
64
+ {items.map((item, index) => (
65
+ <Box key={item.title}>
66
+ {index > 0 && <Separator size="4" />}
67
+ <Flex
68
+ gap="3"
69
+ pl="3"
70
+ py="3"
71
+ style={{ borderLeft: `2px solid ${accentBorder}` }}
72
+ >
73
+ <Typo
74
+ as="span"
75
+ mono
76
+ style={{ flexShrink: 0, color: numColor }}
77
+ variant="body"
78
+ weight="bold"
79
+ >
80
+ {index + 1}
81
+ </Typo>
82
+ <Box style={{ minWidth: 0 }}>
83
+ <Typo as="p" variant="body" weight="medium">
84
+ {item.title}
85
+ </Typo>
86
+ <Box mt="1" style={{ maxWidth: '65ch' }}>
87
+ <Typo as="p" variant="body">
88
+ {item.detail}
89
+ </Typo>
90
+ </Box>
91
+ </Box>
92
+ </Flex>
93
+ </Box>
94
+ ))}
95
+ </Flex>
96
+ </Section>
97
+ );
98
+ }
@@ -0,0 +1,101 @@
1
+ 'use client';
2
+
3
+ import { Box, Card, Flex, Grid, Heading, Typo } from '../../../atoms';
4
+ import type { RpReportSkill } from '../utils/report-parse';
5
+
6
+ export interface CoachingPanelProps {
7
+ skills: RpReportSkill[];
8
+ }
9
+
10
+ function Section({
11
+ title,
12
+ desc,
13
+ children,
14
+ }: {
15
+ title: string;
16
+ desc?: string;
17
+ children: React.ReactNode;
18
+ }): React.ReactElement {
19
+ return (
20
+ <section>
21
+ <Box mb="3">
22
+ <Heading
23
+ size={{ initial: '4', md: '5' }}
24
+ style={{ textWrap: 'balance' }}
25
+ weight="bold"
26
+ >
27
+ {title}
28
+ </Heading>
29
+ {desc ? (
30
+ <Box mt="1" style={{ maxWidth: '65ch' }}>
31
+ <Typo as="p" color="gray" variant="body">
32
+ {desc}
33
+ </Typo>
34
+ </Box>
35
+ ) : null}
36
+ </Box>
37
+ {children}
38
+ </section>
39
+ );
40
+ }
41
+
42
+ export function CoachingPanel({ skills }: CoachingPanelProps): React.ReactElement | null {
43
+ if (skills.length === 0) return null;
44
+ return (
45
+ <Section desc="다음 재도전에 바로 쓸 대화 기술" title="즉시 적용 스킬">
46
+ <Grid columns="1" gap="6">
47
+ {skills.map((skill, index) => (
48
+ <Card
49
+ key={skill.title}
50
+ style={{ padding: 'var(--space-3)' }}
51
+ variant="surface"
52
+ >
53
+ <Flex align="center" gap="2">
54
+ <Flex
55
+ align="center"
56
+ justify="center"
57
+ style={{
58
+ width: 22,
59
+ height: 22,
60
+ borderRadius: 'var(--radius-full)',
61
+ background: 'var(--accent-9)',
62
+ flexShrink: 0,
63
+ }}
64
+ >
65
+ <Typo
66
+ as="span"
67
+ mono
68
+ style={{ color: 'var(--accent-contrast)' }}
69
+ variant="body"
70
+ weight="bold"
71
+ >
72
+ {index + 1}
73
+ </Typo>
74
+ </Flex>
75
+ <Typo as="span" variant="body" weight="medium">
76
+ {skill.title}
77
+ </Typo>
78
+ </Flex>
79
+ <Box mt="2" style={{ maxWidth: '65ch' }}>
80
+ <Typo as="p" variant="body">
81
+ {skill.description}
82
+ </Typo>
83
+ </Box>
84
+ <Box
85
+ mt="2"
86
+ p="2"
87
+ style={{
88
+ borderRadius: 'var(--radius-2)',
89
+ background: 'var(--accent-a2)',
90
+ }}
91
+ >
92
+ <Typo as="p" style={{ color: 'var(--accent-11)' }} variant="body">
93
+ {skill.example}
94
+ </Typo>
95
+ </Box>
96
+ </Card>
97
+ ))}
98
+ </Grid>
99
+ </Section>
100
+ );
101
+ }