@things-factory/learning 0.0.0-stage → 10.1.261

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 (167) hide show
  1. package/dist-client/bootstrap.d.ts +2 -0
  2. package/dist-client/bootstrap.js +4 -0
  3. package/dist-client/bootstrap.js.map +1 -0
  4. package/dist-client/components/learning-assign-form.d.ts +24 -0
  5. package/dist-client/components/learning-assign-form.js +77 -0
  6. package/dist-client/components/learning-assign-form.js.map +1 -0
  7. package/dist-client/components/learning-course-form.d.ts +29 -0
  8. package/dist-client/components/learning-course-form.js +104 -0
  9. package/dist-client/components/learning-course-form.js.map +1 -0
  10. package/dist-client/components/learning-material-form.d.ts +41 -0
  11. package/dist-client/components/learning-material-form.js +202 -0
  12. package/dist-client/components/learning-material-form.js.map +1 -0
  13. package/dist-client/components/learning-question-form.d.ts +33 -0
  14. package/dist-client/components/learning-question-form.js +142 -0
  15. package/dist-client/components/learning-question-form.js.map +1 -0
  16. package/dist-client/components/learning-todo.d.ts +12 -0
  17. package/dist-client/components/learning-todo.js +43 -0
  18. package/dist-client/components/learning-todo.js.map +1 -0
  19. package/dist-client/components/learning-unit-form.d.ts +44 -0
  20. package/dist-client/components/learning-unit-form.js +151 -0
  21. package/dist-client/components/learning-unit-form.js.map +1 -0
  22. package/dist-client/index.d.ts +2 -0
  23. package/dist-client/index.js +3 -0
  24. package/dist-client/index.js.map +1 -0
  25. package/dist-client/pages/learning-course-editor.d.ts +63 -0
  26. package/dist-client/pages/learning-course-editor.js +476 -0
  27. package/dist-client/pages/learning-course-editor.js.map +1 -0
  28. package/dist-client/pages/learning-course-list.d.ts +37 -0
  29. package/dist-client/pages/learning-course-list.js +92 -0
  30. package/dist-client/pages/learning-course-list.js.map +1 -0
  31. package/dist-client/pages/learning-enrollment-status.d.ts +65 -0
  32. package/dist-client/pages/learning-enrollment-status.js +211 -0
  33. package/dist-client/pages/learning-enrollment-status.js.map +1 -0
  34. package/dist-client/pages/learning-view.d.ts +63 -0
  35. package/dist-client/pages/learning-view.js +446 -0
  36. package/dist-client/pages/learning-view.js.map +1 -0
  37. package/dist-client/pages/my-learning.d.ts +22 -0
  38. package/dist-client/pages/my-learning.js +110 -0
  39. package/dist-client/pages/my-learning.js.map +1 -0
  40. package/dist-client/route.d.ts +1 -0
  41. package/dist-client/route.js +21 -0
  42. package/dist-client/route.js.map +1 -0
  43. package/dist-client/tsconfig.tsbuildinfo +1 -0
  44. package/dist-client/utils/learning-api.d.ts +61 -0
  45. package/dist-client/utils/learning-api.js +85 -0
  46. package/dist-client/utils/learning-api.js.map +1 -0
  47. package/dist-client/utils/learning-words.d.ts +20 -0
  48. package/dist-client/utils/learning-words.js +22 -0
  49. package/dist-client/utils/learning-words.js.map +1 -0
  50. package/dist-client/utils/material-source.d.ts +12 -0
  51. package/dist-client/utils/material-source.js +77 -0
  52. package/dist-client/utils/material-source.js.map +1 -0
  53. package/dist-server/activities.d.ts +38 -0
  54. package/dist-server/activities.js +42 -0
  55. package/dist-server/activities.js.map +1 -0
  56. package/dist-server/ai-reads.d.ts +9 -0
  57. package/dist-server/ai-reads.js +70 -0
  58. package/dist-server/ai-reads.js.map +1 -0
  59. package/dist-server/attachment-reader.d.ts +1 -0
  60. package/dist-server/attachment-reader.js +23 -0
  61. package/dist-server/attachment-reader.js.map +1 -0
  62. package/dist-server/index.d.ts +4 -0
  63. package/dist-server/index.js +33 -0
  64. package/dist-server/index.js.map +1 -0
  65. package/dist-server/learning-subjects.d.ts +10 -0
  66. package/dist-server/learning-subjects.js +31 -0
  67. package/dist-server/learning-subjects.js.map +1 -0
  68. package/dist-server/role-tasks.d.ts +2 -0
  69. package/dist-server/role-tasks.js +17 -0
  70. package/dist-server/role-tasks.js.map +1 -0
  71. package/dist-server/service/course/course-mutation.d.ts +19 -0
  72. package/dist-server/service/course/course-mutation.js +177 -0
  73. package/dist-server/service/course/course-mutation.js.map +1 -0
  74. package/dist-server/service/course/course-query.d.ts +27 -0
  75. package/dist-server/service/course/course-query.js +183 -0
  76. package/dist-server/service/course/course-query.js.map +1 -0
  77. package/dist-server/service/course/course-question.d.ts +41 -0
  78. package/dist-server/service/course/course-question.js +116 -0
  79. package/dist-server/service/course/course-question.js.map +1 -0
  80. package/dist-server/service/course/course-rules.d.ts +51 -0
  81. package/dist-server/service/course/course-rules.js +93 -0
  82. package/dist-server/service/course/course-rules.js.map +1 -0
  83. package/dist-server/service/course/course-service.d.ts +72 -0
  84. package/dist-server/service/course/course-service.js +398 -0
  85. package/dist-server/service/course/course-service.js.map +1 -0
  86. package/dist-server/service/course/course-type.d.ts +56 -0
  87. package/dist-server/service/course/course-type.js +202 -0
  88. package/dist-server/service/course/course-type.js.map +1 -0
  89. package/dist-server/service/course/course-unit.d.ts +37 -0
  90. package/dist-server/service/course/course-unit.js +128 -0
  91. package/dist-server/service/course/course-unit.js.map +1 -0
  92. package/dist-server/service/course/course-version.d.ts +45 -0
  93. package/dist-server/service/course/course-version.js +151 -0
  94. package/dist-server/service/course/course-version.js.map +1 -0
  95. package/dist-server/service/course/course.d.ts +23 -0
  96. package/dist-server/service/course/course.js +92 -0
  97. package/dist-server/service/course/course.js.map +1 -0
  98. package/dist-server/service/course/index.d.ts +15 -0
  99. package/dist-server/service/course/index.js +25 -0
  100. package/dist-server/service/course/index.js.map +1 -0
  101. package/dist-server/service/course/material-audio.d.ts +1 -0
  102. package/dist-server/service/course/material-audio.js +43 -0
  103. package/dist-server/service/course/material-audio.js.map +1 -0
  104. package/dist-server/service/course/material-draft-mutation.d.ts +13 -0
  105. package/dist-server/service/course/material-draft-mutation.js +55 -0
  106. package/dist-server/service/course/material-draft-mutation.js.map +1 -0
  107. package/dist-server/service/course/material-draft-rules.d.ts +38 -0
  108. package/dist-server/service/course/material-draft-rules.js +83 -0
  109. package/dist-server/service/course/material-draft-rules.js.map +1 -0
  110. package/dist-server/service/course/material-draft-service.d.ts +15 -0
  111. package/dist-server/service/course/material-draft-service.js +162 -0
  112. package/dist-server/service/course/material-draft-service.js.map +1 -0
  113. package/dist-server/service/enrollment/enrollment-mutation.d.ts +11 -0
  114. package/dist-server/service/enrollment/enrollment-mutation.js +124 -0
  115. package/dist-server/service/enrollment/enrollment-mutation.js.map +1 -0
  116. package/dist-server/service/enrollment/enrollment-query.d.ts +24 -0
  117. package/dist-server/service/enrollment/enrollment-query.js +236 -0
  118. package/dist-server/service/enrollment/enrollment-query.js.map +1 -0
  119. package/dist-server/service/enrollment/enrollment-remind.d.ts +1 -0
  120. package/dist-server/service/enrollment/enrollment-remind.js +45 -0
  121. package/dist-server/service/enrollment/enrollment-remind.js.map +1 -0
  122. package/dist-server/service/enrollment/enrollment-service.d.ts +110 -0
  123. package/dist-server/service/enrollment/enrollment-service.js +427 -0
  124. package/dist-server/service/enrollment/enrollment-service.js.map +1 -0
  125. package/dist-server/service/enrollment/enrollment-status.d.ts +53 -0
  126. package/dist-server/service/enrollment/enrollment-status.js +194 -0
  127. package/dist-server/service/enrollment/enrollment-status.js.map +1 -0
  128. package/dist-server/service/enrollment/enrollment-type.d.ts +82 -0
  129. package/dist-server/service/enrollment/enrollment-type.js +293 -0
  130. package/dist-server/service/enrollment/enrollment-type.js.map +1 -0
  131. package/dist-server/service/enrollment/enrollment.d.ts +53 -0
  132. package/dist-server/service/enrollment/enrollment.js +191 -0
  133. package/dist-server/service/enrollment/enrollment.js.map +1 -0
  134. package/dist-server/service/enrollment/index.d.ts +15 -0
  135. package/dist-server/service/enrollment/index.js +26 -0
  136. package/dist-server/service/enrollment/index.js.map +1 -0
  137. package/dist-server/service/enrollment/learning-ai.d.ts +45 -0
  138. package/dist-server/service/enrollment/learning-ai.js +93 -0
  139. package/dist-server/service/enrollment/learning-ai.js.map +1 -0
  140. package/dist-server/service/enrollment/learning-documents.d.ts +7 -0
  141. package/dist-server/service/enrollment/learning-documents.js +159 -0
  142. package/dist-server/service/enrollment/learning-documents.js.map +1 -0
  143. package/dist-server/service/enrollment/learning-outcome.d.ts +10 -0
  144. package/dist-server/service/enrollment/learning-outcome.js +16 -0
  145. package/dist-server/service/enrollment/learning-outcome.js.map +1 -0
  146. package/dist-server/service/enrollment/progress-rules.d.ts +119 -0
  147. package/dist-server/service/enrollment/progress-rules.js +132 -0
  148. package/dist-server/service/enrollment/progress-rules.js.map +1 -0
  149. package/dist-server/service/enrollment/quiz-attempt.d.ts +34 -0
  150. package/dist-server/service/enrollment/quiz-attempt.js +105 -0
  151. package/dist-server/service/enrollment/quiz-attempt.js.map +1 -0
  152. package/dist-server/service/enrollment/unit-progress.d.ts +34 -0
  153. package/dist-server/service/enrollment/unit-progress.js +134 -0
  154. package/dist-server/service/enrollment/unit-progress.js.map +1 -0
  155. package/dist-server/service/index.d.ts +14 -0
  156. package/dist-server/service/index.js +43 -0
  157. package/dist-server/service/index.js.map +1 -0
  158. package/dist-server/tsconfig.tsbuildinfo +1 -0
  159. package/package.json +57 -4
  160. package/things-factory.config.js +14 -0
  161. package/translations/en.json +278 -0
  162. package/translations/es.json +278 -0
  163. package/translations/ja.json +278 -0
  164. package/translations/ko.json +278 -0
  165. package/translations/ms.json +278 -0
  166. package/translations/zh.json +278 -0
  167. package/README.md +0 -3
