@things-factory/learning 0.0.0-stage → 10.1.260
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/bootstrap.d.ts +2 -0
- package/dist-client/bootstrap.js +4 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/components/learning-assign-form.d.ts +24 -0
- package/dist-client/components/learning-assign-form.js +77 -0
- package/dist-client/components/learning-assign-form.js.map +1 -0
- package/dist-client/components/learning-course-form.d.ts +29 -0
- package/dist-client/components/learning-course-form.js +104 -0
- package/dist-client/components/learning-course-form.js.map +1 -0
- package/dist-client/components/learning-question-form.d.ts +33 -0
- package/dist-client/components/learning-question-form.js +142 -0
- package/dist-client/components/learning-question-form.js.map +1 -0
- package/dist-client/components/learning-todo.d.ts +12 -0
- package/dist-client/components/learning-todo.js +43 -0
- package/dist-client/components/learning-todo.js.map +1 -0
- package/dist-client/components/learning-unit-form.d.ts +41 -0
- package/dist-client/components/learning-unit-form.js +143 -0
- package/dist-client/components/learning-unit-form.js.map +1 -0
- package/dist-client/index.d.ts +2 -0
- package/dist-client/index.js +3 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/learning-course-editor.d.ts +62 -0
- package/dist-client/pages/learning-course-editor.js +463 -0
- package/dist-client/pages/learning-course-editor.js.map +1 -0
- package/dist-client/pages/learning-course-list.d.ts +37 -0
- package/dist-client/pages/learning-course-list.js +88 -0
- package/dist-client/pages/learning-course-list.js.map +1 -0
- package/dist-client/pages/learning-enrollment-status.d.ts +53 -0
- package/dist-client/pages/learning-enrollment-status.js +228 -0
- package/dist-client/pages/learning-enrollment-status.js.map +1 -0
- package/dist-client/pages/learning-view.d.ts +52 -0
- package/dist-client/pages/learning-view.js +371 -0
- package/dist-client/pages/learning-view.js.map +1 -0
- package/dist-client/pages/my-learning.d.ts +22 -0
- package/dist-client/pages/my-learning.js +110 -0
- package/dist-client/pages/my-learning.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +21 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/utils/learning-api.d.ts +16 -0
- package/dist-client/utils/learning-api.js +36 -0
- package/dist-client/utils/learning-api.js.map +1 -0
- package/dist-client/utils/learning-words.d.ts +20 -0
- package/dist-client/utils/learning-words.js +22 -0
- package/dist-client/utils/learning-words.js.map +1 -0
- package/dist-server/activities.d.ts +38 -0
- package/dist-server/activities.js +42 -0
- package/dist-server/activities.js.map +1 -0
- package/dist-server/ai-reads.d.ts +9 -0
- package/dist-server/ai-reads.js +70 -0
- package/dist-server/ai-reads.js.map +1 -0
- package/dist-server/attachment-reader.d.ts +1 -0
- package/dist-server/attachment-reader.js +23 -0
- package/dist-server/attachment-reader.js.map +1 -0
- package/dist-server/index.d.ts +4 -0
- package/dist-server/index.js +33 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/learning-subjects.d.ts +10 -0
- package/dist-server/learning-subjects.js +31 -0
- package/dist-server/learning-subjects.js.map +1 -0
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +17 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/service/course/course-mutation.d.ts +19 -0
- package/dist-server/service/course/course-mutation.js +176 -0
- package/dist-server/service/course/course-mutation.js.map +1 -0
- package/dist-server/service/course/course-query.d.ts +26 -0
- package/dist-server/service/course/course-query.js +172 -0
- package/dist-server/service/course/course-query.js.map +1 -0
- package/dist-server/service/course/course-question.d.ts +41 -0
- package/dist-server/service/course/course-question.js +116 -0
- package/dist-server/service/course/course-question.js.map +1 -0
- package/dist-server/service/course/course-rules.d.ts +49 -0
- package/dist-server/service/course/course-rules.js +89 -0
- package/dist-server/service/course/course-rules.js.map +1 -0
- package/dist-server/service/course/course-service.d.ts +71 -0
- package/dist-server/service/course/course-service.js +402 -0
- package/dist-server/service/course/course-service.js.map +1 -0
- package/dist-server/service/course/course-type.d.ts +55 -0
- package/dist-server/service/course/course-type.js +198 -0
- package/dist-server/service/course/course-type.js.map +1 -0
- package/dist-server/service/course/course-unit.d.ts +36 -0
- package/dist-server/service/course/course-unit.js +123 -0
- package/dist-server/service/course/course-unit.js.map +1 -0
- package/dist-server/service/course/course-version.d.ts +45 -0
- package/dist-server/service/course/course-version.js +151 -0
- package/dist-server/service/course/course-version.js.map +1 -0
- package/dist-server/service/course/course.d.ts +23 -0
- package/dist-server/service/course/course.js +92 -0
- package/dist-server/service/course/course.js.map +1 -0
- package/dist-server/service/course/index.d.ts +14 -0
- package/dist-server/service/course/index.js +24 -0
- package/dist-server/service/course/index.js.map +1 -0
- package/dist-server/service/enrollment/enrollment-mutation.d.ts +11 -0
- package/dist-server/service/enrollment/enrollment-mutation.js +124 -0
- package/dist-server/service/enrollment/enrollment-mutation.js.map +1 -0
- package/dist-server/service/enrollment/enrollment-query.d.ts +23 -0
- package/dist-server/service/enrollment/enrollment-query.js +245 -0
- package/dist-server/service/enrollment/enrollment-query.js.map +1 -0
- package/dist-server/service/enrollment/enrollment-remind.d.ts +1 -0
- package/dist-server/service/enrollment/enrollment-remind.js +45 -0
- package/dist-server/service/enrollment/enrollment-remind.js.map +1 -0
- package/dist-server/service/enrollment/enrollment-service.d.ts +110 -0
- package/dist-server/service/enrollment/enrollment-service.js +417 -0
- package/dist-server/service/enrollment/enrollment-service.js.map +1 -0
- package/dist-server/service/enrollment/enrollment-status.d.ts +40 -0
- package/dist-server/service/enrollment/enrollment-status.js +135 -0
- package/dist-server/service/enrollment/enrollment-status.js.map +1 -0
- package/dist-server/service/enrollment/enrollment-type.d.ts +95 -0
- package/dist-server/service/enrollment/enrollment-type.js +341 -0
- package/dist-server/service/enrollment/enrollment-type.js.map +1 -0
- package/dist-server/service/enrollment/enrollment.d.ts +52 -0
- package/dist-server/service/enrollment/enrollment.js +186 -0
- package/dist-server/service/enrollment/enrollment.js.map +1 -0
- package/dist-server/service/enrollment/index.d.ts +15 -0
- package/dist-server/service/enrollment/index.js +26 -0
- package/dist-server/service/enrollment/index.js.map +1 -0
- package/dist-server/service/enrollment/learning-ai.d.ts +38 -0
- package/dist-server/service/enrollment/learning-ai.js +96 -0
- package/dist-server/service/enrollment/learning-ai.js.map +1 -0
- package/dist-server/service/enrollment/learning-documents.d.ts +7 -0
- package/dist-server/service/enrollment/learning-documents.js +160 -0
- package/dist-server/service/enrollment/learning-documents.js.map +1 -0
- package/dist-server/service/enrollment/learning-outcome.d.ts +10 -0
- package/dist-server/service/enrollment/learning-outcome.js +16 -0
- package/dist-server/service/enrollment/learning-outcome.js.map +1 -0
- package/dist-server/service/enrollment/progress-rules.d.ts +94 -0
- package/dist-server/service/enrollment/progress-rules.js +97 -0
- package/dist-server/service/enrollment/progress-rules.js.map +1 -0
- package/dist-server/service/enrollment/quiz-attempt.d.ts +34 -0
- package/dist-server/service/enrollment/quiz-attempt.js +105 -0
- package/dist-server/service/enrollment/quiz-attempt.js.map +1 -0
- package/dist-server/service/enrollment/unit-progress.d.ts +30 -0
- package/dist-server/service/enrollment/unit-progress.js +114 -0
- package/dist-server/service/enrollment/unit-progress.js.map +1 -0
- package/dist-server/service/index.d.ts +14 -0
- package/dist-server/service/index.js +43 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +56 -4
- package/things-factory.config.js +14 -0
- package/translations/en.json +255 -0
- package/translations/es.json +255 -0
- package/translations/ja.json +255 -0
- package/translations/ko.json +255 -0
- package/translations/ms.json +255 -0
- package/translations/zh.json +255 -0
- package/README.md +0 -3
|
@@ -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":"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","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,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 {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/button/filled-button.js';
|
|
3
|
+
import '@material/web/button/text-button.js';
|
|
4
|
+
import '@material/web/icon/icon.js';
|
|
5
|
+
import '@operato/input/ox-input-select-buttons.js';
|
|
6
|
+
import '@operato/popup/ox-popup-actions.js';
|
|
7
|
+
import { css, html, LitElement } from 'lit';
|
|
8
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
9
|
+
import { i18next, localize } from '@operato/i18n';
|
|
10
|
+
import { openPopup } from '@operato/layout';
|
|
11
|
+
import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles';
|
|
12
|
+
import { QUESTION_KINDS, questionKindWord, t } from '../utils/learning-words.js';
|
|
13
|
+
/** Opens the question form. Resolves with the question as given, or null. */
|
|
14
|
+
export function openQuestionForm(question) {
|
|
15
|
+
const popup = openPopup(html `<learning-question-form .question=${question} @done=${(e) => popup.close(e.detail)} @close=${() => popup.close()}></learning-question-form>`, { backdrop: true, size: 'large', title: question.id ? t('title.learning-question-edit') : t('title.learning-question-new') });
|
|
16
|
+
return popup.result.then(value => (value ?? null));
|
|
17
|
+
}
|
|
18
|
+
/* 문항 (ADR-0194 decision 4) - how it is answered, what it asks, its choices with which are right, and why. */
|
|
19
|
+
let LearningQuestionForm = class LearningQuestionForm extends localize(i18next)(LitElement) {
|
|
20
|
+
constructor() {
|
|
21
|
+
super(...arguments);
|
|
22
|
+
this.question = { kind: 'SINGLE', text: '', choices: [] };
|
|
23
|
+
this.draft = { kind: 'SINGLE', text: '', choices: [] };
|
|
24
|
+
}
|
|
25
|
+
static { this.styles = [
|
|
26
|
+
CommonPopupStyles,
|
|
27
|
+
PopupBodyStyles,
|
|
28
|
+
FormFieldStyles,
|
|
29
|
+
PopupFieldStyles,
|
|
30
|
+
css `
|
|
31
|
+
ox-input-select-buttons {
|
|
32
|
+
align-self: flex-start;
|
|
33
|
+
}
|
|
34
|
+
textarea {
|
|
35
|
+
min-height: 80px;
|
|
36
|
+
resize: vertical;
|
|
37
|
+
}
|
|
38
|
+
.choice {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: var(--spacing-small, 4px);
|
|
42
|
+
}
|
|
43
|
+
.choice input[type='text'] {
|
|
44
|
+
flex: 1;
|
|
45
|
+
}
|
|
46
|
+
.field .choice input[type='checkbox'],
|
|
47
|
+
.field .choice input[type='radio'] {
|
|
48
|
+
width: auto;
|
|
49
|
+
margin: 0;
|
|
50
|
+
}
|
|
51
|
+
.choice button {
|
|
52
|
+
border: 0;
|
|
53
|
+
background: transparent;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
color: var(--ui-muted);
|
|
56
|
+
padding: 4px;
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
]; }
|
|
60
|
+
willUpdate(changed) {
|
|
61
|
+
if (changed.has('question')) {
|
|
62
|
+
const choices = this.question.choices?.length ? this.question.choices.map(one => ({ ...one })) : [{ text: '', correct: true }, { text: '', correct: false }];
|
|
63
|
+
this.draft = { ...this.question, choices };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
render() {
|
|
67
|
+
const multiple = this.draft.kind === 'MULTIPLE';
|
|
68
|
+
const right = this.draft.choices.filter(one => one.correct).length;
|
|
69
|
+
const ready = this.draft.text.trim() && this.draft.choices.length >= 2 && this.draft.choices.every(one => one.text.trim()) && right >= 1 && (multiple || right === 1);
|
|
70
|
+
return html `
|
|
71
|
+
<div class="body">
|
|
72
|
+
<div class="field">
|
|
73
|
+
<span>${t('field.learning-question-kind')}</span>
|
|
74
|
+
<ox-input-select-buttons .value=${this.draft.kind} .options=${QUESTION_KINDS.map(code => ({ display: questionKindWord(code), value: code }))} @change=${(e) => e.detail && this.kind(e.detail)}></ox-input-select-buttons>
|
|
75
|
+
</div>
|
|
76
|
+
<label class="field">
|
|
77
|
+
<span>${t('field.learning-question-text')}</span>
|
|
78
|
+
<textarea .value=${this.draft.text} @input=${(e) => (this.draft = { ...this.draft, text: e.target.value })}></textarea>
|
|
79
|
+
</label>
|
|
80
|
+
<fieldset class="field" style="border:0;padding:0;margin:0">
|
|
81
|
+
<legend><span>${t('field.learning-question-choices')}</span></legend>
|
|
82
|
+
${this.draft.choices.map((choice, index) => html `<div class="choice">
|
|
83
|
+
<input
|
|
84
|
+
type=${multiple ? 'checkbox' : 'radio'}
|
|
85
|
+
name="correct"
|
|
86
|
+
aria-label=${t('label.learning-choice-correct', { number: index + 1 })}
|
|
87
|
+
.checked=${choice.correct}
|
|
88
|
+
@change=${(e) => this.mark(index, e.target.checked)}
|
|
89
|
+
/>
|
|
90
|
+
<input type="text" aria-label=${t('label.learning-choice-text', { number: index + 1 })} .value=${choice.text} ?readonly=${this.draft.kind === 'TRUE_FALSE'} @input=${(e) => this.write(index, e.target.value)} />
|
|
91
|
+
${this.draft.kind !== 'TRUE_FALSE' && this.draft.choices.length > 2
|
|
92
|
+
? html `<button aria-label=${t('button.learning-choice-remove')} @click=${() => this.removeChoice(index)}><md-icon>close</md-icon></button>`
|
|
93
|
+
: ''}
|
|
94
|
+
</div>`)}
|
|
95
|
+
${this.draft.kind !== 'TRUE_FALSE'
|
|
96
|
+
? html `<md-text-button style="align-self:flex-start" @click=${() => (this.draft = { ...this.draft, choices: [...this.draft.choices, { text: '', correct: false }] })}>${t('button.learning-choice-add')}</md-text-button>`
|
|
97
|
+
: ''}
|
|
98
|
+
<span>${multiple ? t('text.learning-question-multiple-hint') : t('text.learning-question-single-hint')}</span>
|
|
99
|
+
</fieldset>
|
|
100
|
+
<label class="field">
|
|
101
|
+
<span>${t('field.learning-question-explanation')}</span>
|
|
102
|
+
<textarea .value=${this.draft.explanation ?? ''} @input=${(e) => (this.draft = { ...this.draft, explanation: e.target.value })}></textarea>
|
|
103
|
+
</label>
|
|
104
|
+
</div>
|
|
105
|
+
<ox-popup-actions>
|
|
106
|
+
<md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>
|
|
107
|
+
<md-filled-button slot="confirm" ?disabled=${!ready} @click=${() => this.dispatchEvent(new CustomEvent('done', { detail: this.draft }))}>${t('button.confirm')}</md-filled-button>
|
|
108
|
+
</ox-popup-actions>
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
111
|
+
kind(kind) {
|
|
112
|
+
if (kind === 'TRUE_FALSE')
|
|
113
|
+
this.draft = { ...this.draft, kind, choices: [{ text: t('text.quiz-true'), correct: true }, { text: t('text.quiz-false'), correct: false }] };
|
|
114
|
+
else if (kind === 'SINGLE' && this.draft.choices.filter(one => one.correct).length > 1)
|
|
115
|
+
this.draft = { ...this.draft, kind, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === this.draft.choices.findIndex(c => c.correct) })) };
|
|
116
|
+
else
|
|
117
|
+
this.draft = { ...this.draft, kind };
|
|
118
|
+
}
|
|
119
|
+
mark(index, checked) {
|
|
120
|
+
const multiple = this.draft.kind === 'MULTIPLE';
|
|
121
|
+
this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === index ? checked : multiple ? one.correct : false })) };
|
|
122
|
+
}
|
|
123
|
+
write(index, text) {
|
|
124
|
+
this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => (i === index ? { ...one, text } : one)) };
|
|
125
|
+
}
|
|
126
|
+
removeChoice(index) {
|
|
127
|
+
this.draft = { ...this.draft, choices: this.draft.choices.filter((_, i) => i !== index) };
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
__decorate([
|
|
131
|
+
property({ attribute: false }),
|
|
132
|
+
__metadata("design:type", Object)
|
|
133
|
+
], LearningQuestionForm.prototype, "question", void 0);
|
|
134
|
+
__decorate([
|
|
135
|
+
state(),
|
|
136
|
+
__metadata("design:type", Object)
|
|
137
|
+
], LearningQuestionForm.prototype, "draft", void 0);
|
|
138
|
+
LearningQuestionForm = __decorate([
|
|
139
|
+
customElement('learning-question-form')
|
|
140
|
+
], LearningQuestionForm);
|
|
141
|
+
export { LearningQuestionForm };
|
|
142
|
+
//# sourceMappingURL=learning-question-form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-question-form.js","sourceRoot":"","sources":["../../client/components/learning-question-form.ts"],"names":[],"mappings":";AAAA,OAAO,uCAAuC,CAAA;AAC9C,OAAO,qCAAqC,CAAA;AAC5C,OAAO,4BAA4B,CAAA;AACnC,OAAO,2CAA2C,CAAA;AAClD,OAAO,oCAAoC,CAAA;AAE3C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,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,cAAc,EAAE,gBAAgB,EAAE,CAAC,EAAE,MAAM,4BAA4B,CAAA;AAIhF,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,QAAuB;IACtD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAA,qCAAqC,QAAQ,UAAU,CAAC,CAAc,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,4BAA4B,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAA;IACrT,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAQ,CAAC,CAAA;AAC3D,CAAC;AAED,6GAA6G;AAEtG,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAAhE;;QAqC2B,aAAQ,GAAkB,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;QAClF,UAAK,GAAkB,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;IA6EnF,CAAC;aAlHQ,WAAM,GAAG;QACd,iBAAiB;QACjB,eAAe;QACf,eAAe;QACf,gBAAgB;QAChB,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4BF;KACF,AAlCY,CAkCZ;IAKD,UAAU,CAAC,OAA6B;QACtC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAA;YAC5J,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;QAC5C,CAAC;IACH,CAAC;IAED,MAAM;QACJ,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAA;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAC,CAAA;QACrK,OAAO,IAAI,CAAA;;;kBAGG,CAAC,CAAC,8BAA8B,CAAC;4CACP,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;;;kBAGnM,CAAC,CAAC,8BAA8B,CAAC;6BACtB,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;;0BAG/F,CAAC,CAAC,iCAAiC,CAAC;YAClD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CACtB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAA;;uBAEZ,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO;;6BAEzB,CAAC,CAAC,+BAA+B,EAAE,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;2BAC3D,MAAM,CAAC,OAAO;0BACf,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;;8CAE1B,CAAC,CAAC,4BAA4B,EAAE,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,WAAW,MAAM,CAAC,IAAI,cAAc,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,WAAW,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAChN,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YACjE,CAAC,CAAC,IAAI,CAAA,sBAAsB,CAAC,CAAC,+BAA+B,CAAC,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,oCAAoC;YAC3I,CAAC,CAAC,EAAE;mBACD,CACR;YACC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY;YAChC,CAAC,CAAC,IAAI,CAAA,wDAAwD,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,4BAA4B,CAAC,mBAAmB;YAC1N,CAAC,CAAC,EAAE;kBACE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,oCAAoC,CAAC;;;kBAG9F,CAAC,CAAC,qCAAqC,CAAC;6BAC7B,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,WAAW,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;;;+CAI9F,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;qDAClE,CAAC,KAAK,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,gBAAgB,CAAC;;KAEjK,CAAA;IACH,CAAC;IAEO,IAAI,CAAC,IAAY;QACvB,IAAI,IAAI,KAAK,YAAY;YACvB,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,iBAAiB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;aAC1I,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;YACpF,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;;YACzJ,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAA;IAC3C,CAAC;IAEO,IAAI,CAAC,KAAa,EAAE,OAAgB;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAA;QAC/C,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAA;IAC5J,CAAC;IAEO,KAAK,CAAC,KAAa,EAAE,IAAY;QACvC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAA;IACrH,CAAC;IAEO,YAAY,CAAC,KAAa;QAChC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,EAAE,CAAA;IAC3F,CAAC;;AA7E+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;sDAAoE;AAClF;IAAhB,KAAK,EAAE;;mDAAyE;AAtCtE,oBAAoB;IADhC,aAAa,CAAC,wBAAwB,CAAC;GAC3B,oBAAoB,CAmHhC","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/icon/icon.js'\nimport '@operato/input/ox-input-select-buttons.js'\nimport '@operato/popup/ox-popup-actions.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { QUESTION_KINDS, questionKindWord, t } from '../utils/learning-words.js'\n\nexport type QuestionDraft = { id?: string; kind: string; text: string; explanation?: string | null; drafted?: boolean; choices: { id?: string; text: string; correct: boolean }[] }\n\n/** Opens the question form. Resolves with the question as given, or null. */\nexport function openQuestionForm(question: QuestionDraft): Promise<QuestionDraft | null> {\n const popup = openPopup(html`<learning-question-form .question=${question} @done=${(e: CustomEvent) => popup.close(e.detail)} @close=${() => popup.close()}></learning-question-form>`, { backdrop: true, size: 'large', title: question.id ? t('title.learning-question-edit') : t('title.learning-question-new') })\n return popup.result.then(value => (value ?? null) as any)\n}\n\n/* 문항 (ADR-0194 decision 4) - how it is answered, what it asks, its choices with which are right, and why. */\n@customElement('learning-question-form')\nexport class LearningQuestionForm 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: 80px;\n resize: vertical;\n }\n .choice {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n }\n .choice input[type='text'] {\n flex: 1;\n }\n .field .choice input[type='checkbox'],\n .field .choice input[type='radio'] {\n width: auto;\n margin: 0;\n }\n .choice button {\n border: 0;\n background: transparent;\n cursor: pointer;\n color: var(--ui-muted);\n padding: 4px;\n }\n `\n ]\n\n @property({ attribute: false }) question: QuestionDraft = { kind: 'SINGLE', text: '', choices: [] }\n @state() private draft: QuestionDraft = { kind: 'SINGLE', text: '', choices: [] }\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('question')) {\n const choices = this.question.choices?.length ? this.question.choices.map(one => ({ ...one })) : [{ text: '', correct: true }, { text: '', correct: false }]\n this.draft = { ...this.question, choices }\n }\n }\n\n render() {\n const multiple = this.draft.kind === 'MULTIPLE'\n const right = this.draft.choices.filter(one => one.correct).length\n const ready = this.draft.text.trim() && this.draft.choices.length >= 2 && this.draft.choices.every(one => one.text.trim()) && right >= 1 && (multiple || right === 1)\n return html`\n <div class=\"body\">\n <div class=\"field\">\n <span>${t('field.learning-question-kind')}</span>\n <ox-input-select-buttons .value=${this.draft.kind} .options=${QUESTION_KINDS.map(code => ({ display: questionKindWord(code), value: code }))} @change=${(e: CustomEvent) => e.detail && this.kind(e.detail)}></ox-input-select-buttons>\n </div>\n <label class=\"field\">\n <span>${t('field.learning-question-text')}</span>\n <textarea .value=${this.draft.text} @input=${(e: any) => (this.draft = { ...this.draft, text: e.target.value })}></textarea>\n </label>\n <fieldset class=\"field\" style=\"border:0;padding:0;margin:0\">\n <legend><span>${t('field.learning-question-choices')}</span></legend>\n ${this.draft.choices.map(\n (choice, index) => html`<div class=\"choice\">\n <input\n type=${multiple ? 'checkbox' : 'radio'}\n name=\"correct\"\n aria-label=${t('label.learning-choice-correct', { number: index + 1 })}\n .checked=${choice.correct}\n @change=${(e: any) => this.mark(index, e.target.checked)}\n />\n <input type=\"text\" aria-label=${t('label.learning-choice-text', { number: index + 1 })} .value=${choice.text} ?readonly=${this.draft.kind === 'TRUE_FALSE'} @input=${(e: any) => this.write(index, e.target.value)} />\n ${this.draft.kind !== 'TRUE_FALSE' && this.draft.choices.length > 2\n ? html`<button aria-label=${t('button.learning-choice-remove')} @click=${() => this.removeChoice(index)}><md-icon>close</md-icon></button>`\n : ''}\n </div>`\n )}\n ${this.draft.kind !== 'TRUE_FALSE'\n ? html`<md-text-button style=\"align-self:flex-start\" @click=${() => (this.draft = { ...this.draft, choices: [...this.draft.choices, { text: '', correct: false }] })}>${t('button.learning-choice-add')}</md-text-button>`\n : ''}\n <span>${multiple ? t('text.learning-question-multiple-hint') : t('text.learning-question-single-hint')}</span>\n </fieldset>\n <label class=\"field\">\n <span>${t('field.learning-question-explanation')}</span>\n <textarea .value=${this.draft.explanation ?? ''} @input=${(e: any) => (this.draft = { ...this.draft, explanation: e.target.value })}></textarea>\n </label>\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=${!ready} @click=${() => this.dispatchEvent(new CustomEvent('done', { detail: this.draft }))}>${t('button.confirm')}</md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private kind(kind: string) {\n if (kind === 'TRUE_FALSE')\n this.draft = { ...this.draft, kind, choices: [{ text: t('text.quiz-true'), correct: true }, { text: t('text.quiz-false'), correct: false }] }\n else if (kind === 'SINGLE' && this.draft.choices.filter(one => one.correct).length > 1)\n this.draft = { ...this.draft, kind, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === this.draft.choices.findIndex(c => c.correct) })) }\n else this.draft = { ...this.draft, kind }\n }\n\n private mark(index: number, checked: boolean) {\n const multiple = this.draft.kind === 'MULTIPLE'\n this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === index ? checked : multiple ? one.correct : false })) }\n }\n\n private write(index: number, text: string) {\n this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => (i === index ? { ...one, text } : one)) }\n }\n\n private removeChoice(index: number) {\n this.draft = { ...this.draft, choices: this.draft.choices.filter((_, i) => i !== index) }\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import '@material/web/button/filled-button.js';
|
|
2
|
+
import { LitElement } from 'lit';
|
|
3
|
+
declare const LearningTodo_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
4
|
+
export declare class LearningTodo extends LearningTodo_base {
|
|
5
|
+
static styles: import("lit").CSSResult;
|
|
6
|
+
input: {
|
|
7
|
+
headline?: string;
|
|
8
|
+
address?: string;
|
|
9
|
+
};
|
|
10
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/button/filled-button.js';
|
|
3
|
+
import { css, html, LitElement } from 'lit';
|
|
4
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
5
|
+
import { i18next, localize } from '@operato/i18n';
|
|
6
|
+
import { navigate } from '@operato/shell';
|
|
7
|
+
/*
|
|
8
|
+
* 수강 (ADR-0194 decision 5) - what the worklist to-do shows: the course, by when, and the way to it. Completing the
|
|
9
|
+
* course there ends the to-do; nothing is answered here.
|
|
10
|
+
*/
|
|
11
|
+
let LearningTodo = class LearningTodo extends localize(i18next)(LitElement) {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.input = {};
|
|
15
|
+
}
|
|
16
|
+
static { this.styles = css `
|
|
17
|
+
:host {
|
|
18
|
+
display: block;
|
|
19
|
+
padding: var(--spacing-large, 12px);
|
|
20
|
+
font-size: var(--ui-t-body, 13px);
|
|
21
|
+
line-height: 1.6;
|
|
22
|
+
color: var(--ui-ink);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
p {
|
|
26
|
+
margin: 0 0 var(--spacing-medium, 8px);
|
|
27
|
+
}
|
|
28
|
+
`; }
|
|
29
|
+
render() {
|
|
30
|
+
const { headline = '', address } = this.input ?? {};
|
|
31
|
+
return html `<p>${headline}</p>
|
|
32
|
+
${address ? html `<md-filled-button @click=${() => navigate(address)}>${i18next.t('button.learning-open-course')}</md-filled-button>` : ''}`;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
__decorate([
|
|
36
|
+
property({ attribute: false }),
|
|
37
|
+
__metadata("design:type", Object)
|
|
38
|
+
], LearningTodo.prototype, "input", void 0);
|
|
39
|
+
LearningTodo = __decorate([
|
|
40
|
+
customElement('learning-todo')
|
|
41
|
+
], LearningTodo);
|
|
42
|
+
export { LearningTodo };
|
|
43
|
+
//# sourceMappingURL=learning-todo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-todo.js","sourceRoot":"","sources":["../../client/components/learning-todo.ts"],"names":[],"mappings":";AAAA,OAAO,uCAAuC,CAAA;AAE9C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC;;;GAGG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAAxD;;QAe2B,UAAK,GAA4C,EAAE,CAAA;IAOrF,CAAC;aArBQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;GAYlB,AAZY,CAYZ;IAID,MAAM;QACJ,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAA;QACnD,OAAO,IAAI,CAAA,MAAM,QAAQ;QACrB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,4BAA4B,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IAC/I,CAAC;;AAN+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;2CAAoD;AAfxE,YAAY;IADxB,aAAa,CAAC,eAAe,CAAC;GAClB,YAAY,CAsBxB","sourcesContent":["import '@material/web/button/filled-button.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate } from '@operato/shell'\n\n/*\n * 수강 (ADR-0194 decision 5) - what the worklist to-do shows: the course, by when, and the way to it. Completing the\n * course there ends the to-do; nothing is answered here.\n */\n@customElement('learning-todo')\nexport class LearningTodo extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: block;\n padding: var(--spacing-large, 12px);\n font-size: var(--ui-t-body, 13px);\n line-height: 1.6;\n color: var(--ui-ink);\n }\n\n p {\n margin: 0 0 var(--spacing-medium, 8px);\n }\n `\n\n @property({ attribute: false }) input: { headline?: string; address?: string } = {}\n\n render() {\n const { headline = '', address } = this.input ?? {}\n return html`<p>${headline}</p>\n ${address ? html`<md-filled-button @click=${() => navigate(address)}>${i18next.t('button.learning-open-course')}</md-filled-button>` : ''}`\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/input/ox-input-file.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 UnitDraft = {
|
|
8
|
+
id?: string;
|
|
9
|
+
kind: string;
|
|
10
|
+
name: string;
|
|
11
|
+
body?: string | null;
|
|
12
|
+
url?: string | null;
|
|
13
|
+
minSeconds?: number | null;
|
|
14
|
+
durationSeconds?: number | null;
|
|
15
|
+
file?: {
|
|
16
|
+
name?: string;
|
|
17
|
+
} | null;
|
|
18
|
+
};
|
|
19
|
+
export type UnitEdit = {
|
|
20
|
+
unit: UnitDraft;
|
|
21
|
+
file?: File;
|
|
22
|
+
durationSeconds?: number;
|
|
23
|
+
};
|
|
24
|
+
/** Opens the unit form. Resolves with what was given - the unit and a file picked for it - or null. */
|
|
25
|
+
export declare function openUnitForm(unit: UnitDraft): Promise<UnitEdit | null>;
|
|
26
|
+
/** A video's length in seconds, read from the file in the browser - the server needs it to know when it was watched to the end. */
|
|
27
|
+
export declare function videoDurationOf(file: File): Promise<number | undefined>;
|
|
28
|
+
declare const LearningUnitForm_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
29
|
+
export declare class LearningUnitForm extends LearningUnitForm_base {
|
|
30
|
+
static styles: import("lit").CSSResult[];
|
|
31
|
+
unit: UnitDraft;
|
|
32
|
+
private draft;
|
|
33
|
+
private file?;
|
|
34
|
+
private duration?;
|
|
35
|
+
willUpdate(changed: Map<string, unknown>): void;
|
|
36
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
37
|
+
private patch;
|
|
38
|
+
private pick;
|
|
39
|
+
private done;
|
|
40
|
+
}
|
|
41
|
+
export {};
|