@tipp/ui 2.9.0 → 2.10.1

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 +5715 -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 +4687 -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 +3687 -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 +167 -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 +5712 -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-32WIS4L7.js +131 -0
  123. package/dist/chunk-32WIS4L7.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-TFJJKM3W.js +261 -0
  154. package/dist/chunk-TFJJKM3W.js.map +1 -0
  155. package/dist/{chunk-KQ3ME6XY.js → chunk-U2EAXN3Y.js} +4 -4
  156. package/dist/chunk-UXAEOCOH.js +150 -0
  157. package/dist/chunk-UXAEOCOH.js.map +1 -0
  158. package/dist/chunk-YHP75Y5K.js +242 -0
  159. package/dist/chunk-YHP75Y5K.js.map +1 -0
  160. package/dist/chunk-ZE36DFOC.js +142 -0
  161. package/dist/chunk-ZE36DFOC.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 +265 -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
@@ -0,0 +1,38 @@
1
+ import { RpSessionScriptLine } from '../types.js';
2
+ import { RpQuality, RpReportStep } from './report-parse.js';
3
+
4
+ interface SpeechShare {
5
+ userPct: number;
6
+ partnerPct: number;
7
+ }
8
+ type QColor = 'green' | 'amber' | 'red' | 'gray';
9
+ declare const COLOR_VARS: Record<QColor, {
10
+ fg: string;
11
+ bg: string;
12
+ border: string;
13
+ }>;
14
+ declare function qualityColor(quality: RpQuality | null): QColor;
15
+ declare const QUALITY_RATIO: Record<RpQuality, number>;
16
+ declare function qualityRatio(quality: RpQuality | null): number | null;
17
+ declare function gradeColorFromLabel(label: string | null): QColor;
18
+ declare function stepColor(step: RpReportStep): QColor;
19
+ declare function stepVars(step: RpReportStep): {
20
+ fg: string;
21
+ bg: string;
22
+ border: string;
23
+ };
24
+ declare function stepRatio(step: RpReportStep): number | null;
25
+ declare function stepScoreText(step: RpReportStep): string | null;
26
+ declare function stepGradeLabel(step: RpReportStep): string | null;
27
+ declare function hasStepSignal(step: RpReportStep): boolean;
28
+ declare function stepCode(step: RpReportStep): string;
29
+ declare function formatDuration(seconds: number | null): string;
30
+ declare function formatDate(iso: string): string;
31
+ declare function formatTimestamp(ms: number): string;
32
+ declare const SYNC_TOLERANCE = 0.05;
33
+ declare function isLengthClose(mediaSec: number | null, sttSec: number): boolean;
34
+ declare const SPEECH_OVERTALK_THRESHOLD = 40;
35
+ declare function computeSpeechShare(lines: RpSessionScriptLine[]): SpeechShare | null;
36
+ declare function statusLabelOf(status: string | null): string;
37
+
38
+ export { COLOR_VARS, type QColor, QUALITY_RATIO, SPEECH_OVERTALK_THRESHOLD, SYNC_TOLERANCE, type SpeechShare, computeSpeechShare, formatDate, formatDuration, formatTimestamp, gradeColorFromLabel, hasStepSignal, isLengthClose, qualityColor, qualityRatio, statusLabelOf, stepCode, stepColor, stepGradeLabel, stepRatio, stepScoreText, stepVars };
@@ -0,0 +1,46 @@
1
+ import {
2
+ COLOR_VARS,
3
+ QUALITY_RATIO,
4
+ SPEECH_OVERTALK_THRESHOLD,
5
+ SYNC_TOLERANCE,
6
+ computeSpeechShare,
7
+ formatDate,
8
+ formatDuration,
9
+ formatTimestamp,
10
+ gradeColorFromLabel,
11
+ hasStepSignal,
12
+ isLengthClose,
13
+ qualityColor,
14
+ qualityRatio,
15
+ statusLabelOf,
16
+ stepCode,
17
+ stepColor,
18
+ stepGradeLabel,
19
+ stepRatio,
20
+ stepScoreText,
21
+ stepVars
22
+ } from "../../../chunk-4YAA25AV.js";
23
+ import "../../../chunk-CRTRMMJ7.js";
24
+ export {
25
+ COLOR_VARS,
26
+ QUALITY_RATIO,
27
+ SPEECH_OVERTALK_THRESHOLD,
28
+ SYNC_TOLERANCE,
29
+ computeSpeechShare,
30
+ formatDate,
31
+ formatDuration,
32
+ formatTimestamp,
33
+ gradeColorFromLabel,
34
+ hasStepSignal,
35
+ isLengthClose,
36
+ qualityColor,
37
+ qualityRatio,
38
+ statusLabelOf,
39
+ stepCode,
40
+ stepColor,
41
+ stepGradeLabel,
42
+ stepRatio,
43
+ stepScoreText,
44
+ stepVars
45
+ };
46
+ //# sourceMappingURL=session-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,131 @@
1
+ import {
2
+ StepSummary
3
+ } from "./chunk-EBFQFOUQ.js";
4
+ import {
5
+ ScoreSummary
6
+ } from "./chunk-6YIKOYBQ.js";
7
+ import {
8
+ ScoreTrendSection
9
+ } from "./chunk-YHP75Y5K.js";
10
+ import {
11
+ SectionNav
12
+ } from "./chunk-NKW6BO5R.js";
13
+ import {
14
+ StepDetail
15
+ } from "./chunk-KJPBBBVK.js";
16
+ import {
17
+ hasStepSignal
18
+ } from "./chunk-4YAA25AV.js";
19
+ import {
20
+ AnalysisSection,
21
+ Section
22
+ } from "./chunk-7MXY3D3Z.js";
23
+ import {
24
+ CoachingPanel
25
+ } from "./chunk-GA6W62DO.js";
26
+ import {
27
+ Separator
28
+ } from "./chunk-HYITAA4J.js";
29
+ import {
30
+ Grid
31
+ } from "./chunk-EGEQY3KT.js";
32
+ import {
33
+ Heading
34
+ } from "./chunk-USGOLLYO.js";
35
+ import {
36
+ Box
37
+ } from "./chunk-4Y5BEXVN.js";
38
+ import {
39
+ Typo
40
+ } from "./chunk-L4QQGIYQ.js";
41
+ import {
42
+ Flex
43
+ } from "./chunk-25HMMI7R.js";
44
+
45
+ // src/app/roleplay/parts/content.tsx
46
+ import { jsx, jsxs } from "react/jsx-runtime";
47
+ function Content({
48
+ detail,
49
+ report,
50
+ history,
51
+ speechShare,
52
+ scrollMarginTop
53
+ }) {
54
+ var _a;
55
+ const { steps, skills, strengths, improvements } = report;
56
+ const scoreValue = (_a = detail.totalScore) != null ? _a : report.totalScore;
57
+ const showTrend = history.length > 1;
58
+ const showStepSummary = steps.length > 0 && steps.some(hasStepSignal);
59
+ const showStepDetail = steps.length > 0;
60
+ const navItems = [
61
+ { id: "score-summary", label: "\uC885\uD569 \uC810\uC218", score: scoreValue, max: 100 },
62
+ ...strengths.length > 0 ? [{ id: "strengths", label: "\uAC15\uC810" }] : [],
63
+ ...improvements.length > 0 ? [{ id: "improvements", label: "\uAC1C\uC120\uC810" }] : [],
64
+ ...showTrend ? [{ id: "score-trend", label: "\uCD1D\uC810 \uCD94\uC774" }] : [],
65
+ ...showStepSummary ? [{ id: "step-summary", label: "\uD3C9\uAC00 \uB2E8\uACC4 \uC694\uC57D" }] : [],
66
+ ...showStepDetail ? [{ id: "step-detail", label: "\uB2E8\uACC4\uBCC4 \uC0C1\uC138 \uBD84\uC11D" }] : [],
67
+ ...skills.length > 0 ? [{ id: "coaching", label: "\uC989\uC2DC \uC801\uC6A9 \uC2A4\uD0AC" }] : []
68
+ ];
69
+ return /* @__PURE__ */ jsxs(
70
+ Grid,
71
+ {
72
+ align: "start",
73
+ columns: { initial: "1", md: "minmax(0, 1fr) 224px" },
74
+ gap: "7",
75
+ children: [
76
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "7", children: [
77
+ report.frameworkTitle !== null || report.frameworkDescription !== null ? /* @__PURE__ */ jsxs(Box, { children: [
78
+ report.frameworkTitle !== null ? /* @__PURE__ */ jsx(Heading, { size: "5", weight: "bold", children: report.frameworkTitle }) : null,
79
+ report.frameworkDescription !== null ? /* @__PURE__ */ jsx(Box, { mt: report.frameworkTitle !== null ? "2" : "0", children: /* @__PURE__ */ jsx(Typo, { as: "p", variant: "body", children: report.frameworkDescription }) }) : null
80
+ ] }) : null,
81
+ /* @__PURE__ */ jsx(Box, { id: "score-summary", style: { scrollMarginTop }, children: /* @__PURE__ */ jsx(ScoreSummary, { detail, report, speechShare }) }),
82
+ strengths.length > 0 ? /* @__PURE__ */ jsx(Box, { id: "strengths", style: { scrollMarginTop }, children: /* @__PURE__ */ jsx(
83
+ AnalysisSection,
84
+ {
85
+ desc: "\uC774\uBC88 \uC138\uC158\uC5D0\uC11C \uC798\uD55C \uC810",
86
+ items: strengths,
87
+ title: "\uAC15\uC810",
88
+ tone: "good"
89
+ }
90
+ ) }) : null,
91
+ improvements.length > 0 ? /* @__PURE__ */ jsx(Box, { id: "improvements", style: { scrollMarginTop }, children: /* @__PURE__ */ jsx(
92
+ AnalysisSection,
93
+ {
94
+ desc: "\uB2E4\uC74C \uC2DC\uB3C4\uC5D0\uC11C \uBCF4\uC644\uD560 \uC810",
95
+ items: improvements,
96
+ title: "\uAC1C\uC120\uC810",
97
+ tone: "bad"
98
+ }
99
+ ) }) : null,
100
+ showTrend ? /* @__PURE__ */ jsx(Box, { id: "score-trend", style: { scrollMarginTop }, children: /* @__PURE__ */ jsx(ScoreTrendSection, { history }) }) : null,
101
+ showStepSummary ? /* @__PURE__ */ jsx(Box, { id: "step-summary", style: { scrollMarginTop }, children: /* @__PURE__ */ jsx(StepSummary, { steps }) }) : null,
102
+ showStepDetail ? /* @__PURE__ */ jsx(Box, { id: "step-detail", style: { scrollMarginTop }, children: /* @__PURE__ */ jsx(
103
+ Section,
104
+ {
105
+ desc: "\uB300\uD654 \uD504\uB808\uC784\uC6CC\uD06C \uB2E8\uACC4\uBCC4 \uD3C9\uAC00 \uACB0\uACFC\uC785\uB2C8\uB2E4.",
106
+ title: "\uB2E8\uACC4\uBCC4 \uC0C1\uC138 \uBD84\uC11D",
107
+ children: /* @__PURE__ */ jsx(Flex, { direction: "column", children: steps.map((step, i) => /* @__PURE__ */ jsxs(Box, { children: [
108
+ i > 0 ? /* @__PURE__ */ jsx(
109
+ Separator,
110
+ {
111
+ mb: "6",
112
+ size: "4",
113
+ style: { marginTop: "calc(var(--space-9) * 1.6)" }
114
+ }
115
+ ) : null,
116
+ /* @__PURE__ */ jsx(StepDetail, { scrollMarginTop, step })
117
+ ] }, step.key)) })
118
+ }
119
+ ) }) : null,
120
+ skills.length > 0 ? /* @__PURE__ */ jsx(Box, { id: "coaching", style: { scrollMarginTop }, children: /* @__PURE__ */ jsx(CoachingPanel, { skills }) }) : null
121
+ ] }),
122
+ /* @__PURE__ */ jsx(SectionNav, { items: navItems, steps: showStepDetail ? steps : [] })
123
+ ]
124
+ }
125
+ );
126
+ }
127
+
128
+ export {
129
+ Content
130
+ };
131
+ //# sourceMappingURL=chunk-32WIS4L7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app/roleplay/parts/content.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\nimport { Box, Flex, Grid, Heading, Separator, Typo } from '../../../atoms';\nimport type { RpSessionDetailData, RpSessionHistoryItem } from '../types';\nimport type { RpReport } from '../utils/report-parse';\nimport { hasStepSignal } from '../utils/session-utils';\nimport type { SpeechShare } from '../utils/session-utils';\nimport { AnalysisSection, Section } from './analysis-section';\nimport { CoachingPanel } from './coaching-panel';\nimport { ScoreSummary } from './score-summary';\nimport { ScoreTrendSection } from './score-trend-section';\nimport { SectionNav } from './section-nav';\nimport type { SectionNavItem } from './section-nav';\nimport { StepDetail } from './step-detail';\nimport { StepSummary } from './step-summary';\n\nexport interface ContentProps {\n detail: RpSessionDetailData;\n report: RpReport;\n history: RpSessionHistoryItem[];\n speechShare: SpeechShare | null;\n scrollMarginTop: string;\n}\n\nexport function Content({\n detail,\n report,\n history,\n speechShare,\n scrollMarginTop,\n}: ContentProps): React.ReactElement {\n const { steps, skills, strengths, improvements } = report;\n const scoreValue = detail.totalScore ?? report.totalScore;\n\n const showTrend = history.length > 1;\n const showStepSummary = steps.length > 0 && steps.some(hasStepSignal);\n const showStepDetail = steps.length > 0;\n\n const navItems: SectionNavItem[] = [\n { id: 'score-summary', label: '종합 점수', score: scoreValue, max: 100 },\n ...(strengths.length > 0 ? [{ id: 'strengths', label: '강점' }] : []),\n ...(improvements.length > 0 ? [{ id: 'improvements', label: '개선점' }] : []),\n ...(showTrend ? [{ id: 'score-trend', label: '총점 추이' }] : []),\n ...(showStepSummary ? [{ id: 'step-summary', label: '평가 단계 요약' }] : []),\n ...(showStepDetail ? [{ id: 'step-detail', label: '단계별 상세 분석' }] : []),\n ...(skills.length > 0 ? [{ id: 'coaching', label: '즉시 적용 스킬' }] : []),\n ];\n\n return (\n <Grid\n align=\"start\"\n columns={{ initial: '1', md: 'minmax(0, 1fr) 224px' }}\n gap=\"7\"\n >\n <Flex direction=\"column\" gap=\"7\">\n {report.frameworkTitle !== null || report.frameworkDescription !== null ? (\n <Box>\n {report.frameworkTitle !== null ? (\n <Heading size=\"5\" weight=\"bold\">\n {report.frameworkTitle}\n </Heading>\n ) : null}\n {report.frameworkDescription !== null ? (\n <Box mt={report.frameworkTitle !== null ? '2' : '0'}>\n <Typo as=\"p\" variant=\"body\">\n {report.frameworkDescription}\n </Typo>\n </Box>\n ) : null}\n </Box>\n ) : null}\n\n <Box id=\"score-summary\" style={{ scrollMarginTop }}>\n <ScoreSummary detail={detail} report={report} speechShare={speechShare} />\n </Box>\n\n {strengths.length > 0 ? (\n <Box id=\"strengths\" style={{ scrollMarginTop }}>\n <AnalysisSection\n desc=\"이번 세션에서 잘한 점\"\n items={strengths}\n title=\"강점\"\n tone=\"good\"\n />\n </Box>\n ) : null}\n\n {improvements.length > 0 ? (\n <Box id=\"improvements\" style={{ scrollMarginTop }}>\n <AnalysisSection\n desc=\"다음 시도에서 보완할 점\"\n items={improvements}\n title=\"개선점\"\n tone=\"bad\"\n />\n </Box>\n ) : null}\n\n {showTrend ? (\n <Box id=\"score-trend\" style={{ scrollMarginTop }}>\n <ScoreTrendSection history={history} />\n </Box>\n ) : null}\n\n {showStepSummary ? (\n <Box id=\"step-summary\" style={{ scrollMarginTop }}>\n <StepSummary steps={steps} />\n </Box>\n ) : null}\n\n {showStepDetail ? (\n <Box id=\"step-detail\" style={{ scrollMarginTop }}>\n <Section\n desc=\"대화 프레임워크 단계별 평가 결과입니다.\"\n title=\"단계별 상세 분석\"\n >\n <Flex direction=\"column\">\n {steps.map((step, i) => (\n <Box key={step.key}>\n {i > 0 ? (\n <Separator\n mb=\"6\"\n size=\"4\"\n style={{ marginTop: 'calc(var(--space-9) * 1.6)' }}\n />\n ) : null}\n <StepDetail scrollMarginTop={scrollMarginTop} step={step} />\n </Box>\n ))}\n </Flex>\n </Section>\n </Box>\n ) : null}\n\n {skills.length > 0 ? (\n <Box id=\"coaching\" style={{ scrollMarginTop }}>\n <CoachingPanel skills={skills} />\n </Box>\n ) : null}\n </Flex>\n\n <SectionNav items={navItems} steps={showStepDetail ? steps : []} />\n </Grid>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,166 @@
1
+ import {
2
+ ScriptTranscript
3
+ } from "./chunk-EHDUKUML.js";
4
+ import {
5
+ computeSpeechShare,
6
+ isLengthClose
7
+ } from "./chunk-4YAA25AV.js";
8
+ import {
9
+ Tabs
10
+ } from "./chunk-CGDBUDY3.js";
11
+ import {
12
+ Grid
13
+ } from "./chunk-EGEQY3KT.js";
14
+ import {
15
+ Dialog
16
+ } from "./chunk-KDS7OQOY.js";
17
+ import {
18
+ Box
19
+ } from "./chunk-4Y5BEXVN.js";
20
+ import {
21
+ __async
22
+ } from "./chunk-CRTRMMJ7.js";
23
+
24
+ // src/app/roleplay/parts/session-replay-dialog.tsx
25
+ import { useEffect, useMemo, useRef, useState } from "react";
26
+ import { jsx, jsxs } from "react/jsx-runtime";
27
+ function resolveUrl(raw, disp, resolver) {
28
+ return __async(this, null, function* () {
29
+ if (!raw) return void 0;
30
+ if (!resolver) return raw;
31
+ try {
32
+ return yield resolver(raw, { disposition: disp });
33
+ } catch (e) {
34
+ return raw;
35
+ }
36
+ });
37
+ }
38
+ function SessionReplayDialog({
39
+ open,
40
+ onOpenChange,
41
+ detail,
42
+ script,
43
+ onResolveMediaUrl
44
+ }) {
45
+ var _a, _b, _c;
46
+ const videoRef = useRef(null);
47
+ const audioRef = useRef(null);
48
+ const hasVideo = Boolean(detail.trainingVideoURL);
49
+ const hasAudio = Boolean(detail.trainingAudioURL);
50
+ const [tab, setTab] = useState(hasVideo ? "video" : "audio");
51
+ const [currentMs, setCurrentMs] = useState(0);
52
+ const [videoSec, setVideoSec] = useState((_a = detail.videoDurationSec) != null ? _a : null);
53
+ const [audioSec, setAudioSec] = useState((_b = detail.audioDurationSec) != null ? _b : null);
54
+ const [resolvedVideoUrl, setResolvedVideoUrl] = useState(void 0);
55
+ const [resolvedAudioUrl, setResolvedAudioUrl] = useState(void 0);
56
+ useEffect(() => {
57
+ if (!open) return;
58
+ void resolveUrl(detail.trainingVideoURL, "inline", onResolveMediaUrl).then(
59
+ setResolvedVideoUrl
60
+ );
61
+ void resolveUrl(detail.trainingAudioURL, "inline", onResolveMediaUrl).then(
62
+ setResolvedAudioUrl
63
+ );
64
+ }, [open, detail.trainingVideoURL, detail.trainingAudioURL, onResolveMediaUrl]);
65
+ const lines = useMemo(() => {
66
+ var _a2;
67
+ return (_a2 = script == null ? void 0 : script.items) != null ? _a2 : [];
68
+ }, [script == null ? void 0 : script.items]);
69
+ const speechShare = useMemo(() => computeSpeechShare(lines), [lines]);
70
+ const sttSec = ((_c = script == null ? void 0 : script.durationMs) != null ? _c : 0) / 1e3;
71
+ const canSync = tab === "video" ? hasVideo && isLengthClose(videoSec, sttSec) : hasAudio && isLengthClose(audioSec, sttSec);
72
+ const activeMediaRef = tab === "video" ? videoRef : audioRef;
73
+ const handleSeek = (startMs) => {
74
+ if (!canSync) return;
75
+ const el = activeMediaRef.current;
76
+ if (!el) return;
77
+ el.currentTime = startMs / 1e3;
78
+ void el.play();
79
+ };
80
+ const handleTimeUpdate = () => {
81
+ const el = activeMediaRef.current;
82
+ if (el) {
83
+ setCurrentMs(el.currentTime * 1e3);
84
+ }
85
+ };
86
+ return /* @__PURE__ */ jsx(Dialog.Root, { onOpenChange, open, children: /* @__PURE__ */ jsx(Dialog.Content, { maxWidth: "1040px", title: "\uC138\uC158 \uB2E4\uC2DC\uBCF4\uAE30", width: "100%", children: /* @__PURE__ */ jsxs(
87
+ Grid,
88
+ {
89
+ columns: { initial: "1", md: "minmax(0, 1fr) minmax(0, 1.05fr)" },
90
+ gap: "5",
91
+ mt: "2",
92
+ children: [
93
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(
94
+ Tabs.Root,
95
+ {
96
+ onValueChange: (v) => {
97
+ setTab(v);
98
+ },
99
+ value: tab,
100
+ children: [
101
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
102
+ hasVideo ? /* @__PURE__ */ jsx(Tabs.Trigger, { value: "video", children: "\uC601\uC0C1" }) : null,
103
+ hasAudio ? /* @__PURE__ */ jsx(Tabs.Trigger, { value: "audio", children: "\uC624\uB514\uC624" }) : null
104
+ ] }),
105
+ hasVideo ? /* @__PURE__ */ jsx(Tabs.Content, { value: "video", children: /* @__PURE__ */ jsx(
106
+ Box,
107
+ {
108
+ mt: "3",
109
+ style: {
110
+ borderRadius: "var(--radius-3)",
111
+ overflow: "hidden",
112
+ background: "var(--gray-3)"
113
+ },
114
+ children: /* @__PURE__ */ jsx(
115
+ "video",
116
+ {
117
+ controls: true,
118
+ onLoadedMetadata: () => {
119
+ var _a2, _b2;
120
+ setVideoSec((_b2 = (_a2 = videoRef.current) == null ? void 0 : _a2.duration) != null ? _b2 : null);
121
+ },
122
+ onTimeUpdate: handleTimeUpdate,
123
+ ref: videoRef,
124
+ src: resolvedVideoUrl,
125
+ style: { width: "100%", display: "block", maxHeight: 440 }
126
+ }
127
+ )
128
+ }
129
+ ) }) : null,
130
+ hasAudio ? /* @__PURE__ */ jsx(Tabs.Content, { value: "audio", children: /* @__PURE__ */ jsx(Box, { mt: "3", children: /* @__PURE__ */ jsx(
131
+ "audio",
132
+ {
133
+ controls: true,
134
+ onLoadedMetadata: () => {
135
+ var _a2, _b2;
136
+ setAudioSec((_b2 = (_a2 = audioRef.current) == null ? void 0 : _a2.duration) != null ? _b2 : null);
137
+ },
138
+ onTimeUpdate: handleTimeUpdate,
139
+ ref: audioRef,
140
+ src: resolvedAudioUrl,
141
+ style: { width: "100%" }
142
+ }
143
+ ) }) }) : null
144
+ ]
145
+ }
146
+ ) }),
147
+ /* @__PURE__ */ jsx(
148
+ ScriptTranscript,
149
+ {
150
+ canSync,
151
+ currentMs,
152
+ lines,
153
+ onSeek: handleSeek,
154
+ partnerName: detail.characterTitle,
155
+ speechShare
156
+ }
157
+ )
158
+ ]
159
+ }
160
+ ) }) });
161
+ }
162
+
163
+ export {
164
+ SessionReplayDialog
165
+ };
166
+ //# sourceMappingURL=chunk-3UGBMKSQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app/roleplay/parts/session-replay-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { Box, Dialog, Grid, Tabs } from '../../../atoms';\nimport { computeSpeechShare, isLengthClose } from '../utils/session-utils';\nimport type { RpSessionDetailData, RpSessionScript } from '../types';\nimport { ScriptTranscript } from './script-transcript';\n\nexport interface SessionReplayDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n detail: RpSessionDetailData;\n script?: RpSessionScript | null;\n onResolveMediaUrl?: (\n rawUrl: string,\n options: { disposition: 'inline' | 'attachment' }\n ) => string | Promise<string>;\n}\n\ntype ReplayTab = 'video' | 'audio';\n\nasync function resolveUrl(\n raw: string | null | undefined,\n disp: 'inline' | 'attachment',\n resolver?: (rawUrl: string, options: { disposition: 'inline' | 'attachment' }) => string | Promise<string>\n): Promise<string | undefined> {\n if (!raw) return undefined;\n if (!resolver) return raw;\n try {\n return await resolver(raw, { disposition: disp });\n } catch {\n return raw;\n }\n}\n\nexport function SessionReplayDialog({\n open,\n onOpenChange,\n detail,\n script,\n onResolveMediaUrl,\n}: SessionReplayDialogProps): React.ReactElement {\n const videoRef = useRef<HTMLVideoElement>(null);\n const audioRef = useRef<HTMLAudioElement>(null);\n\n const hasVideo = Boolean(detail.trainingVideoURL);\n const hasAudio = Boolean(detail.trainingAudioURL);\n\n const [tab, setTab] = useState<ReplayTab>(hasVideo ? 'video' : 'audio');\n const [currentMs, setCurrentMs] = useState(0);\n const [videoSec, setVideoSec] = useState<number | null>(detail.videoDurationSec ?? null);\n const [audioSec, setAudioSec] = useState<number | null>(detail.audioDurationSec ?? null);\n\n const [resolvedVideoUrl, setResolvedVideoUrl] = useState<string | undefined>(undefined);\n const [resolvedAudioUrl, setResolvedAudioUrl] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!open) return;\n void resolveUrl(detail.trainingVideoURL, 'inline', onResolveMediaUrl).then(\n setResolvedVideoUrl\n );\n void resolveUrl(detail.trainingAudioURL, 'inline', onResolveMediaUrl).then(\n setResolvedAudioUrl\n );\n }, [open, detail.trainingVideoURL, detail.trainingAudioURL, onResolveMediaUrl]);\n\n const lines = useMemo(() => script?.items ?? [], [script?.items]);\n const speechShare = useMemo(() => computeSpeechShare(lines), [lines]);\n const sttSec = (script?.durationMs ?? 0) / 1000;\n\n const canSync =\n tab === 'video'\n ? hasVideo && isLengthClose(videoSec, sttSec)\n : hasAudio && isLengthClose(audioSec, sttSec);\n\n const activeMediaRef = tab === 'video' ? videoRef : audioRef;\n\n const handleSeek = (startMs: number): void => {\n if (!canSync) return;\n const el = activeMediaRef.current;\n if (!el) return;\n el.currentTime = startMs / 1000;\n void el.play();\n };\n\n const handleTimeUpdate = (): void => {\n const el = activeMediaRef.current;\n if (el) {\n setCurrentMs(el.currentTime * 1000);\n }\n };\n\n return (\n <Dialog.Root onOpenChange={onOpenChange} open={open}>\n <Dialog.Content maxWidth=\"1040px\" title=\"세션 다시보기\" width=\"100%\">\n <Grid\n columns={{ initial: '1', md: 'minmax(0, 1fr) minmax(0, 1.05fr)' }}\n gap=\"5\"\n mt=\"2\"\n >\n <Box>\n <Tabs.Root\n onValueChange={(v) => {\n setTab(v as ReplayTab);\n }}\n value={tab}\n >\n <Tabs.List>\n {hasVideo ? <Tabs.Trigger value=\"video\">영상</Tabs.Trigger> : null}\n {hasAudio ? <Tabs.Trigger value=\"audio\">오디오</Tabs.Trigger> : null}\n </Tabs.List>\n {hasVideo ? (\n <Tabs.Content value=\"video\">\n <Box\n mt=\"3\"\n style={{\n borderRadius: 'var(--radius-3)',\n overflow: 'hidden',\n background: 'var(--gray-3)',\n }}\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption -- 학습 다시보기 영상은 자막 트랙이 없는 사용자 녹화본 */}\n <video\n controls\n onLoadedMetadata={() => {\n setVideoSec(videoRef.current?.duration ?? null);\n }}\n onTimeUpdate={handleTimeUpdate}\n ref={videoRef}\n src={resolvedVideoUrl}\n style={{ width: '100%', display: 'block', maxHeight: 440 }}\n />\n </Box>\n </Tabs.Content>\n ) : null}\n {hasAudio ? (\n <Tabs.Content value=\"audio\">\n <Box mt=\"3\">\n {/* eslint-disable-next-line jsx-a11y/media-has-caption -- 학습 다시보기 오디오는 자막 트랙이 없는 사용자 녹음본 */}\n <audio\n controls\n onLoadedMetadata={() => {\n setAudioSec(audioRef.current?.duration ?? null);\n }}\n onTimeUpdate={handleTimeUpdate}\n ref={audioRef}\n src={resolvedAudioUrl}\n style={{ width: '100%' }}\n />\n </Box>\n </Tabs.Content>\n ) : null}\n </Tabs.Root>\n </Box>\n\n <ScriptTranscript\n canSync={canSync}\n currentMs={currentMs}\n lines={lines}\n onSeek={handleSeek}\n partnerName={detail.characterTitle}\n speechShare={speechShare}\n />\n </Grid>\n </Dialog.Content>\n </Dialog.Root>\n );\n}\n"],"mappings":"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","names":["_a","_b"]}
@@ -0,0 +1,161 @@
1
+ // src/app/roleplay/utils/session-utils.ts
2
+ import { format } from "date-fns";
3
+ var COLOR_VARS = {
4
+ green: {
5
+ fg: "var(--green-11)",
6
+ bg: "var(--green-a3)",
7
+ border: "var(--green-a6)"
8
+ },
9
+ amber: {
10
+ fg: "var(--amber-11)",
11
+ bg: "var(--amber-a3)",
12
+ border: "var(--amber-a6)"
13
+ },
14
+ red: { fg: "var(--red-11)", bg: "var(--red-a3)", border: "var(--red-a6)" },
15
+ gray: { fg: "var(--gray-9)", bg: "var(--gray-a3)", border: "var(--gray-5)" }
16
+ };
17
+ function qualityColor(quality) {
18
+ switch (quality) {
19
+ case "\uCDA9\uC871":
20
+ return "green";
21
+ case "\uBD80\uBD84\uCDA9\uC871":
22
+ return "amber";
23
+ case "\uBBF8\uD761":
24
+ return "red";
25
+ default:
26
+ return "gray";
27
+ }
28
+ }
29
+ var QUALITY_RATIO = {
30
+ \uCDA9\uC871: 1,
31
+ \uBD80\uBD84\uCDA9\uC871: 0.6,
32
+ \uBBF8\uD761: 0.3
33
+ };
34
+ function qualityRatio(quality) {
35
+ return quality === null ? null : QUALITY_RATIO[quality];
36
+ }
37
+ function gradeColorFromLabel(label) {
38
+ if (!label) return "gray";
39
+ if (label.includes("\uBBF8\uD761") || label.includes("\uBD80\uC871") || label.includes("\uBBF8\uB2EC"))
40
+ return "red";
41
+ if (label.includes("\uBD80\uBD84") || label.includes("\uBCF4\uD1B5")) return "amber";
42
+ if (label.includes("\uCDA9\uC871") || label.includes("\uC6B0\uC218") || label.includes("\uCDA9\uBD84"))
43
+ return "green";
44
+ return "gray";
45
+ }
46
+ function stepColor(step) {
47
+ return step.quality !== null ? qualityColor(step.quality) : gradeColorFromLabel(step.qualityLabel);
48
+ }
49
+ function stepVars(step) {
50
+ return COLOR_VARS[stepColor(step)];
51
+ }
52
+ function stepRatio(step) {
53
+ if (step.maxScore !== null && step.maxScore > 0 && step.score !== null) {
54
+ return Math.max(0, Math.min(1, step.score / step.maxScore));
55
+ }
56
+ return qualityRatio(step.quality);
57
+ }
58
+ function stepScoreText(step) {
59
+ if (step.score === null) return null;
60
+ return step.maxScore !== null ? `${step.score}/${step.maxScore}` : `${step.score}`;
61
+ }
62
+ function stepGradeLabel(step) {
63
+ return step.qualityLabel;
64
+ }
65
+ function hasStepSignal(step) {
66
+ return step.score !== null || step.qualityLabel !== null;
67
+ }
68
+ var LETTER_RE = /[A-Za-z]/;
69
+ function stepCode(step) {
70
+ if (step.initial !== null && step.initial.trim().length > 0) {
71
+ return step.initial.trim();
72
+ }
73
+ const label = step.label.trim();
74
+ const first = label.charAt(0);
75
+ const second = label.charAt(1);
76
+ if (LETTER_RE.test(first) && (second === " " || second === "\u2014" || second === "-")) {
77
+ return first.toUpperCase();
78
+ }
79
+ if (first === "[" && LETTER_RE.test(second) && label.charAt(2) === "]") {
80
+ return second.toUpperCase();
81
+ }
82
+ return step.key.split("_")[0].slice(0, 4).toUpperCase();
83
+ }
84
+ function formatDuration(seconds) {
85
+ if (seconds === null) return "\u2014";
86
+ const m = Math.floor(seconds / 60);
87
+ const s = seconds % 60;
88
+ return `${m}\uBD84 ${String(s).padStart(2, "0")}\uCD08`;
89
+ }
90
+ function formatDate(iso) {
91
+ const d = new Date(iso);
92
+ if (Number.isNaN(d.getTime())) return iso;
93
+ return format(d, "M/d HH:mm");
94
+ }
95
+ function formatTimestamp(ms) {
96
+ const total = Math.floor(ms / 1e3);
97
+ const m = Math.floor(total / 60);
98
+ const s = total % 60;
99
+ return `${m}:${String(s).padStart(2, "0")}`;
100
+ }
101
+ var SYNC_TOLERANCE = 0.05;
102
+ function isLengthClose(mediaSec, sttSec) {
103
+ if (!mediaSec || mediaSec <= 0 || sttSec <= 0) {
104
+ return false;
105
+ }
106
+ return Math.abs(mediaSec - sttSec) / Math.max(mediaSec, sttSec) <= SYNC_TOLERANCE;
107
+ }
108
+ var SPEECH_OVERTALK_THRESHOLD = 40;
109
+ function computeSpeechShare(lines) {
110
+ let userChars = 0;
111
+ let partnerChars = 0;
112
+ for (const line of lines) {
113
+ const count = line.text.replace(/\s/g, "").length;
114
+ if (line.speaker === "user") {
115
+ userChars += count;
116
+ } else {
117
+ partnerChars += count;
118
+ }
119
+ }
120
+ const total = userChars + partnerChars;
121
+ if (total === 0) {
122
+ return null;
123
+ }
124
+ const userPct = Math.round(userChars / total * 100);
125
+ return { userPct, partnerPct: 100 - userPct };
126
+ }
127
+ var STATUS_LABEL = {
128
+ complete: "\uBD84\uC11D \uC644\uB8CC",
129
+ completed: "\uBD84\uC11D \uC644\uB8CC",
130
+ analyzing: "AI \uBD84\uC11D \uC911",
131
+ partial: "\uC77C\uBD80 \uC0B0\uCD9C\uBB3C \uC5C6\uC74C"
132
+ };
133
+ function statusLabelOf(status) {
134
+ var _a;
135
+ if (!status) return "\uC138\uC158";
136
+ return (_a = STATUS_LABEL[status]) != null ? _a : status;
137
+ }
138
+
139
+ export {
140
+ COLOR_VARS,
141
+ qualityColor,
142
+ QUALITY_RATIO,
143
+ qualityRatio,
144
+ gradeColorFromLabel,
145
+ stepColor,
146
+ stepVars,
147
+ stepRatio,
148
+ stepScoreText,
149
+ stepGradeLabel,
150
+ hasStepSignal,
151
+ stepCode,
152
+ formatDuration,
153
+ formatDate,
154
+ formatTimestamp,
155
+ SYNC_TOLERANCE,
156
+ isLengthClose,
157
+ SPEECH_OVERTALK_THRESHOLD,
158
+ computeSpeechShare,
159
+ statusLabelOf
160
+ };
161
+ //# sourceMappingURL=chunk-4YAA25AV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app/roleplay/utils/session-utils.ts"],"sourcesContent":["// 롤플레잉 세션 상세 순수 유틸 함수 모음.\n// 원본: website/src/modules/customer/roleplay/pages/RpSessionDetail/RpSessionDetail.tsx\n// 'use client' 없음 — 순수 함수, 클라이언트 전용 아님.\n\nimport { format } from 'date-fns';\nimport type { RpSessionScriptLine } from '../types';\nimport type { RpQuality, RpReportStep } from './report-parse';\n\n/* ───────────────────────── 타입 ───────────────────────── */\n\nexport interface SpeechShare { userPct: number; partnerPct: number }\n\nexport type QColor = 'green' | 'amber' | 'red' | 'gray';\n\n/* ───────────────────────── 색 변수 ───────────────────────── */\n\nexport const COLOR_VARS: Record<QColor, { fg: string; bg: string; border: string }> = {\n green: {\n fg: 'var(--green-11)',\n bg: 'var(--green-a3)',\n border: 'var(--green-a6)',\n },\n amber: {\n fg: 'var(--amber-11)',\n bg: 'var(--amber-a3)',\n border: 'var(--amber-a6)',\n },\n red: { fg: 'var(--red-11)', bg: 'var(--red-a3)', border: 'var(--red-a6)' },\n gray: { fg: 'var(--gray-9)', bg: 'var(--gray-a3)', border: 'var(--gray-5)' },\n};\n\n/* ───────────────────────── 등급 색 ───────────────────────── */\n\nexport function qualityColor(quality: RpQuality | null): QColor {\n switch (quality) {\n case '충족':\n return 'green';\n case '부분충족':\n return 'amber';\n case '미흡':\n return 'red';\n default:\n return 'gray';\n }\n}\n\nexport const QUALITY_RATIO: Record<RpQuality, number> = {\n 충족: 1,\n 부분충족: 0.6,\n 미흡: 0.3,\n} as const;\n\nexport function qualityRatio(quality: RpQuality | null): number | null {\n return quality === null ? null : QUALITY_RATIO[quality];\n}\n\n// 표준 등급이 아닌 자유 등급 문자열에서 색만 키워드로 추정(비율은 산출하지 않음).\nexport function gradeColorFromLabel(label: string | null): QColor {\n if (!label) return 'gray';\n if (\n label.includes('미흡') ||\n label.includes('부족') ||\n label.includes('미달')\n )\n return 'red';\n if (label.includes('부분') || label.includes('보통')) return 'amber';\n if (\n label.includes('충족') ||\n label.includes('우수') ||\n label.includes('충분')\n )\n return 'green';\n return 'gray';\n}\n\n/* ───────────────────────── 단계(Step) 헬퍼 ───────────────────────── */\n\n// 단계 색: 표준 등급 우선, 없으면 라벨 키워드 추정.\nexport function stepColor(step: RpReportStep): QColor {\n return step.quality !== null\n ? qualityColor(step.quality)\n : gradeColorFromLabel(step.qualityLabel);\n}\n\nexport function stepVars(step: RpReportStep): { fg: string; bg: string; border: string } {\n return COLOR_VARS[stepColor(step)];\n}\n\n// progress/radar 정규화 비율(0~1). score/maxScore 우선, 없으면 표준 등급 비율, 둘 다 없으면 null.\nexport function stepRatio(step: RpReportStep): number | null {\n if (step.maxScore !== null && step.maxScore > 0 && step.score !== null) {\n return Math.max(0, Math.min(1, step.score / step.maxScore));\n }\n return qualityRatio(step.quality);\n}\n\n// 점수 텍스트: max 있으면 'score/max', 없으면 'score', score 없으면 null.\nexport function stepScoreText(step: RpReportStep): string | null {\n if (step.score === null) return null;\n return step.maxScore !== null\n ? `${step.score}/${step.maxScore}`\n : `${step.score}`;\n}\n\n// 표시할 등급 라벨(자유 문자열 포함). 없으면 null.\nexport function stepGradeLabel(step: RpReportStep): string | null {\n return step.qualityLabel;\n}\n\n// 단계에 표시할 평가 정보가 하나라도 있는지(점수 또는 등급).\nexport function hasStepSignal(step: RpReportStep): boolean {\n return step.score !== null || step.qualityLabel !== null;\n}\n\n// radar 축/요약 라벨용 짧은 코드.\n// displayMeta.steps.initial 이 지정돼 있으면 최우선(이니셜 충돌 해소).\n// 없으면 기존 휴리스틱: acronym 프레임워크는 선두 글자, 아니면 키 약식.\nconst LETTER_RE = /[A-Za-z]/;\n\nexport function stepCode(step: RpReportStep): string {\n if (step.initial !== null && step.initial.trim().length > 0) {\n return step.initial.trim();\n }\n const label = step.label.trim();\n const first = label.charAt(0);\n const second = label.charAt(1);\n // \"O — Opening\" / \"O - Opening\" / \"O Opening\" 형태\n if (\n LETTER_RE.test(first) &&\n (second === ' ' || second === '—' || second === '-')\n ) {\n return first.toUpperCase();\n }\n // \"[I] Introduction\" 형태\n if (first === '[' && LETTER_RE.test(second) && label.charAt(2) === ']') {\n return second.toUpperCase();\n }\n return step.key.split('_')[0].slice(0, 4).toUpperCase();\n}\n\n/* ───────────────────────── 포맷 유틸 ───────────────────────── */\n\nexport function formatDuration(seconds: number | null): string {\n if (seconds === null) return '—';\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${m}분 ${String(s).padStart(2, '0')}초`;\n}\n\nexport function formatDate(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return iso;\n return format(d, 'M/d HH:mm');\n}\n\nexport function formatTimestamp(ms: number): string {\n const total = Math.floor(ms / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\n/* ───────────────────────── 미디어 싱크 ───────────────────────── */\n\n// 미디어 길이와 STT 길이가 이 비율 이내로 가까우면 싱크(클릭 seek/재생 하이라이트) 활성.\nexport const SYNC_TOLERANCE = 0.05;\n\nexport function isLengthClose(mediaSec: number | null, sttSec: number): boolean {\n if (!mediaSec || mediaSec <= 0 || sttSec <= 0) {\n return false;\n }\n return (\n Math.abs(mediaSec - sttSec) / Math.max(mediaSec, sttSec) <= SYNC_TOLERANCE\n );\n}\n\n/* ───────────────────────── 발화량 ───────────────────────── */\n\n// 내 발화량이 이 비율을 넘으면 과다 발화로 간주(빨간 막대 + 주의 문구).\nexport const SPEECH_OVERTALK_THRESHOLD = 40;\n\n// 화자별 발화량 비율. 기준은 공백·개행 제거 글자 수(실제 말한 양에 근접, 공백 패턴 왜곡 방지).\n// 발화 글자가 전혀 없으면 null → 미표시.\nexport function computeSpeechShare(lines: RpSessionScriptLine[]): SpeechShare | null {\n let userChars = 0;\n let partnerChars = 0;\n for (const line of lines) {\n const count = line.text.replace(/\\s/g, '').length;\n if (line.speaker === 'user') {\n userChars += count;\n } else {\n partnerChars += count;\n }\n }\n const total = userChars + partnerChars;\n if (total === 0) {\n return null;\n }\n const userPct = Math.round((userChars / total) * 100);\n return { userPct, partnerPct: 100 - userPct };\n}\n\n/* ───────────────────────── 세션 상태 ───────────────────────── */\n\nconst STATUS_LABEL: Record<string, string> = {\n complete: '분석 완료',\n completed: '분석 완료',\n analyzing: 'AI 분석 중',\n partial: '일부 산출물 없음',\n};\n\nexport function statusLabelOf(status: string | null): string {\n if (!status) return '세션';\n return STATUS_LABEL[status] ?? status;\n}\n"],"mappings":"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","names":[]}