@@ -0,0 +1,2 @@
1
+ import './components/learning-todo.js';
2
+ export default function bootstrap(): void;
@@ -0,0 +1,4 @@
1
+ import './components/learning-todo.js';
2
+ /* The to-do's element registers itself on load - the pages come in through the route when an application puts them in its menu. */
3
+ export default function bootstrap() { }
4
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../client/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,+BAA+B,CAAA;AAEtC,mIAAmI;AACnI,MAAM,CAAC,OAAO,UAAU,SAAS,KAAI,CAAC","sourcesContent":["import './components/learning-todo.js'\n\n/* The to-do's element registers itself on load - the pages come in through the route when an application puts them in its menu. */\nexport default function bootstrap() {}\n"]}
@@ -0,0 +1,24 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import '@material/web/button/text-button.js';
3
+ import '@operato/popup/ox-popup-actions.js';
4
+ import '@things-factory/announcement/dist-client';
5
+ import { LitElement } from 'lit';
6
+ /** Opens the assignment form of a course. Resolves with how many enrolments it holds now, or null. */
7
+ export declare function openAssignForm(course: {
8
+ id: string;
9
+ name: string;
10
+ }): Promise<number | null>;
11
+ declare const LearningAssignForm_base: (new (...args: any[]) => LitElement) & typeof LitElement;
12
+ export declare class LearningAssignForm extends LearningAssignForm_base {
13
+ static styles: import("lit").CSSResult[];
14
+ course: {
15
+ id: string;
16
+ name: string;
17
+ };
18
+ private audiences;
19
+ private dueOn;
20
+ private assign;
21
+ render(): import("lit-html").TemplateResult<1>;
22
+ private submit;
23
+ }
24
+ export {};
@@ -0,0 +1,77 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/button/filled-button.js';
3
+ import '@material/web/button/text-button.js';
4
+ import '@operato/popup/ox-popup-actions.js';
5
+ import '@things-factory/announcement/dist-client';
6
+ import gql from 'graphql-tag';
7
+ import { html, LitElement, nothing } from 'lit';
8
+ import { customElement, property, state } from 'lit/decorators.js';
9
+ import { MutationController } from '@operato/graphql';
10
+ import { i18next, localize } from '@operato/i18n';
11
+ import { openPopup } from '@operato/layout';
12
+ import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles';
13
+ import { t } from '../utils/learning-words.js';
14
+ /** Opens the assignment form of a course. Resolves with how many enrolments it holds now, or null. */
15
+ export function openAssignForm(course) {
16
+ const popup = openPopup(html `<learning-assign-form .course=${course} @assigned=${(e) => popup.close(e.detail)} @close=${() => popup.close()}></learning-assign-form>`, { backdrop: true, size: 'medium', title: t('title.learning-assign', { course: course.name }) });
17
+ return popup.result.then(value => (value ?? null));
18
+ }
19
+ /*
20
+ * 수강 배정 (ADR-0194 decision 7) - whom a course is assigned to, by the one audience rule (the domain, departments with
21
+ * those under them, roles and people - ADR-0163), and the day it is due. Each person gets a to-do.
22
+ */
23
+ let LearningAssignForm = class LearningAssignForm extends localize(i18next)(LitElement) {
24
+ constructor() {
25
+ super(...arguments);
26
+ this.course = { id: '', name: '' };
27
+ this.audiences = [];
28
+ this.dueOn = '';
29
+ this.assign = new MutationController(this, gql `mutation ($courseId: String!, $audiences: [LearningAudienceInput!]!, $dueOn: String) { assignCourse(courseId: $courseId, audiences: $audiences, dueOn: $dueOn) { id } }`, { showsError: true });
30
+ }
31
+ static { this.styles = [CommonPopupStyles, PopupBodyStyles, FormFieldStyles, PopupFieldStyles]; }
32
+ render() {
33
+ const error = this.assign.error;
34
+ return html `
35
+ <div class="body">
36
+ <div class="field">
37
+ <span>${t('field.learning-assign-audience')}</span>
38
+ <announcement-audience-picker .value=${this.audiences} @change=${(e) => Array.isArray(e.detail) && (this.audiences = e.detail)}></announcement-audience-picker>
39
+ <span>${t('text.learning-assign-audience-hint')}</span>
40
+ </div>
41
+ <label class="field">
42
+ <span>${t('field.learning-due-on')}</span>
43
+ <input type="date" .value=${this.dueOn} @input=${(e) => (this.dueOn = e.target.value)} />
44
+ <span>${t('text.learning-due-on-hint')}</span>
45
+ </label>
46
+ ${error ? html `<div class="field"><span invalid role="alert">${error.graphQLErrors?.[0]?.message ?? error.message}</span></div>` : nothing}
47
+ </div>
48
+ <ox-popup-actions>
49
+ <md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>
50
+ <md-filled-button slot="confirm" ?disabled=${!this.audiences.length || this.assign.pending} @click=${() => this.submit()}>${t('button.learning-assign')}</md-filled-button>
51
+ </ox-popup-actions>
52
+ `;
53
+ }
54
+ async submit() {
55
+ const audiences = this.audiences.map(({ scope, targetId, targetName }) => ({ scope, targetId, targetName }));
56
+ const done = await this.assign.run({ courseId: this.course.id, audiences, dueOn: this.dueOn || null });
57
+ if (done)
58
+ this.dispatchEvent(new CustomEvent('assigned', { detail: done.assignCourse.length }));
59
+ }
60
+ };
61
+ __decorate([
62
+ property({ attribute: false }),
63
+ __metadata("design:type", Object)
64
+ ], LearningAssignForm.prototype, "course", void 0);
65
+ __decorate([
66
+ state(),
67
+ __metadata("design:type", Array)
68
+ ], LearningAssignForm.prototype, "audiences", void 0);
69
+ __decorate([
70
+ state(),
71
+ __metadata("design:type", Object)
72
+ ], LearningAssignForm.prototype, "dueOn", void 0);
73
+ LearningAssignForm = __decorate([
74
+ customElement('learning-assign-form')
75
+ ], LearningAssignForm);
76
+ export { LearningAssignForm };
77
+ //# sourceMappingURL=learning-assign-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning-assign-form.js","sourceRoot":"","sources":["../../client/components/learning-assign-form.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@operato/popup/ox-popup-actions.js'\nimport '@things-factory/announcement/dist-client'\n\nimport gql from 'graphql-tag'\nimport { html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { MutationController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { t } from '../utils/learning-words.js'\n\n/** Opens the assignment form of a course. Resolves with how many enrolments it holds now, or null. */\nexport function openAssignForm(course: { id: string; name: string }): Promise<number | null> {\n const popup = openPopup(html`<learning-assign-form .course=${course} @assigned=${(e: CustomEvent) => popup.close(e.detail)} @close=${() => popup.close()}></learning-assign-form>`, { backdrop: true, size: 'medium', title: t('title.learning-assign', { course: course.name }) })\n return popup.result.then(value => (value ?? null) as any)\n}\n\n/*\n * 수강 배정 (ADR-0194 decision 7) - whom a course is assigned to, by the one audience rule (the domain, departments with\n * those under them, roles and people - ADR-0163), and the day it is due. Each person gets a to-do.\n */\n@customElement('learning-assign-form')\nexport class LearningAssignForm extends localize(i18next)(LitElement) {\n static styles = [CommonPopupStyles, PopupBodyStyles, FormFieldStyles, PopupFieldStyles]\n\n @property({ attribute: false }) course: { id: string; name: string } = { id: '', name: '' }\n @state() private audiences: { scope: string; targetId: string; targetName?: string }[] = []\n @state() private dueOn = ''\n\n private assign = new MutationController(this, gql`mutation ($courseId: String!, $audiences: [LearningAudienceInput!]!, $dueOn: String) { assignCourse(courseId: $courseId, audiences: $audiences, dueOn: $dueOn) { id } }`, { showsError: true })\n\n render() {\n const error: any = this.assign.error\n return html`\n <div class=\"body\">\n <div class=\"field\">\n <span>${t('field.learning-assign-audience')}</span>\n <announcement-audience-picker .value=${this.audiences} @change=${(e: CustomEvent) => Array.isArray(e.detail) && (this.audiences = e.detail)}></announcement-audience-picker>\n <span>${t('text.learning-assign-audience-hint')}</span>\n </div>\n <label class=\"field\">\n <span>${t('field.learning-due-on')}</span>\n <input type=\"date\" .value=${this.dueOn} @input=${(e: any) => (this.dueOn = e.target.value)} />\n <span>${t('text.learning-due-on-hint')}</span>\n </label>\n ${error ? html`<div class=\"field\"><span invalid role=\"alert\">${error.graphQLErrors?.[0]?.message ?? error.message}</span></div>` : nothing}\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${!this.audiences.length || this.assign.pending} @click=${() => this.submit()}>${t('button.learning-assign')}</md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async submit() {\n const audiences = this.audiences.map(({ scope, targetId, targetName }) => ({ scope, targetId, targetName }))\n const done = await this.assign.run({ courseId: this.course.id, audiences, dueOn: this.dueOn || null })\n if (done) this.dispatchEvent(new CustomEvent('assigned', { detail: done.assignCourse.length }))\n }\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import '@material/web/button/text-button.js';
3
+ import '@operato/input/ox-input-select-buttons.js';
4
+ import '@operato/popup/ox-popup-actions.js';
5
+ import { LitElement } from 'lit';
6
+ type CourseShape = {
7
+ id?: string;
8
+ name?: string;
9
+ category?: string | null;
10
+ description?: string | null;
11
+ };
12
+ /** Opens the course form - a new course, or what one says changed. Resolves with the course saved, or null. */
13
+ export declare function openCourseForm(course?: CourseShape): Promise<{
14
+ id: string;
15
+ } | null>;
16
+ declare const LearningCourseForm_base: (new (...args: any[]) => LitElement) & typeof LitElement;
17
+ export declare class LearningCourseForm extends LearningCourseForm_base {
18
+ static styles: import("lit").CSSResult[];
19
+ course: CourseShape;
20
+ private name;
21
+ private category;
22
+ private description;
23
+ private save;
24
+ private change;
25
+ willUpdate(changed: Map<string, unknown>): void;
26
+ render(): import("lit-html").TemplateResult<1>;
27
+ private submit;
28
+ }
29
+ export {};
@@ -0,0 +1,104 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/button/filled-button.js';
3
+ import '@material/web/button/text-button.js';
4
+ import '@operato/input/ox-input-select-buttons.js';
5
+ import '@operato/popup/ox-popup-actions.js';
6
+ import gql from 'graphql-tag';
7
+ import { css, html, LitElement, nothing } from 'lit';
8
+ import { customElement, property, state } from 'lit/decorators.js';
9
+ import { MutationController } from '@operato/graphql';
10
+ import { i18next, localize } from '@operato/i18n';
11
+ import { openPopup } from '@operato/layout';
12
+ import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles';
13
+ import { CATEGORIES, categoryWord, t } from '../utils/learning-words.js';
14
+ /** Opens the course form - a new course, or what one says changed. Resolves with the course saved, or null. */
15
+ export function openCourseForm(course = {}) {
16
+ const popup = openPopup(html `<learning-course-form .course=${course} @saved=${(e) => popup.close(e.detail)} @close=${() => popup.close()}></learning-course-form>`, { backdrop: true, size: 'medium', title: course.id ? t('title.learning-course-edit') : t('title.learning-course-new') });
17
+ return popup.result.then(value => (value ?? null));
18
+ }
19
+ /* 과정 (ADR-0194 decision 2) - its name, category and description. The house shape of a popup: only the body scrolls, labels above native inputs, one action row. */
20
+ let LearningCourseForm = class LearningCourseForm extends localize(i18next)(LitElement) {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.course = {};
24
+ this.name = '';
25
+ this.category = 'MANDATORY';
26
+ this.description = '';
27
+ this.save = new MutationController(this, gql `mutation ($course: NewCourse!) { createCourse(course: $course) { id } }`, { showsError: true });
28
+ this.change = new MutationController(this, gql `mutation ($id: String!, $patch: CoursePatch!) { updateCourse(id: $id, patch: $patch) { id } }`, { showsError: true });
29
+ }
30
+ static { this.styles = [
31
+ CommonPopupStyles,
32
+ PopupBodyStyles,
33
+ FormFieldStyles,
34
+ PopupFieldStyles,
35
+ css `
36
+ ox-input-select-buttons {
37
+ align-self: flex-start;
38
+ }
39
+ textarea {
40
+ min-height: 120px;
41
+ resize: vertical;
42
+ }
43
+ `
44
+ ]; }
45
+ willUpdate(changed) {
46
+ if (changed.has('course')) {
47
+ this.name = this.course.name ?? '';
48
+ this.category = this.course.category ?? 'MANDATORY';
49
+ this.description = this.course.description ?? '';
50
+ }
51
+ }
52
+ render() {
53
+ const pending = this.save.pending || this.change.pending;
54
+ const error = this.save.error || this.change.error;
55
+ return html `
56
+ <div class="body">
57
+ <label class="field">
58
+ <span>${t('field.learning-course-name')}</span>
59
+ <input .value=${this.name} maxlength="200" required @input=${(e) => (this.name = e.target.value)} />
60
+ </label>
61
+ <div class="field">
62
+ <span>${t('field.learning-course-category')}</span>
63
+ <ox-input-select-buttons .value=${this.category} .options=${CATEGORIES.map(code => ({ display: categoryWord(code), value: code }))} @change=${(e) => e.detail && (this.category = e.detail)}></ox-input-select-buttons>
64
+ </div>
65
+ <label class="field">
66
+ <span>${t('field.learning-course-description')}</span>
67
+ <textarea .value=${this.description} @input=${(e) => (this.description = e.target.value)}></textarea>
68
+ </label>
69
+ ${error ? html `<div class="field"><span invalid role="alert">${error.graphQLErrors?.[0]?.message ?? error.message}</span></div>` : nothing}
70
+ </div>
71
+ <ox-popup-actions>
72
+ <md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>
73
+ <md-filled-button slot="confirm" ?disabled=${!this.name.trim() || pending} @click=${() => this.submit()}>${t('button.save')}</md-filled-button>
74
+ </ox-popup-actions>
75
+ `;
76
+ }
77
+ async submit() {
78
+ const fields = { name: this.name.trim(), category: this.category, description: this.description.trim() || null };
79
+ const done = this.course.id ? (await this.change.run({ id: this.course.id, patch: fields }))?.updateCourse : (await this.save.run({ course: fields }))?.createCourse;
80
+ if (done)
81
+ this.dispatchEvent(new CustomEvent('saved', { detail: done }));
82
+ }
83
+ };
84
+ __decorate([
85
+ property({ attribute: false }),
86
+ __metadata("design:type", Object)
87
+ ], LearningCourseForm.prototype, "course", void 0);
88
+ __decorate([
89
+ state(),
90
+ __metadata("design:type", Object)
91
+ ], LearningCourseForm.prototype, "name", void 0);
92
+ __decorate([
93
+ state(),
94
+ __metadata("design:type", Object)
95
+ ], LearningCourseForm.prototype, "category", void 0);
96
+ __decorate([
97
+ state(),
98
+ __metadata("design:type", Object)
99
+ ], LearningCourseForm.prototype, "description", void 0);
100
+ LearningCourseForm = __decorate([
101
+ customElement('learning-course-form')
102
+ ], LearningCourseForm);
103
+ export { LearningCourseForm };
104
+ //# sourceMappingURL=learning-course-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning-course-form.js","sourceRoot":"","sources":["../../client/components/learning-course-form.ts"],"names":[],"mappings":";AAAA,OAAO,uCAAuC,CAAA;AAC9C,OAAO,qCAAqC,CAAA;AAC5C,OAAO,2CAA2C,CAAA;AAClD,OAAO,oCAAoC,CAAA;AAE3C,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEvG,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,4BAA4B,CAAA;AAIxE,+GAA+G;AAC/G,MAAM,UAAU,cAAc,CAAC,SAAsB,EAAE;IACrD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAA,iCAAiC,MAAM,WAAW,CAAC,CAAc,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,0BAA0B,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAA;IACzS,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAQ,CAAC,CAAA;AAC3D,CAAC;AAED,mKAAmK;AAE5J,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAA9D;;QAiB2B,WAAM,GAAgB,EAAE,CAAA;QACvC,SAAI,GAAG,EAAE,CAAA;QACT,aAAQ,GAAG,WAAW,CAAA;QACtB,gBAAW,GAAG,EAAE,CAAA;QAEzB,SAAI,GAAG,IAAI,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAA,yEAAyE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;QACvI,WAAM,GAAG,IAAI,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAA,+FAA+F,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;IAyCzK,CAAC;aA/DQ,WAAM,GAAG;QACd,iBAAiB;QACjB,eAAe;QACf,eAAe;QACf,gBAAgB;QAChB,GAAG,CAAA;;;;;;;;KAQF;KACF,AAdY,CAcZ;IAUD,UAAU,CAAC,OAA6B;QACtC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAA;YAClC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,WAAW,CAAA;YACnD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAA;QAClD,CAAC;IACH,CAAC;IAED,MAAM;QACJ,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA;QACxD,MAAM,KAAK,GAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAA;QACvD,OAAO,IAAI,CAAA;;;kBAGG,CAAC,CAAC,4BAA4B,CAAC;0BACvB,IAAI,CAAC,IAAI,oCAAoC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;;;kBAG7F,CAAC,CAAC,gCAAgC,CAAC;4CACT,IAAI,CAAC,QAAQ,aAAa,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC;;;kBAGhM,CAAC,CAAC,mCAAmC,CAAC;6BAC3B,IAAI,CAAC,WAAW,WAAW,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;;UAE7F,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,iDAAiD,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,OAAO;;;+CAGnG,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;qDAClE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,OAAO,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,aAAa,CAAC;;KAE9H,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAA;QAChH,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,YAAY,CAAA;QACpK,IAAI,IAAI;YAAE,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC1E,CAAC;;AA9C+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;kDAAyB;AACvC;IAAhB,KAAK,EAAE;;gDAAkB;AACT;IAAhB,KAAK,EAAE;;oDAA+B;AACtB;IAAhB,KAAK,EAAE;;uDAAyB;AApBtB,kBAAkB;IAD9B,aAAa,CAAC,sBAAsB,CAAC;GACzB,kBAAkB,CAgE9B","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@operato/input/ox-input-select-buttons.js'\nimport '@operato/popup/ox-popup-actions.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { MutationController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { CATEGORIES, categoryWord, t } from '../utils/learning-words.js'\n\ntype CourseShape = { id?: string; name?: string; category?: string | null; description?: string | null }\n\n/** Opens the course form - a new course, or what one says changed. Resolves with the course saved, or null. */\nexport function openCourseForm(course: CourseShape = {}): Promise<{ id: string } | null> {\n const popup = openPopup(html`<learning-course-form .course=${course} @saved=${(e: CustomEvent) => popup.close(e.detail)} @close=${() => popup.close()}></learning-course-form>`, { backdrop: true, size: 'medium', title: course.id ? t('title.learning-course-edit') : t('title.learning-course-new') })\n return popup.result.then(value => (value ?? null) as any)\n}\n\n/* 과정 (ADR-0194 decision 2) - its name, category and description. The house shape of a popup: only the body scrolls, labels above native inputs, one action row. */\n@customElement('learning-course-form')\nexport class LearningCourseForm extends localize(i18next)(LitElement) {\n static styles = [\n CommonPopupStyles,\n PopupBodyStyles,\n FormFieldStyles,\n PopupFieldStyles,\n css`\n ox-input-select-buttons {\n align-self: flex-start;\n }\n textarea {\n min-height: 120px;\n resize: vertical;\n }\n `\n ]\n\n @property({ attribute: false }) course: CourseShape = {}\n @state() private name = ''\n @state() private category = 'MANDATORY'\n @state() private description = ''\n\n private save = new MutationController(this, gql`mutation ($course: NewCourse!) { createCourse(course: $course) { id } }`, { showsError: true })\n private change = new MutationController(this, gql`mutation ($id: String!, $patch: CoursePatch!) { updateCourse(id: $id, patch: $patch) { id } }`, { showsError: true })\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('course')) {\n this.name = this.course.name ?? ''\n this.category = this.course.category ?? 'MANDATORY'\n this.description = this.course.description ?? ''\n }\n }\n\n render() {\n const pending = this.save.pending || this.change.pending\n const error: any = this.save.error || this.change.error\n return html`\n <div class=\"body\">\n <label class=\"field\">\n <span>${t('field.learning-course-name')}</span>\n <input .value=${this.name} maxlength=\"200\" required @input=${(e: any) => (this.name = e.target.value)} />\n </label>\n <div class=\"field\">\n <span>${t('field.learning-course-category')}</span>\n <ox-input-select-buttons .value=${this.category} .options=${CATEGORIES.map(code => ({ display: categoryWord(code), value: code }))} @change=${(e: CustomEvent) => e.detail && (this.category = e.detail)}></ox-input-select-buttons>\n </div>\n <label class=\"field\">\n <span>${t('field.learning-course-description')}</span>\n <textarea .value=${this.description} @input=${(e: any) => (this.description = e.target.value)}></textarea>\n </label>\n ${error ? html`<div class=\"field\"><span invalid role=\"alert\">${error.graphQLErrors?.[0]?.message ?? error.message}</span></div>` : nothing}\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${!this.name.trim() || pending} @click=${() => this.submit()}>${t('button.save')}</md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async submit() {\n const fields = { name: this.name.trim(), category: this.category, description: this.description.trim() || null }\n const done = this.course.id ? (await this.change.run({ id: this.course.id, patch: fields }))?.updateCourse : (await this.save.run({ course: fields }))?.createCourse\n if (done) this.dispatchEvent(new CustomEvent('saved', { detail: done }))\n }\n}\n"]}
@@ -0,0 +1,41 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import '@material/web/button/text-button.js';
3
+ import '@operato/popup/ox-popup-actions.js';
4
+ import { LitElement } from 'lit';
5
+ type SourceUnit = {
6
+ id: string;
7
+ kind: string;
8
+ name: string;
9
+ file?: {
10
+ id: string;
11
+ name: string;
12
+ fullpath: string;
13
+ };
14
+ };
15
+ export declare function openMaterialForm(units: SourceUnit[]): Promise<boolean>;
16
+ declare const LearningMaterialForm_base: (new (...args: any[]) => LitElement) & typeof LitElement;
17
+ export declare class LearningMaterialForm extends LearningMaterialForm_base {
18
+ static styles: import("lit").CSSResult[];
19
+ units: SourceUnit[];
20
+ private unitId;
21
+ private instruction;
22
+ private captions;
23
+ private busy;
24
+ private error;
25
+ private proposal;
26
+ private source?;
27
+ private controller?;
28
+ private generation;
29
+ private transcribe;
30
+ private propose;
31
+ private apply;
32
+ willUpdate(): void;
33
+ disconnectedCallback(): void;
34
+ private get selected();
35
+ render(): import("lit-html").TemplateResult<1>;
36
+ private readCaptions;
37
+ private message;
38
+ private generate;
39
+ private save;
40
+ }
41
+ export {};
@@ -0,0 +1,202 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/button/filled-button.js';
3
+ import '@material/web/button/text-button.js';
4
+ import '@operato/popup/ox-popup-actions.js';
5
+ import gql from 'graphql-tag';
6
+ import { css, html, LitElement, nothing } from 'lit';
7
+ import { customElement, property, state } from 'lit/decorators.js';
8
+ import { MutationController } from '@operato/graphql';
9
+ import { i18next, localize } from '@operato/i18n';
10
+ import { openPopup } from '@operato/layout';
11
+ import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles';
12
+ import { captionParts, pdfMaterial } from '../utils/material-source.js';
13
+ import { t } from '../utils/learning-words.js';
14
+ export function openMaterialForm(units) {
15
+ const popup = openPopup(html `<learning-material-form .units=${units} @done=${() => popup.close(true)} @close=${() => popup.close()}></learning-material-form>`, { backdrop: true, size: 'large', title: t('title.learning-ai-material') });
16
+ return popup.result.then(value => !!value);
17
+ }
18
+ let LearningMaterialForm = class LearningMaterialForm extends localize(i18next)(LitElement) {
19
+ constructor() {
20
+ super(...arguments);
21
+ this.units = [];
22
+ this.unitId = '';
23
+ this.instruction = '';
24
+ this.captions = '';
25
+ this.busy = false;
26
+ this.error = '';
27
+ this.generation = 0;
28
+ this.transcribe = new MutationController(this, gql `mutation ($unitId: String!) { transcribeLearningMaterial(unitId: $unitId) }`);
29
+ this.propose = new MutationController(this, gql `mutation ($unitId: String!, $source: Object!, $instruction: String!) { proposeLearningMaterial(unitId: $unitId, source: $source, instruction: $instruction) }`);
30
+ this.apply = new MutationController(this, gql `mutation ($unitId: String!, $source: Object!, $draft: Object!, $token: String!) { applyLearningMaterial(unitId: $unitId, source: $source, draft: $draft, token: $token) }`);
31
+ }
32
+ static { this.styles = [CommonPopupStyles, PopupBodyStyles, FormFieldStyles, PopupFieldStyles, css `
33
+ textarea { min-height: 100px; resize: vertical; }
34
+ .preview { white-space: pre-wrap; overflow-wrap: anywhere; }
35
+ .error { color: var(--md-sys-color-error); }
36
+ `]; }
37
+ willUpdate() { if (!this.unitId && this.units.length)
38
+ this.unitId = this.units[0].id; }
39
+ disconnectedCallback() { super.disconnectedCallback(); this.generation++; this.controller?.abort(); }
40
+ get selected() { return this.units.find(u => u.id === this.unitId); }
41
+ render() {
42
+ const draft = this.proposal?.draft;
43
+ return html `
44
+ <div class="body">
45
+ <p>${t('text.learning-ai-material-help')}</p>
46
+ <label class="field"><span>${t('field.learning-ai-source')}</span>
47
+ <select ?disabled=${this.busy || !!draft} .value=${this.unitId} @change=${(e) => { this.unitId = e.target.value; this.source = undefined; this.captions = ''; }}>
48
+ ${this.units.map(u => html `<option value=${u.id}>${u.name} · ${u.file?.name}</option>`)}
49
+ </select>
50
+ </label>
51
+ ${this.selected?.kind === 'VIDEO' ? html `
52
+ <label class="field"><span>${t('field.learning-ai-captions')}</span><input type="file" accept=".vtt,.srt,.txt" ?disabled=${this.busy || !!draft} @change=${(e) => this.readCaptions(e.target.files?.[0])} /></label>
53
+ <label class="field"><span>${t('text.learning-ai-captions-help')}</span><textarea maxlength="120000" ?disabled=${this.busy || !!draft} .value=${this.captions} @input=${(e) => this.captions = e.target.value}></textarea></label>
54
+ ` : nothing}
55
+ <label class="field"><span>${t('field.learning-ai-instruction')}</span><textarea maxlength="3900" ?disabled=${this.busy || !!draft} .value=${this.instruction} @input=${(e) => this.instruction = e.target.value}></textarea></label>
56
+ ${this.busy ? html `<p role="status">${t('text.learning-ai-working')}</p>` : nothing}
57
+ ${this.error ? html `<p class="error" role="alert">${this.error}</p>` : nothing}
58
+ ${draft ? html `<section aria-label=${t('title.learning-ai-preview')}>
59
+ <h3>${draft.title}</h3><p class="preview">${draft.introduction}</p>
60
+ <ul>${draft.objectives.map((s) => html `<li>${s}</li>`)}</ul>
61
+ ${draft.units.map((u) => html `<details><summary>${u.name}</summary><p class="preview">${u.body}</p><p>${u.references.join(', ')} · ${u.minSeconds}s</p></details>`)}
62
+ <h3>${t('title.learning-questions')}</h3>
63
+ ${draft.questions.map((q, i) => html `<details><summary>${i + 1}. ${q.text}</summary><ul>${q.choices.map((c) => html `<li>${c.correct ? '✓ ' : ''}${c.text}</li>`)}</ul><p>${q.explanation}</p><p>${q.references.join(', ')}</p></details>`)}
64
+ <details><summary>${t('field.learning-ai-source')}</summary>
65
+ <a href=${this.selected?.file?.fullpath ?? ''} target="_blank" rel="noopener noreferrer">${this.selected?.file?.name}</a>
66
+ ${this.source?.parts.map(p => html `<details><summary>${p.id}</summary>${p.text ? html `<p class="preview">${p.text}</p>` : html `<img style="max-width:100%" src=${p.image} alt=${p.id} />`}</details>`)}
67
+ </details>
68
+ <h3>${t('title.learning-ai-missing')}</h3><ul>${draft.missing.map((s) => html `<li>${s}</li>`)}</ul>
69
+ <p>${t('text.learning-ai-apply-help')}</p>
70
+ <md-text-button ?disabled=${this.busy} @click=${() => { this.proposal = undefined; this.error = ''; }}>${t('button.learning-ai-revise')}</md-text-button>
71
+ </section>` : nothing}
72
+ </div>
73
+ <ox-popup-actions>
74
+ <md-text-button slot="cancel" ?disabled=${this.apply.pending} @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>
75
+ <md-filled-button slot="confirm" ?disabled=${this.busy || !this.selected?.file} @click=${() => draft ? this.save() : this.generate()}>${draft ? t('button.learning-ai-apply') : t('button.learning-ai-generate')}</md-filled-button>
76
+ </ox-popup-actions>`;
77
+ }
78
+ async readCaptions(file) {
79
+ if (!file)
80
+ return;
81
+ this.captions = '';
82
+ if (file.size > 500_000) {
83
+ this.error = t('error.learning-ai-source-limit');
84
+ return;
85
+ }
86
+ const id = ++this.generation;
87
+ this.busy = true;
88
+ this.error = '';
89
+ try {
90
+ const text = await file.text();
91
+ captionParts(text);
92
+ if (id === this.generation && this.isConnected) {
93
+ this.captions = text;
94
+ this.error = '';
95
+ }
96
+ }
97
+ catch {
98
+ if (id === this.generation)
99
+ this.error = t('error.learning-ai-source-limit');
100
+ }
101
+ finally {
102
+ if (id === this.generation)
103
+ this.busy = false;
104
+ }
105
+ }
106
+ message(error) {
107
+ const key = error?.message;
108
+ return key?.startsWith('learning-ai-') ? t(`error.${key}`) : error?.graphQLErrors?.[0]?.message ?? key ?? t('error.learning-ai-source-read');
109
+ }
110
+ async generate() {
111
+ const unit = this.selected;
112
+ if (!unit?.file || this.busy)
113
+ return;
114
+ const id = ++this.generation;
115
+ this.busy = true;
116
+ this.error = '';
117
+ this.controller = new AbortController();
118
+ try {
119
+ if (unit.kind === 'VIDEO' && !this.captions.trim()) {
120
+ const answer = await this.transcribe.run({ unitId: unit.id }, { context: { fetchOptions: { signal: this.controller.signal } } });
121
+ if (id !== this.generation)
122
+ return;
123
+ if (!answer)
124
+ throw this.transcribe.error;
125
+ if (answer.transcribeLearningMaterial.fileId !== unit.file.id)
126
+ throw new Error('learning-ai-draft-stale');
127
+ this.captions = answer.transcribeLearningMaterial.captions;
128
+ }
129
+ const source = unit.kind === 'PDF' ? await pdfMaterial(unit.file.id, unit.file.fullpath, this.controller.signal) : { fileId: unit.file.id, parts: captionParts(this.captions) };
130
+ if (id !== this.generation)
131
+ return;
132
+ this.source = source;
133
+ const answer = await this.propose.run({ unitId: unit.id, source, instruction: `Language: ${i18next.language}. ${this.instruction}` }, { context: { fetchOptions: { signal: this.controller.signal } } });
134
+ if (id !== this.generation)
135
+ return;
136
+ if (!answer)
137
+ throw this.propose.error;
138
+ this.proposal = answer.proposeLearningMaterial;
139
+ }
140
+ catch (error) {
141
+ if (id === this.generation)
142
+ this.error = this.message(error);
143
+ }
144
+ finally {
145
+ if (id === this.generation)
146
+ this.busy = false;
147
+ }
148
+ }
149
+ async save() {
150
+ if (!this.proposal || !this.source || this.busy)
151
+ return;
152
+ this.busy = true;
153
+ this.error = '';
154
+ try {
155
+ const answer = await this.apply.run({ unitId: this.unitId, source: this.source, draft: this.proposal.draft, token: this.proposal.token });
156
+ if (!answer) {
157
+ this.error = this.message(this.apply.error);
158
+ return;
159
+ }
160
+ this.dispatchEvent(new CustomEvent('done'));
161
+ }
162
+ catch (error) {
163
+ this.error = this.message(error);
164
+ }
165
+ finally {
166
+ this.busy = false;
167
+ }
168
+ }
169
+ };
170
+ __decorate([
171
+ property({ attribute: false }),
172
+ __metadata("design:type", Array)
173
+ ], LearningMaterialForm.prototype, "units", void 0);
174
+ __decorate([
175
+ state(),
176
+ __metadata("design:type", Object)
177
+ ], LearningMaterialForm.prototype, "unitId", void 0);
178
+ __decorate([
179
+ state(),
180
+ __metadata("design:type", Object)
181
+ ], LearningMaterialForm.prototype, "instruction", void 0);
182
+ __decorate([
183
+ state(),
184
+ __metadata("design:type", Object)
185
+ ], LearningMaterialForm.prototype, "captions", void 0);
186
+ __decorate([
187
+ state(),
188
+ __metadata("design:type", Object)
189
+ ], LearningMaterialForm.prototype, "busy", void 0);
190
+ __decorate([
191
+ state(),
192
+ __metadata("design:type", Object)
193
+ ], LearningMaterialForm.prototype, "error", void 0);
194
+ __decorate([
195
+ state(),
196
+ __metadata("design:type", Object)
197
+ ], LearningMaterialForm.prototype, "proposal", void 0);
198
+ LearningMaterialForm = __decorate([
199
+ customElement('learning-material-form')
200
+ ], LearningMaterialForm);
201
+ export { LearningMaterialForm };
202
+ //# sourceMappingURL=learning-material-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning-material-form.js","sourceRoot":"","sources":["../../client/components/learning-material-form.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@operato/popup/ox-popup-actions.js'\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { MutationController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\nimport { captionParts, pdfMaterial, type MaterialSource } from '../utils/material-source.js'\nimport { t } from '../utils/learning-words.js'\n\ntype SourceUnit = { id: string; kind: string; name: string; file?: { id: string; name: string; fullpath: string } }\nexport function openMaterialForm(units: SourceUnit[]): Promise<boolean> {\n const popup = openPopup(html`<learning-material-form .units=${units} @done=${() => popup.close(true)} @close=${() => popup.close()}></learning-material-form>`, { backdrop: true, size: 'large', title: t('title.learning-ai-material') })\n return popup.result.then(value => !!value)\n}\n\n@customElement('learning-material-form')\nexport class LearningMaterialForm extends localize(i18next)(LitElement) {\n static styles = [CommonPopupStyles, PopupBodyStyles, FormFieldStyles, PopupFieldStyles, css`\n textarea { min-height: 100px; resize: vertical; }\n .preview { white-space: pre-wrap; overflow-wrap: anywhere; }\n .error { color: var(--md-sys-color-error); }\n `]\n @property({ attribute: false }) units: SourceUnit[] = []\n @state() private unitId = ''\n @state() private instruction = ''\n @state() private captions = ''\n @state() private busy = false\n @state() private error = ''\n @state() private proposal: any\n private source?: MaterialSource\n private controller?: AbortController\n private generation = 0\n private transcribe = new MutationController(this, gql`mutation ($unitId: String!) { transcribeLearningMaterial(unitId: $unitId) }`)\n private propose = new MutationController(this, gql`mutation ($unitId: String!, $source: Object!, $instruction: String!) { proposeLearningMaterial(unitId: $unitId, source: $source, instruction: $instruction) }`)\n private apply = new MutationController(this, gql`mutation ($unitId: String!, $source: Object!, $draft: Object!, $token: String!) { applyLearningMaterial(unitId: $unitId, source: $source, draft: $draft, token: $token) }`)\n\n willUpdate() { if (!this.unitId && this.units.length) this.unitId = this.units[0].id }\n disconnectedCallback() { super.disconnectedCallback(); this.generation++; this.controller?.abort() }\n private get selected() { return this.units.find(u => u.id === this.unitId) }\n\n render() {\n const draft = this.proposal?.draft\n return html`\n <div class=\"body\">\n <p>${t('text.learning-ai-material-help')}</p>\n <label class=\"field\"><span>${t('field.learning-ai-source')}</span>\n <select ?disabled=${this.busy || !!draft} .value=${this.unitId} @change=${(e: any) => { this.unitId = e.target.value; this.source = undefined; this.captions = '' }}>\n ${this.units.map(u => html`<option value=${u.id}>${u.name} · ${u.file?.name}</option>`)}\n </select>\n </label>\n ${this.selected?.kind === 'VIDEO' ? html`\n <label class=\"field\"><span>${t('field.learning-ai-captions')}</span><input type=\"file\" accept=\".vtt,.srt,.txt\" ?disabled=${this.busy || !!draft} @change=${(e: any) => this.readCaptions(e.target.files?.[0])} /></label>\n <label class=\"field\"><span>${t('text.learning-ai-captions-help')}</span><textarea maxlength=\"120000\" ?disabled=${this.busy || !!draft} .value=${this.captions} @input=${(e: any) => this.captions = e.target.value}></textarea></label>\n ` : nothing}\n <label class=\"field\"><span>${t('field.learning-ai-instruction')}</span><textarea maxlength=\"3900\" ?disabled=${this.busy || !!draft} .value=${this.instruction} @input=${(e: any) => this.instruction = e.target.value}></textarea></label>\n ${this.busy ? html`<p role=\"status\">${t('text.learning-ai-working')}</p>` : nothing}\n ${this.error ? html`<p class=\"error\" role=\"alert\">${this.error}</p>` : nothing}\n ${draft ? html`<section aria-label=${t('title.learning-ai-preview')}>\n <h3>${draft.title}</h3><p class=\"preview\">${draft.introduction}</p>\n <ul>${draft.objectives.map((s: string) => html`<li>${s}</li>`)}</ul>\n ${draft.units.map((u: any) => html`<details><summary>${u.name}</summary><p class=\"preview\">${u.body}</p><p>${u.references.join(', ')} · ${u.minSeconds}s</p></details>`)}\n <h3>${t('title.learning-questions')}</h3>\n ${draft.questions.map((q: any, i: number) => html`<details><summary>${i + 1}. ${q.text}</summary><ul>${q.choices.map((c: any) => html`<li>${c.correct ? '✓ ' : ''}${c.text}</li>`)}</ul><p>${q.explanation}</p><p>${q.references.join(', ')}</p></details>`)}\n <details><summary>${t('field.learning-ai-source')}</summary>\n <a href=${this.selected?.file?.fullpath ?? ''} target=\"_blank\" rel=\"noopener noreferrer\">${this.selected?.file?.name}</a>\n ${this.source?.parts.map(p => html`<details><summary>${p.id}</summary>${p.text ? html`<p class=\"preview\">${p.text}</p>` : html`<img style=\"max-width:100%\" src=${p.image} alt=${p.id} />`}</details>`)}\n </details>\n <h3>${t('title.learning-ai-missing')}</h3><ul>${draft.missing.map((s: string) => html`<li>${s}</li>`)}</ul>\n <p>${t('text.learning-ai-apply-help')}</p>\n <md-text-button ?disabled=${this.busy} @click=${() => { this.proposal = undefined; this.error = '' }}>${t('button.learning-ai-revise')}</md-text-button>\n </section>` : nothing}\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" ?disabled=${this.apply.pending} @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.busy || !this.selected?.file} @click=${() => draft ? this.save() : this.generate()}>${draft ? t('button.learning-ai-apply') : t('button.learning-ai-generate')}</md-filled-button>\n </ox-popup-actions>`\n }\n private async readCaptions(file?: File) {\n if (!file) return\n this.captions = ''\n if (file.size > 500_000) { this.error = t('error.learning-ai-source-limit'); return }\n const id = ++this.generation\n this.busy = true; this.error = ''\n try {\n const text = await file.text()\n captionParts(text)\n if (id === this.generation && this.isConnected) { this.captions = text; this.error = '' }\n } catch { if (id === this.generation) this.error = t('error.learning-ai-source-limit') }\n finally { if (id === this.generation) this.busy = false }\n }\n private message(error: any) {\n const key = error?.message\n return key?.startsWith('learning-ai-') ? t(`error.${key}`) : error?.graphQLErrors?.[0]?.message ?? key ?? t('error.learning-ai-source-read')\n }\n private async generate() {\n const unit = this.selected\n if (!unit?.file || this.busy) return\n const id = ++this.generation\n this.busy = true; this.error = ''; this.controller = new AbortController()\n try {\n if (unit.kind === 'VIDEO' && !this.captions.trim()) {\n const answer = await this.transcribe.run({ unitId: unit.id }, { context: { fetchOptions: { signal: this.controller.signal } } })\n if (id !== this.generation) return\n if (!answer) throw this.transcribe.error\n if (answer.transcribeLearningMaterial.fileId !== unit.file.id) throw new Error('learning-ai-draft-stale')\n this.captions = answer.transcribeLearningMaterial.captions\n }\n const source = unit.kind === 'PDF' ? await pdfMaterial(unit.file.id, unit.file.fullpath, this.controller.signal) : { fileId: unit.file.id, parts: captionParts(this.captions) }\n if (id !== this.generation) return\n this.source = source\n const answer = await this.propose.run({ unitId: unit.id, source, instruction: `Language: ${i18next.language}. ${this.instruction}` }, { context: { fetchOptions: { signal: this.controller.signal } } })\n if (id !== this.generation) return\n if (!answer) throw this.propose.error\n this.proposal = answer.proposeLearningMaterial\n } catch (error) { if (id === this.generation) this.error = this.message(error) }\n finally { if (id === this.generation) this.busy = false }\n }\n private async save() {\n if (!this.proposal || !this.source || this.busy) return\n this.busy = true; this.error = ''\n try {\n const answer = await this.apply.run({ unitId: this.unitId, source: this.source, draft: this.proposal.draft, token: this.proposal.token })\n if (!answer) { this.error = this.message(this.apply.error); return }\n this.dispatchEvent(new CustomEvent('done'))\n } catch (error) { this.error = this.message(error) }\n finally { this.busy = false }\n }\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import '@material/web/button/text-button.js';
3
+ import '@material/web/icon/icon.js';
4
+ import '@operato/input/ox-input-select-buttons.js';
5
+ import '@operato/popup/ox-popup-actions.js';
6
+ import { LitElement } from 'lit';
7
+ export type QuestionDraft = {
8
+ id?: string;
9
+ kind: string;
10
+ text: string;
11
+ explanation?: string | null;
12
+ drafted?: boolean;
13
+ choices: {
14
+ id?: string;
15
+ text: string;
16
+ correct: boolean;
17
+ }[];
18
+ };
19
+ /** Opens the question form. Resolves with the question as given, or null. */
20
+ export declare function openQuestionForm(question: QuestionDraft): Promise<QuestionDraft | null>;
21
+ declare const LearningQuestionForm_base: (new (...args: any[]) => LitElement) & typeof LitElement;
22
+ export declare class LearningQuestionForm extends LearningQuestionForm_base {
23
+ static styles: import("lit").CSSResult[];
24
+ question: QuestionDraft;
25
+ private draft;
26
+ willUpdate(changed: Map<string, unknown>): void;
27
+ render(): import("lit-html").TemplateResult<1>;
28
+ private kind;
29
+ private mark;
30
+ private write;
31
+ private removeChoice;
32
+ }
33
+ export {};