@things-factory/learning 10.1.262 → 10.1.263
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/pages/learning-course-list.js +27 -5
- package/dist-client/pages/learning-course-list.js.map +1 -1
- package/dist-client/pages/learning-enrollment-status.js +12 -2
- package/dist-client/pages/learning-enrollment-status.js.map +1 -1
- package/dist-client/pages/learning-view.d.ts +10 -0
- package/dist-client/pages/learning-view.js +59 -3
- package/dist-client/pages/learning-view.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/ai-reads.d.ts +1 -0
- package/dist-server/ai-reads.js +26 -2
- package/dist-server/ai-reads.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-mutation.d.ts +2 -0
- package/dist-server/service/enrollment/enrollment-mutation.js +18 -0
- package/dist-server/service/enrollment/enrollment-mutation.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-query.d.ts +2 -0
- package/dist-server/service/enrollment/enrollment-query.js +15 -0
- package/dist-server/service/enrollment/enrollment-query.js.map +1 -1
- package/dist-server/service/enrollment/enrollment-service.d.ts +4 -0
- package/dist-server/service/enrollment/enrollment-service.js +1 -0
- package/dist-server/service/enrollment/enrollment-service.js.map +1 -1
- package/dist-server/service/enrollment/index.d.ts +3 -1
- package/dist-server/service/enrollment/index.js +3 -1
- package/dist-server/service/enrollment/index.js.map +1 -1
- package/dist-server/service/enrollment/lesson-note-service.d.ts +15 -0
- package/dist-server/service/enrollment/lesson-note-service.js +86 -0
- package/dist-server/service/enrollment/lesson-note-service.js.map +1 -0
- package/dist-server/service/enrollment/lesson-note.d.ts +35 -0
- package/dist-server/service/enrollment/lesson-note.js +107 -0
- package/dist-server/service/enrollment/lesson-note.js.map +1 -0
- package/dist-server/service/index.d.ts +2 -1
- package/dist-server/service/index.js +1 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -14
- package/translations/en.json +22 -16
- package/translations/es.json +7 -1
- package/translations/ja.json +7 -1
- package/translations/ko.json +23 -17
- package/translations/ms.json +7 -1
- package/translations/zh.json +7 -1
|
@@ -9,6 +9,7 @@ import { i18next, localize } from '@operato/i18n';
|
|
|
9
9
|
import { navigate, PageView } from '@operato/shell';
|
|
10
10
|
import { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
|
|
11
11
|
import { isMobileDevice } from '@operato/utils';
|
|
12
|
+
import { SELECTION_ROW_PICKER } from '@things-factory/ai-assistant/dist-client/index.js';
|
|
12
13
|
import { openCourseForm } from '../components/learning-course-form.js';
|
|
13
14
|
import { categoryWord, dayOf, minutesOf, sourceKindWord, stateWord, t, unitKindWord } from '../utils/learning-words.js';
|
|
14
15
|
/**
|
|
@@ -40,6 +41,11 @@ const rateOf = (record) => {
|
|
|
40
41
|
const counts = record?.enrollmentCounts;
|
|
41
42
|
return counts?.total ? `${Math.round((counts.completed / counts.total) * 100)}%` : '';
|
|
42
43
|
};
|
|
44
|
+
/** How the course's enrolments stand, in words for its card - nothing while nobody is enrolled. */
|
|
45
|
+
const standingOf = (record) => {
|
|
46
|
+
const counts = record?.enrollmentCounts;
|
|
47
|
+
return counts?.total ? t('text.learning-course-standing', { enrolled: counts.total, completed: counts.completed, rate: rateOf(record) }) : '';
|
|
48
|
+
};
|
|
43
49
|
let LearningCourseList = class LearningCourseList extends localize(i18next)(PageView) {
|
|
44
50
|
static { this.styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, css ``]; }
|
|
45
51
|
get context() {
|
|
@@ -50,7 +56,14 @@ let LearningCourseList = class LearningCourseList extends localize(i18next)(Page
|
|
|
50
56
|
};
|
|
51
57
|
}
|
|
52
58
|
render() {
|
|
53
|
-
return html `<ox-
|
|
59
|
+
return html `<ox-assistant-selection-bar
|
|
60
|
+
.gridOf=${() => this.grist}
|
|
61
|
+
name=${t('title.learning-course-list')}
|
|
62
|
+
list="courses"
|
|
63
|
+
axis="courses"
|
|
64
|
+
.labelOf=${(row) => row.name ?? ''}
|
|
65
|
+
></ox-assistant-selection-bar>
|
|
66
|
+
<ox-grist class="page-fill" search-mode="term" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>`;
|
|
54
67
|
}
|
|
55
68
|
async pageUpdated(changes) {
|
|
56
69
|
if (this.active && changes.active && this.gristConfig)
|
|
@@ -59,12 +72,18 @@ let LearningCourseList = class LearningCourseList extends localize(i18next)(Page
|
|
|
59
72
|
async pageInitialized() {
|
|
60
73
|
const open = (record) => record?.id && navigate(`learning-course-editor?id=${record.id}`);
|
|
61
74
|
this.gristConfig = {
|
|
62
|
-
rows: { appendable: false, selectable:
|
|
75
|
+
rows: { appendable: false, selectable: { multiple: true } },
|
|
63
76
|
pagination: { limit: 50, pages: [50, 100] },
|
|
64
77
|
emptyNote: { title: t('text.learning-no-course'), description: t('text.learning-no-course-description') },
|
|
65
|
-
|
|
78
|
+
/* The card says each line in words - 「본문 · 1분」, 「개정본 1번」, 「수강 13명 · 이수 0명(0%)」 - as the other lists' cards do. */
|
|
79
|
+
list: { fields: ['name', 'category'], details: ['taught', 'published', 'standing'] },
|
|
66
80
|
columns: [
|
|
67
|
-
|
|
81
|
+
{ type: 'gutter', gutterName: 'sequence' },
|
|
82
|
+
SELECTION_ROW_PICKER,
|
|
83
|
+
/* Rows are picked for the assistant; the course opens from its own button, as the other lists do. */
|
|
84
|
+
{ type: 'gutter', gutterName: 'button', icon: 'edit_note', handlers: { click: (_c, _d, _col, record) => open(record) } },
|
|
85
|
+
/* Locked: the course stays in view while the counts scroll by. */
|
|
86
|
+
{ ...column('name', t('field.learning-course-name'), 240), fixed: true },
|
|
68
87
|
column('category', t('field.learning-course-category'), 100, (value) => categoryWord(value)),
|
|
69
88
|
column('format', t('field.learning-format'), 160, (_v, _c, record) => formatOf(record)),
|
|
70
89
|
column('units', t('field.learning-unit-count'), 70, (_v, _c, record) => (record?.publishedVersion ? record.publishedVersion.units.length : ''), 'right'),
|
|
@@ -79,7 +98,10 @@ let LearningCourseList = class LearningCourseList extends localize(i18next)(Page
|
|
|
79
98
|
column('rate', t('field.learning-completion-rate'), 80, (_v, _c, record) => rateOf(record), 'right'),
|
|
80
99
|
column('source', t('field.learning-source-kind'), 100, (_v, _c, record) => sourceKindWord(record?.publishedVersion?.sourceKind)),
|
|
81
100
|
column('manager', t('field.learning-course-manager'), 100, (_v, _c, record) => record?.manager?.name ?? ''),
|
|
82
|
-
column('updatedAt', t('field.learning-updated-at'), 110, (value) => dayOf(value))
|
|
101
|
+
column('updatedAt', t('field.learning-updated-at'), 110, (value) => dayOf(value)),
|
|
102
|
+
/* The card's own lines - the grid shows their parts in columns. */
|
|
103
|
+
{ ...column('taught', '', 0, (_v, _c, record) => [formatOf(record), expectedOf(record)].filter(Boolean).join(' · ')), hidden: true },
|
|
104
|
+
{ ...column('standing', '', 0, (_v, _c, record) => standingOf(record)), hidden: true }
|
|
83
105
|
]
|
|
84
106
|
};
|
|
85
107
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"learning-course-list.js","sourceRoot":"","sources":["../../client/pages/learning-course-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport { openCourseForm } from '../components/learning-course-form.js'\nimport { categoryWord, dayOf, minutesOf, sourceKindWord, stateWord, t, unitKindWord } from '../utils/learning-words.js'\n\n/**\n * 과정 관리 (ADR-0194 decision 10) - the domain's courses with the version learners take now. A row opens the course's\n * editor; a new course starts there.\n */\nconst column = (name: string, header: string, width: number, renderer?: (value: any, column: any, record: any) => any, align?: string) => ({\n type: 'string',\n name,\n header,\n width,\n record: { editable: false, ...(renderer ? { renderer } : {}), ...(align ? { align } : {}) }\n})\n\n/** What a course is taught with - the kinds of its published version's units, each once, in their order. */\nconst formatOf = (record: any) => [...new Set<string>((record?.publishedVersion?.units ?? []).map((one: any) => one.kind))].map(unitKindWord).join(' · ')\n\n/** How long it takes at least, as the server counts it (expectedSeconds) - in minutes, rounded up. */\nconst expectedOf = (record: any) => {\n const seconds = record?.publishedVersion?.expectedSeconds\n return seconds ? t('text.learning-minutes-value', { minutes: Math.max(1, minutesOf(seconds + 59)) }) : ''\n}\n\nconst quizOf = (record: any) => {\n if (!record?.publishedVersion) return ''\n const score = record.publishedVersion.passScore\n return score != null ? t('text.learning-score-value', { score }) : t('text.learning-quiz-none')\n}\n\n/** Completed of those enrolled, cancelled ones left out. */\nconst rateOf = (record: any) => {\n const counts = record?.enrollmentCounts\n return counts?.total ? `${Math.round((counts.completed / counts.total) * 100)}%` : ''\n}\n\n@customElement('learning-course-list')\nexport class LearningCourseList extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, css``]\n\n @state() gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: t('title.learning-course-list'),\n search: { handler: (search: string) => (this.grist.searchText = search), value: this.grist?.searchText || '' },\n actions: [{ title: t('button.learning-course-new'), icon: 'add', emphasis: { raised: true }, action: () => this.create() }]\n }\n }\n\n render() {\n return html`<ox-grist class=\"page-fill\" search-mode=\"term\" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>`\n }\n\n async pageUpdated(changes: any) {\n if (this.active && changes.active && this.gristConfig) this.grist?.fetch()\n }\n\n async pageInitialized() {\n const open = (record: any) => record?.id && navigate(`learning-course-editor?id=${record.id}`)\n this.gristConfig = {\n rows: { appendable: false, selectable: false, handlers: { click: (_c: any, _d: any, _col: any, record: any) => open(record) } },\n pagination: { limit: 50, pages: [50, 100] },\n emptyNote: { title: t('text.learning-no-course'), description: t('text.learning-no-course-description') },\n list: { fields: ['name', 'category'], details: ['format', 'expected', 'rate'] },\n columns: [\n column('name', t('field.learning-course-name'), 240),\n column('category', t('field.learning-course-category'), 100, (value: string) => categoryWord(value)),\n column('format', t('field.learning-format'), 160, (_v: any, _c: any, record: any) => formatOf(record)),\n column('units', t('field.learning-unit-count'), 70, (_v: any, _c: any, record: any) => (record?.publishedVersion ? record.publishedVersion.units.length : ''), 'right'),\n column('expected', t('field.learning-expected-time'), 90, (_v: any, _c: any, record: any) => expectedOf(record), 'right'),\n column('quiz', t('field.learning-pass-score'), 90, (_v: any, _c: any, record: any) => quizOf(record), 'right'),\n column('published', t('field.learning-published-version'), 120, (_v: any, _c: any, record: any) =>\n record?.publishedVersion ? t('text.learning-version-number', { number: record.publishedVersion.number }) : t('text.learning-not-published')\n ),\n column('enrolled', t('field.learning-enrolled'), 80, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.total ?? '', 'right'),\n column('assigned', stateWord('ASSIGNED'), 70, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.assigned ?? '', 'right'),\n column('inProgress', stateWord('IN_PROGRESS'), 70, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.inProgress ?? '', 'right'),\n column('completed', stateWord('COMPLETED'), 70, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.completed ?? '', 'right'),\n column('overdue', stateWord('OVERDUE'), 80, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.overdue ?? '', 'right'),\n column('rate', t('field.learning-completion-rate'), 80, (_v: any, _c: any, record: any) => rateOf(record), 'right'),\n column('source', t('field.learning-source-kind'), 100, (_v: any, _c: any, record: any) => sourceKindWord(record?.publishedVersion?.sourceKind)),\n column('manager', t('field.learning-course-manager'), 100, (_v: any, _c: any, record: any) => record?.manager?.name ?? ''),\n column('updatedAt', t('field.learning-updated-at'), 110, (value: string) => dayOf(value))\n ]\n }\n }\n\n async fetchHandler({ page, limit, sortings = [], filters = [], signal }: FetchOption) {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n courses(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n id name category updatedAt manager { id name }\n publishedVersion { id number passScore sourceKind expectedSeconds units { id kind } }\n enrollmentCounts { total assigned inProgress completed overdue }\n }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n if (response.errors?.length) throw response.errors[0]\n return { total: response.data.courses.total || 0, records: response.data.courses.items || [] }\n }\n\n private async create() {\n const course = await openCourseForm()\n if (course) navigate(`learning-course-editor?id=${course.id}`)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"learning-course-list.js","sourceRoot":"","sources":["../../client/pages/learning-course-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\nimport { SELECTION_ROW_PICKER } from '@things-factory/ai-assistant/dist-client/index.js'\n\nimport { openCourseForm } from '../components/learning-course-form.js'\nimport { categoryWord, dayOf, minutesOf, sourceKindWord, stateWord, t, unitKindWord } from '../utils/learning-words.js'\n\n/**\n * 과정 관리 (ADR-0194 decision 10) - the domain's courses with the version learners take now. A row opens the course's\n * editor; a new course starts there.\n */\nconst column = (name: string, header: string, width: number, renderer?: (value: any, column: any, record: any) => any, align?: string) => ({\n type: 'string',\n name,\n header,\n width,\n record: { editable: false, ...(renderer ? { renderer } : {}), ...(align ? { align } : {}) }\n})\n\n/** What a course is taught with - the kinds of its published version's units, each once, in their order. */\nconst formatOf = (record: any) => [...new Set<string>((record?.publishedVersion?.units ?? []).map((one: any) => one.kind))].map(unitKindWord).join(' · ')\n\n/** How long it takes at least, as the server counts it (expectedSeconds) - in minutes, rounded up. */\nconst expectedOf = (record: any) => {\n const seconds = record?.publishedVersion?.expectedSeconds\n return seconds ? t('text.learning-minutes-value', { minutes: Math.max(1, minutesOf(seconds + 59)) }) : ''\n}\n\nconst quizOf = (record: any) => {\n if (!record?.publishedVersion) return ''\n const score = record.publishedVersion.passScore\n return score != null ? t('text.learning-score-value', { score }) : t('text.learning-quiz-none')\n}\n\n/** Completed of those enrolled, cancelled ones left out. */\nconst rateOf = (record: any) => {\n const counts = record?.enrollmentCounts\n return counts?.total ? `${Math.round((counts.completed / counts.total) * 100)}%` : ''\n}\n\n/** How the course's enrolments stand, in words for its card - nothing while nobody is enrolled. */\nconst standingOf = (record: any) => {\n const counts = record?.enrollmentCounts\n return counts?.total ? t('text.learning-course-standing', { enrolled: counts.total, completed: counts.completed, rate: rateOf(record) }) : ''\n}\n\n@customElement('learning-course-list')\nexport class LearningCourseList extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, css``]\n\n @state() gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: t('title.learning-course-list'),\n search: { handler: (search: string) => (this.grist.searchText = search), value: this.grist?.searchText || '' },\n actions: [{ title: t('button.learning-course-new'), icon: 'add', emphasis: { raised: true }, action: () => this.create() }]\n }\n }\n\n render() {\n return html`<ox-assistant-selection-bar\n .gridOf=${() => this.grist}\n name=${t('title.learning-course-list')}\n list=\"courses\"\n axis=\"courses\"\n .labelOf=${(row: any) => row.name ?? ''}\n ></ox-assistant-selection-bar>\n <ox-grist class=\"page-fill\" search-mode=\"term\" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>`\n }\n\n async pageUpdated(changes: any) {\n if (this.active && changes.active && this.gristConfig) this.grist?.fetch()\n }\n\n async pageInitialized() {\n const open = (record: any) => record?.id && navigate(`learning-course-editor?id=${record.id}`)\n this.gristConfig = {\n rows: { appendable: false, selectable: { multiple: true } },\n pagination: { limit: 50, pages: [50, 100] },\n emptyNote: { title: t('text.learning-no-course'), description: t('text.learning-no-course-description') },\n /* The card says each line in words - 「본문 · 1분」, 「개정본 1번」, 「수강 13명 · 이수 0명(0%)」 - as the other lists' cards do. */\n list: { fields: ['name', 'category'], details: ['taught', 'published', 'standing'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n SELECTION_ROW_PICKER,\n /* Rows are picked for the assistant; the course opens from its own button, as the other lists do. */\n { type: 'gutter', gutterName: 'button', icon: 'edit_note', handlers: { click: (_c: any, _d: any, _col: any, record: any) => open(record) } },\n /* Locked: the course stays in view while the counts scroll by. */\n { ...column('name', t('field.learning-course-name'), 240), fixed: true },\n column('category', t('field.learning-course-category'), 100, (value: string) => categoryWord(value)),\n column('format', t('field.learning-format'), 160, (_v: any, _c: any, record: any) => formatOf(record)),\n column('units', t('field.learning-unit-count'), 70, (_v: any, _c: any, record: any) => (record?.publishedVersion ? record.publishedVersion.units.length : ''), 'right'),\n column('expected', t('field.learning-expected-time'), 90, (_v: any, _c: any, record: any) => expectedOf(record), 'right'),\n column('quiz', t('field.learning-pass-score'), 90, (_v: any, _c: any, record: any) => quizOf(record), 'right'),\n column('published', t('field.learning-published-version'), 120, (_v: any, _c: any, record: any) =>\n record?.publishedVersion ? t('text.learning-version-number', { number: record.publishedVersion.number }) : t('text.learning-not-published')\n ),\n column('enrolled', t('field.learning-enrolled'), 80, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.total ?? '', 'right'),\n column('assigned', stateWord('ASSIGNED'), 70, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.assigned ?? '', 'right'),\n column('inProgress', stateWord('IN_PROGRESS'), 70, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.inProgress ?? '', 'right'),\n column('completed', stateWord('COMPLETED'), 70, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.completed ?? '', 'right'),\n column('overdue', stateWord('OVERDUE'), 80, (_v: any, _c: any, record: any) => record?.enrollmentCounts?.overdue ?? '', 'right'),\n column('rate', t('field.learning-completion-rate'), 80, (_v: any, _c: any, record: any) => rateOf(record), 'right'),\n column('source', t('field.learning-source-kind'), 100, (_v: any, _c: any, record: any) => sourceKindWord(record?.publishedVersion?.sourceKind)),\n column('manager', t('field.learning-course-manager'), 100, (_v: any, _c: any, record: any) => record?.manager?.name ?? ''),\n column('updatedAt', t('field.learning-updated-at'), 110, (value: string) => dayOf(value)),\n /* The card's own lines - the grid shows their parts in columns. */\n { ...column('taught', '', 0, (_v: any, _c: any, record: any) => [formatOf(record), expectedOf(record)].filter(Boolean).join(' · ')), hidden: true },\n { ...column('standing', '', 0, (_v: any, _c: any, record: any) => standingOf(record)), hidden: true }\n ]\n }\n }\n\n async fetchHandler({ page, limit, sortings = [], filters = [], signal }: FetchOption) {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n courses(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n id name category updatedAt manager { id name }\n publishedVersion { id number passScore sourceKind expectedSeconds units { id kind } }\n enrollmentCounts { total assigned inProgress completed overdue }\n }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n if (response.errors?.length) throw response.errors[0]\n return { total: response.data.courses.total || 0, records: response.data.courses.items || [] }\n }\n\n private async create() {\n const course = await openCourseForm()\n if (course) navigate(`learning-course-editor?id=${course.id}`)\n }\n}\n"]}
|
|
@@ -13,6 +13,7 @@ import { PageView } from '@operato/shell';
|
|
|
13
13
|
import { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
|
|
14
14
|
import { isMobileDevice } from '@operato/utils';
|
|
15
15
|
import { hasPrivilege } from '@things-factory/auth-base/dist-client';
|
|
16
|
+
import { SELECTION_ROW_PICKER } from '@things-factory/ai-assistant/dist-client/index.js';
|
|
16
17
|
import { issueDocument } from '@things-factory/pdf/dist-client';
|
|
17
18
|
import { STATUS_ROW_FIELDS } from '../utils/learning-api.js';
|
|
18
19
|
import { dayOf, minutesOf, STATES, stateWord, t } from '../utils/learning-words.js';
|
|
@@ -62,7 +63,16 @@ let LearningEnrollmentStatus = class LearningEnrollmentStatus extends localize(i
|
|
|
62
63
|
}
|
|
63
64
|
render() {
|
|
64
65
|
return this.gristConfig
|
|
65
|
-
? html
|
|
66
|
+
? html `${this.manager
|
|
67
|
+
? html `<ox-assistant-selection-bar
|
|
68
|
+
.gridOf=${() => this.grist}
|
|
69
|
+
name=${t('title.learning-enrollment-status')}
|
|
70
|
+
list="learningEnrollments"
|
|
71
|
+
axis="enrollments"
|
|
72
|
+
.labelOf=${(row) => [row.name, row.course].filter(Boolean).join(' · ')}
|
|
73
|
+
></ox-assistant-selection-bar>`
|
|
74
|
+
: ''}
|
|
75
|
+
<ox-grist
|
|
66
76
|
class="page-fill"
|
|
67
77
|
search-mode="term"
|
|
68
78
|
.mode=${isMobileDevice() ? 'CARD' : 'GRID'}
|
|
@@ -113,7 +123,7 @@ let LearningEnrollmentStatus = class LearningEnrollmentStatus extends localize(i
|
|
|
113
123
|
emptyNote: { title: t('text.learning-no-enrollment') },
|
|
114
124
|
list: { fields: ['name', 'state'], details: ['course', 'dueOn'] },
|
|
115
125
|
columns: [
|
|
116
|
-
...(manager ? [{ type: 'gutter', gutterName: '
|
|
126
|
+
...(manager ? [{ type: 'gutter', gutterName: 'sequence' }, SELECTION_ROW_PICKER] : []),
|
|
117
127
|
column('name', t('field.learning-learner'), 120),
|
|
118
128
|
column('department', t('field.learning-department'), 120),
|
|
119
129
|
column('course', t('field.learning-course-name'), 220, manager ? { filter: { type: 'select', operator: 'eq', options: () => this.courseOptions() } } : {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"learning-enrollment-status.js","sourceRoot":"","sources":["../../client/pages/learning-enrollment-status.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-filters-form.js'\nimport '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport { html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client, MutationController, QUERY_ANSWERED, QueryController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\nimport { hasPrivilege } from '@things-factory/auth-base/dist-client'\nimport { issueDocument } from '@things-factory/pdf/dist-client'\n\nimport { STATUS_ROW_FIELDS } from '../utils/learning-api.js'\nimport { dayOf, minutesOf, STATES, stateWord, t } from '../utils/learning-words.js'\n\n/** The states the list opens on - a cancelled enrolment is shown only when its chip is pressed. */\nexport const SHOWN_STATES = STATES.filter(state => state !== 'CANCELLED')\n\nconst TEAM_ROW_FIELDS = 'id state departmentName units unitsDone user { id name } course { id name } enrollment { id status dueOn completedAt }'\n\n/**\n * 수강 현황 (ADR-0194 decisions 10, 11) - where enrolments stand: who, their department, the state today, units done,\n * minutes, score and the completion day. The conditions - course, state with its counts, search, sort, page - are the\n * grid's, kept in the address, and read by the server. A manager reminds and cancels from here and issues the completion\n * register of the course filtered (PDF, through the issued document ledger) or exports the list (Excel). A department\n * head sees the people of their departments - the state and days only.\n */\n@customElement('learning-enrollment-status')\nexport class LearningEnrollmentStatus extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles]\n\n /* A manager reads everyone; anyone else reads their departments - known once asked. */\n @state() private manager?: boolean\n @state() private gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n /* The courses the course filter offers - a manager's only. */\n private courses = new QueryController(this, gql`query { courses(pagination: { page: 1, limit: 500 }) { items { id name } } }`, {\n variables: () => (this.manager ? {} : undefined)\n })\n private reminder = new MutationController(this, gql`mutation ($ids: [String!]!) { remindEnrollments(ids: $ids) }`)\n private canceller = new MutationController(this, gql`mutation ($ids: [String!]!, $reason: String!) { cancelEnrollments(ids: $ids, reason: $reason) { id } }`, { showsError: true })\n\n /** The course the grid's `course` filter names - the completion register is of it. */\n private get filteredCourse(): { id: string; name: string } | undefined {\n const id = this.grist?.filters?.find((one: any) => one.name === 'course')?.value\n return id ? (this.courses.data?.courses?.items ?? []).find((one: any) => one.id === id) : undefined\n }\n\n get context() {\n const course = this.filteredCourse\n return {\n title: t('title.learning-enrollment-status'),\n search: { handler: (search: string) => (this.grist.searchText = search), value: this.grist?.searchText || '' },\n filter: { handler: () => this.grist.toggleHeadroom() },\n ...(this.manager\n ? {\n actions: [\n { title: t('button.learning-remind'), icon: 'notifications_active', action: () => this.remind() },\n { title: t('button.learning-cancel-enrollment'), icon: 'block', action: () => this.cancel() },\n ...(course ? [{ title: t('button.learning-register-pdf'), icon: 'picture_as_pdf', action: () => issueDocument({ kind: 'learning.completion-register', refId: course.id, withAsOf: true }) }] : [])\n ],\n exportable: { name: course ? t('text.learning-register-name', { course: course.name }) : t('title.learning-enrollment-status'), data: () => this.grist.exportRecords() }\n }\n : {})\n }\n }\n\n render() {\n return this.gristConfig\n ? html`<ox-grist\n class=\"page-fill\"\n search-mode=\"term\"\n .mode=${isMobileDevice() ? 'CARD' : 'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .countHandler=${this.countHandler.bind(this)}\n ?url-params-sensitive=${this.active}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\"><ox-filters-form without-search></ox-filters-form></div>\n </div>\n </ox-grist>`\n : html``\n }\n\n async pageInitialized() {\n this.manager = !!(await hasPrivilege({ category: 'course', privilege: 'query', domainOwnerGranted: true }))\n this.gristConfig = this.configOf(this.manager)\n this.updateContext()\n }\n\n /** The courses for the course filter, once the read of them has answered. */\n private async courseOptions(): Promise<{ value: string; display: string }[]> {\n if (this.courses.loading) await new Promise(resolve => this.addEventListener(QUERY_ANSWERED, resolve, { once: true }))\n return [{ value: '', display: t('text.learning-course-all') }, ...(this.courses.data?.courses?.items ?? []).map((one: any) => ({ value: one.id, display: one.name }))]\n }\n\n private recordOf(row: any) {\n const e = row.enrollment ?? {}\n return {\n id: row.id,\n name: row.user?.name ?? '',\n department: row.departmentName ?? '',\n course: row.course?.name ?? '',\n state: row.state,\n dueOn: e.dueOn ?? '',\n units: `${row.unitsDone}/${row.units}`,\n minutes: e.learnedSeconds != null ? minutesOf(e.learnedSeconds) : null,\n score: e.score ?? null,\n completedOn: dayOf(e.completedAt),\n certificateNo: e.certificateNo ?? '',\n open: e.status === 'ASSIGNED' || e.status === 'IN_PROGRESS'\n }\n }\n\n private configOf(manager: boolean) {\n const column = (name: string, header: string, width: any, extra: any = {}) => ({ type: 'string', name, header, width, sortable: false, ...extra, record: { editable: false, ...extra.record } })\n return {\n rows: { appendable: false, selectable: manager ? { multiple: true } : false },\n pagination: { limit: 100, pages: [50, 100, 500] },\n emptyNote: { title: t('text.learning-no-enrollment') },\n list: { fields: ['name', 'state'], details: ['course', 'dueOn'] },\n columns: [\n ...(manager ? [{ type: 'gutter', gutterName: 'row-selector', multiple: true }] : []),\n column('name', t('field.learning-learner'), 120),\n column('department', t('field.learning-department'), 120),\n column('course', t('field.learning-course-name'), 220, manager ? { filter: { type: 'select', operator: 'eq', options: () => this.courseOptions() } } : {}),\n column('state', t('field.learning-state'), 100, {\n record: { renderer: (value: string) => stateWord(value) },\n /* Chips with their counts; the list opens on every state but cancelled, and none pressed is every state. */\n filter: { type: 'select-buttons', operator: 'in', value: SHOWN_STATES, options: STATES.map(value => ({ value, display: stateWord(value) })) }\n }),\n column('dueOn', t('field.learning-due-on'), 110, { sortable: true }),\n column('units', t('field.learning-units-done'), 90, { record: { align: 'right' } }),\n ...(manager ? [column('minutes', t('field.learning-minutes'), 120, { record: { align: 'right' } }), column('score', t('field.learning-score'), 70, { record: { align: 'right' } })] : []),\n column('completedOn', t('field.learning-completed-on'), 110),\n ...(manager ? [column('certificateNo', t('field.learning-certificate-no'), 180)] : [])\n ],\n sorters: [{ name: 'dueOn' }]\n }\n }\n\n async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [], search, signal }: FetchOption) {\n const door = this.manager ? 'enrollmentStatus' : 'teamEnrollmentStatus'\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $search: String) {\n responses: ${door}(filters: $filters, pagination: $pagination, sortings: $sortings, search: $search) {\n items { ${this.manager ? STATUS_ROW_FIELDS : TEAM_ROW_FIELDS} }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings, search: search?.trim() || undefined },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n if (response.errors?.length) throw response.errors[0]\n /* The register action names the course filtered - said again with each read. */\n this.updateContext()\n return { total: response.data.responses.total, records: response.data.responses.items.map((row: any) => this.recordOf(row)) }\n }\n\n /* The state chips' numbers, under the list's other conditions - the chips' own left out, counted by the server. */\n async countHandler({ filters = [], search, signal }: FetchOption) {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $search: String) {\n enrollmentStateCounts(filters: $filters, search: $search) { value count }\n }\n `,\n variables: { filters: filters.filter((one: any) => one.name !== 'state'), search: search?.trim() || undefined },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n if (response.errors?.length) throw response.errors[0]\n return { state: Object.fromEntries(response.data.enrollmentStateCounts.map((row: { value: string; count: number }) => [row.value, row.count])) }\n }\n\n private selectedOpen(): any[] {\n return (this.grist?.selected ?? []).filter((one: any) => one.open)\n }\n\n private async remind() {\n const ids = this.selectedOpen().map(one => one.id)\n if (!ids.length) return notify({ level: 'warn', message: t('text.learning-pick-open') })\n const done = await this.reminder.run({ ids })\n if (done) notify({ message: t('text.learning-reminded', { count: done.remindEnrollments }) })\n }\n\n /** Cancels the enrolments picked - all but the cancelled ones - with one reason, together. */\n private async cancel() {\n const ids = (this.grist?.selected ?? []).filter((one: any) => one.state !== 'CANCELLED').map((one: any) => one.id)\n if (!ids.length) return notify({ level: 'warn', message: t('text.learning-pick-cancellable') })\n const reason = await OxPrompt.prompt({ title: t('title.learning-cancel-enrollments', { count: ids.length }), text: t('field.learning-cancel-reason'), multiline: true })\n if (reason === null || !String(reason).trim()) return\n const done = await this.canceller.run({ ids, reason: String(reason).trim() })\n if (done) {\n notify({ message: t('text.learning-cancelled', { count: done.cancelEnrollments.length }) })\n this.grist.fetch()\n }\n }\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"learning-enrollment-status.js","sourceRoot":"","sources":["../../client/pages/learning-enrollment-status.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist/ox-filters-form.js'\nimport '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport { html } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client, MutationController, QUERY_ANSWERED, QueryController } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\nimport { hasPrivilege } from '@things-factory/auth-base/dist-client'\nimport { SELECTION_ROW_PICKER } from '@things-factory/ai-assistant/dist-client/index.js'\nimport { issueDocument } from '@things-factory/pdf/dist-client'\n\nimport { STATUS_ROW_FIELDS } from '../utils/learning-api.js'\nimport { dayOf, minutesOf, STATES, stateWord, t } from '../utils/learning-words.js'\n\n/** The states the list opens on - a cancelled enrolment is shown only when its chip is pressed. */\nexport const SHOWN_STATES = STATES.filter(state => state !== 'CANCELLED')\n\nconst TEAM_ROW_FIELDS = 'id state departmentName units unitsDone user { id name } course { id name } enrollment { id status dueOn completedAt }'\n\n/**\n * 수강 현황 (ADR-0194 decisions 10, 11) - where enrolments stand: who, their department, the state today, units done,\n * minutes, score and the completion day. The conditions - course, state with its counts, search, sort, page - are the\n * grid's, kept in the address, and read by the server. A manager reminds and cancels from here and issues the completion\n * register of the course filtered (PDF, through the issued document ledger) or exports the list (Excel). A department\n * head sees the people of their departments - the state and days only.\n */\n@customElement('learning-enrollment-status')\nexport class LearningEnrollmentStatus extends localize(i18next)(PageView) {\n static styles = [WorkSurfacePageStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles]\n\n /* A manager reads everyone; anyone else reads their departments - known once asked. */\n @state() private manager?: boolean\n @state() private gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n /* The courses the course filter offers - a manager's only. */\n private courses = new QueryController(this, gql`query { courses(pagination: { page: 1, limit: 500 }) { items { id name } } }`, {\n variables: () => (this.manager ? {} : undefined)\n })\n private reminder = new MutationController(this, gql`mutation ($ids: [String!]!) { remindEnrollments(ids: $ids) }`)\n private canceller = new MutationController(this, gql`mutation ($ids: [String!]!, $reason: String!) { cancelEnrollments(ids: $ids, reason: $reason) { id } }`, { showsError: true })\n\n /** The course the grid's `course` filter names - the completion register is of it. */\n private get filteredCourse(): { id: string; name: string } | undefined {\n const id = this.grist?.filters?.find((one: any) => one.name === 'course')?.value\n return id ? (this.courses.data?.courses?.items ?? []).find((one: any) => one.id === id) : undefined\n }\n\n get context() {\n const course = this.filteredCourse\n return {\n title: t('title.learning-enrollment-status'),\n search: { handler: (search: string) => (this.grist.searchText = search), value: this.grist?.searchText || '' },\n filter: { handler: () => this.grist.toggleHeadroom() },\n ...(this.manager\n ? {\n actions: [\n { title: t('button.learning-remind'), icon: 'notifications_active', action: () => this.remind() },\n { title: t('button.learning-cancel-enrollment'), icon: 'block', action: () => this.cancel() },\n ...(course ? [{ title: t('button.learning-register-pdf'), icon: 'picture_as_pdf', action: () => issueDocument({ kind: 'learning.completion-register', refId: course.id, withAsOf: true }) }] : [])\n ],\n exportable: { name: course ? t('text.learning-register-name', { course: course.name }) : t('title.learning-enrollment-status'), data: () => this.grist.exportRecords() }\n }\n : {})\n }\n }\n\n render() {\n return this.gristConfig\n ? html`${this.manager\n ? html`<ox-assistant-selection-bar\n .gridOf=${() => this.grist}\n name=${t('title.learning-enrollment-status')}\n list=\"learningEnrollments\"\n axis=\"enrollments\"\n .labelOf=${(row: any) => [row.name, row.course].filter(Boolean).join(' · ')}\n ></ox-assistant-selection-bar>`\n : ''}\n <ox-grist\n class=\"page-fill\"\n search-mode=\"term\"\n .mode=${isMobileDevice() ? 'CARD' : 'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .countHandler=${this.countHandler.bind(this)}\n ?url-params-sensitive=${this.active}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\"><ox-filters-form without-search></ox-filters-form></div>\n </div>\n </ox-grist>`\n : html``\n }\n\n async pageInitialized() {\n this.manager = !!(await hasPrivilege({ category: 'course', privilege: 'query', domainOwnerGranted: true }))\n this.gristConfig = this.configOf(this.manager)\n this.updateContext()\n }\n\n /** The courses for the course filter, once the read of them has answered. */\n private async courseOptions(): Promise<{ value: string; display: string }[]> {\n if (this.courses.loading) await new Promise(resolve => this.addEventListener(QUERY_ANSWERED, resolve, { once: true }))\n return [{ value: '', display: t('text.learning-course-all') }, ...(this.courses.data?.courses?.items ?? []).map((one: any) => ({ value: one.id, display: one.name }))]\n }\n\n private recordOf(row: any) {\n const e = row.enrollment ?? {}\n return {\n id: row.id,\n name: row.user?.name ?? '',\n department: row.departmentName ?? '',\n course: row.course?.name ?? '',\n state: row.state,\n dueOn: e.dueOn ?? '',\n units: `${row.unitsDone}/${row.units}`,\n minutes: e.learnedSeconds != null ? minutesOf(e.learnedSeconds) : null,\n score: e.score ?? null,\n completedOn: dayOf(e.completedAt),\n certificateNo: e.certificateNo ?? '',\n open: e.status === 'ASSIGNED' || e.status === 'IN_PROGRESS'\n }\n }\n\n private configOf(manager: boolean) {\n const column = (name: string, header: string, width: any, extra: any = {}) => ({ type: 'string', name, header, width, sortable: false, ...extra, record: { editable: false, ...extra.record } })\n return {\n rows: { appendable: false, selectable: manager ? { multiple: true } : false },\n pagination: { limit: 100, pages: [50, 100, 500] },\n emptyNote: { title: t('text.learning-no-enrollment') },\n list: { fields: ['name', 'state'], details: ['course', 'dueOn'] },\n columns: [\n ...(manager ? [{ type: 'gutter', gutterName: 'sequence' }, SELECTION_ROW_PICKER] : []),\n column('name', t('field.learning-learner'), 120),\n column('department', t('field.learning-department'), 120),\n column('course', t('field.learning-course-name'), 220, manager ? { filter: { type: 'select', operator: 'eq', options: () => this.courseOptions() } } : {}),\n column('state', t('field.learning-state'), 100, {\n record: { renderer: (value: string) => stateWord(value) },\n /* Chips with their counts; the list opens on every state but cancelled, and none pressed is every state. */\n filter: { type: 'select-buttons', operator: 'in', value: SHOWN_STATES, options: STATES.map(value => ({ value, display: stateWord(value) })) }\n }),\n column('dueOn', t('field.learning-due-on'), 110, { sortable: true }),\n column('units', t('field.learning-units-done'), 90, { record: { align: 'right' } }),\n ...(manager ? [column('minutes', t('field.learning-minutes'), 120, { record: { align: 'right' } }), column('score', t('field.learning-score'), 70, { record: { align: 'right' } })] : []),\n column('completedOn', t('field.learning-completed-on'), 110),\n ...(manager ? [column('certificateNo', t('field.learning-certificate-no'), 180)] : [])\n ],\n sorters: [{ name: 'dueOn' }]\n }\n }\n\n async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [], search, signal }: FetchOption) {\n const door = this.manager ? 'enrollmentStatus' : 'teamEnrollmentStatus'\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!], $search: String) {\n responses: ${door}(filters: $filters, pagination: $pagination, sortings: $sortings, search: $search) {\n items { ${this.manager ? STATUS_ROW_FIELDS : TEAM_ROW_FIELDS} }\n total\n }\n }\n `,\n variables: { filters, pagination: { page, limit }, sortings, search: search?.trim() || undefined },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n if (response.errors?.length) throw response.errors[0]\n /* The register action names the course filtered - said again with each read. */\n this.updateContext()\n return { total: response.data.responses.total, records: response.data.responses.items.map((row: any) => this.recordOf(row)) }\n }\n\n /* The state chips' numbers, under the list's other conditions - the chips' own left out, counted by the server. */\n async countHandler({ filters = [], search, signal }: FetchOption) {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $search: String) {\n enrollmentStateCounts(filters: $filters, search: $search) { value count }\n }\n `,\n variables: { filters: filters.filter((one: any) => one.name !== 'state'), search: search?.trim() || undefined },\n fetchPolicy: 'no-cache',\n context: { fetchOptions: { signal }, queryDeduplication: false }\n })\n if (response.errors?.length) throw response.errors[0]\n return { state: Object.fromEntries(response.data.enrollmentStateCounts.map((row: { value: string; count: number }) => [row.value, row.count])) }\n }\n\n private selectedOpen(): any[] {\n return (this.grist?.selected ?? []).filter((one: any) => one.open)\n }\n\n private async remind() {\n const ids = this.selectedOpen().map(one => one.id)\n if (!ids.length) return notify({ level: 'warn', message: t('text.learning-pick-open') })\n const done = await this.reminder.run({ ids })\n if (done) notify({ message: t('text.learning-reminded', { count: done.remindEnrollments }) })\n }\n\n /** Cancels the enrolments picked - all but the cancelled ones - with one reason, together. */\n private async cancel() {\n const ids = (this.grist?.selected ?? []).filter((one: any) => one.state !== 'CANCELLED').map((one: any) => one.id)\n if (!ids.length) return notify({ level: 'warn', message: t('text.learning-pick-cancellable') })\n const reason = await OxPrompt.prompt({ title: t('title.learning-cancel-enrollments', { count: ids.length }), text: t('field.learning-cancel-reason'), multiline: true })\n if (reason === null || !String(reason).trim()) return\n const done = await this.canceller.run({ ids, reason: String(reason).trim() })\n if (done) {\n notify({ message: t('text.learning-cancelled', { count: done.cancelEnrollments.length }) })\n this.grist.fetch()\n }\n }\n}\n\n"]}
|
|
@@ -23,6 +23,12 @@ export declare class LearningView extends LearningView_base {
|
|
|
23
23
|
private switching;
|
|
24
24
|
private read;
|
|
25
25
|
private savedAt?;
|
|
26
|
+
/** The notes of the PDF unit open, as written here since they were read (ADR-0194 amendment 2) - keyed by enrolment and unit. */
|
|
27
|
+
private written?;
|
|
28
|
+
private noteError;
|
|
29
|
+
private noteRead;
|
|
30
|
+
private noteSave;
|
|
31
|
+
private noteSaves;
|
|
26
32
|
private tickSave;
|
|
27
33
|
private quizStart;
|
|
28
34
|
private quizSubmit;
|
|
@@ -52,6 +58,10 @@ export declare class LearningView extends LearningView_base {
|
|
|
52
58
|
* back too - starts from what the server counted, not from what the page read when it opened (2026-11-08).
|
|
53
59
|
*/
|
|
54
60
|
private open;
|
|
61
|
+
/** The notes of a PDF unit by page: as written here, else as read - undefined while they are read. */
|
|
62
|
+
private notesOf;
|
|
63
|
+
/** A page's notes changed - kept here at once and sent, one after another. */
|
|
64
|
+
private note;
|
|
55
65
|
/** Keeps the server's answer for a unit - the next time it opens, it starts there. */
|
|
56
66
|
private keep;
|
|
57
67
|
private tick;
|
|
@@ -41,6 +41,18 @@ let LearningView = class LearningView extends localize(i18next)(PageView) {
|
|
|
41
41
|
variables: () => (this.enrollmentId ? { id: this.enrollmentId } : undefined),
|
|
42
42
|
keepPrevious: true
|
|
43
43
|
});
|
|
44
|
+
this.noteError = '';
|
|
45
|
+
/* A PDF unit's notes - read when it opens, the learner's own. Never from the cache: a note written is not put there. */
|
|
46
|
+
this.noteRead = new QueryController(this, gql `query ($enrollmentId: String!, $unitId: String!) { myLessonNotes(enrollmentId: $enrollmentId, unitId: $unitId) { page strokes } }`, {
|
|
47
|
+
variables: () => {
|
|
48
|
+
const unit = this.enrollment?.units?.find((row) => row.unit.id === this.unitId)?.unit;
|
|
49
|
+
return unit?.kind === 'PDF' && !this.switching ? { enrollmentId: this.enrollmentId, unitId: unit.id } : undefined;
|
|
50
|
+
},
|
|
51
|
+
fetchPolicy: 'network-only'
|
|
52
|
+
});
|
|
53
|
+
this.noteSave = new MutationController(this, gql `mutation ($enrollmentId: String!, $unitId: String!, $page: Int!, $strokes: Object!) { saveLessonNote(enrollmentId: $enrollmentId, unitId: $unitId, page: $page, strokes: $strokes) { id updatedAt } }`, { showsError: true });
|
|
54
|
+
/* Notes go one after another, each the page's whole strokes - the last written is the one kept. */
|
|
55
|
+
this.noteSaves = Promise.resolve();
|
|
44
56
|
this.tickSave = new MutationController(this, gql(TICK_MUTATION), { showsError: true });
|
|
45
57
|
this.quizStart = new MutationController(this, gql `mutation ($enrollmentId: String!) { startQuizAttempt(enrollmentId: $enrollmentId) { attemptId questions { id kind text choices { id text } } } }`, {
|
|
46
58
|
showsError: true
|
|
@@ -188,6 +200,7 @@ let LearningView = class LearningView extends localize(i18next)(PageView) {
|
|
|
188
200
|
if (id && id !== this.enrollmentId) {
|
|
189
201
|
this.enrollmentId = id;
|
|
190
202
|
this.unitId = '';
|
|
203
|
+
this.written = undefined;
|
|
191
204
|
this.drawn = undefined;
|
|
192
205
|
this.result = undefined;
|
|
193
206
|
}
|
|
@@ -267,6 +280,8 @@ let LearningView = class LearningView extends localize(i18next)(PageView) {
|
|
|
267
280
|
* never under the next. Keyed by the unit alone, the same unit opened from another enrolment kept the element and
|
|
268
281
|
* its clock, and the first enrolment's seconds went out under the second (2026-11-08).
|
|
269
282
|
*/
|
|
283
|
+
/* A PDF's notes, once read - until then nothing is written, so a stroke never replaces a page not read yet. */
|
|
284
|
+
const notes = unit.kind === 'PDF' ? this.notesOf(one.id, unit.id) : undefined;
|
|
270
285
|
return html `<h3>${unit.name}</h3>
|
|
271
286
|
${keyed(`${one.id}:${unit.id}`, html `<ox-lesson
|
|
272
287
|
.kind=${unit.kind}
|
|
@@ -281,10 +296,13 @@ let LearningView = class LearningView extends localize(i18next)(PageView) {
|
|
|
281
296
|
.requiredSeconds=${unit.minSeconds ?? 60}
|
|
282
297
|
.learnedSeconds=${progress.learnedSeconds ?? 0}
|
|
283
298
|
.done=${!!row.progress?.doneAt || !!progress.doneAt || one.status === 'COMPLETED'}
|
|
299
|
+
.notes=${notes ?? {}}
|
|
300
|
+
?noting=${!!notes && one.status !== 'CANCELLED'}
|
|
284
301
|
@lesson-tick=${(e) => this.tick(one.id, unit.id, e.detail)}
|
|
302
|
+
@lesson-note=${(e) => this.note(one.id, unit.id, e.detail)}
|
|
285
303
|
></ox-lesson>`)}
|
|
286
|
-
${this.tickError
|
|
287
|
-
? html `<div class="error" role="alert">${this.tickError}</div>`
|
|
304
|
+
${this.tickError || this.noteError
|
|
305
|
+
? html `<div class="error" role="alert">${this.tickError || this.noteError}</div>`
|
|
288
306
|
: html `<p class="saved" role="status">${this.savedAt ? t('text.learning-saved-at', { time: this.savedAt.toLocaleTimeString(i18next.language) }) : ''}</p>`}`;
|
|
289
307
|
}
|
|
290
308
|
renderQuiz(one) {
|
|
@@ -329,13 +347,41 @@ let LearningView = class LearningView extends localize(i18next)(PageView) {
|
|
|
329
347
|
if (unitId === this.unitId || this.switching)
|
|
330
348
|
return;
|
|
331
349
|
this.tickError = '';
|
|
350
|
+
this.noteError = '';
|
|
332
351
|
this.switching = true;
|
|
333
352
|
await this.updateComplete;
|
|
334
353
|
await this.ticks;
|
|
354
|
+
await this.noteSaves;
|
|
335
355
|
this.starts.reopen();
|
|
356
|
+
this.written = undefined;
|
|
336
357
|
this.unitId = unitId;
|
|
337
358
|
this.switching = false;
|
|
338
359
|
}
|
|
360
|
+
/** The notes of a PDF unit by page: as written here, else as read - undefined while they are read. */
|
|
361
|
+
notesOf(enrollmentId, unitId) {
|
|
362
|
+
const key = `${enrollmentId}:${unitId}`;
|
|
363
|
+
if (this.written?.key === key)
|
|
364
|
+
return this.written.pages;
|
|
365
|
+
const rows = this.noteRead.data?.myLessonNotes;
|
|
366
|
+
if (!rows)
|
|
367
|
+
return undefined;
|
|
368
|
+
return Object.fromEntries(rows.map((row) => [row.page, row.strokes ?? []]));
|
|
369
|
+
}
|
|
370
|
+
/** A page's notes changed - kept here at once and sent, one after another. */
|
|
371
|
+
note(enrollmentId, unitId, { page, strokes }) {
|
|
372
|
+
const key = `${enrollmentId}:${unitId}`;
|
|
373
|
+
const pages = this.notesOf(enrollmentId, unitId) ?? {};
|
|
374
|
+
this.written = { key, pages: { ...pages, [page]: strokes } };
|
|
375
|
+
this.noteSaves = this.noteSaves.then(async () => {
|
|
376
|
+
const answer = await this.noteSave.run({ enrollmentId, unitId, page, strokes });
|
|
377
|
+
if (!answer) {
|
|
378
|
+
this.noteError = this.errorText(this.noteSave.error);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
this.noteError = '';
|
|
382
|
+
this.savedAt = new Date();
|
|
383
|
+
});
|
|
384
|
+
}
|
|
339
385
|
/** Keeps the server's answer for a unit - the next time it opens, it starts there. */
|
|
340
386
|
keep(enrollmentId, unitId, answer) {
|
|
341
387
|
this.starts.keep(enrollmentId, unitId, answer?.progress);
|
|
@@ -437,8 +483,18 @@ __decorate([
|
|
|
437
483
|
], LearningView.prototype, "switching", void 0);
|
|
438
484
|
__decorate([
|
|
439
485
|
state(),
|
|
440
|
-
__metadata("design:type", Date
|
|
486
|
+
__metadata("design:type", Date
|
|
487
|
+
/** The notes of the PDF unit open, as written here since they were read (ADR-0194 amendment 2) - keyed by enrolment and unit. */
|
|
488
|
+
)
|
|
441
489
|
], LearningView.prototype, "savedAt", void 0);
|
|
490
|
+
__decorate([
|
|
491
|
+
state(),
|
|
492
|
+
__metadata("design:type", Object)
|
|
493
|
+
], LearningView.prototype, "written", void 0);
|
|
494
|
+
__decorate([
|
|
495
|
+
state(),
|
|
496
|
+
__metadata("design:type", Object)
|
|
497
|
+
], LearningView.prototype, "noteError", void 0);
|
|
442
498
|
LearningView = __decorate([
|
|
443
499
|
customElement('learning-view')
|
|
444
500
|
], LearningView);
|