@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.
Files changed (40) hide show
  1. package/dist-client/pages/learning-course-list.js +27 -5
  2. package/dist-client/pages/learning-course-list.js.map +1 -1
  3. package/dist-client/pages/learning-enrollment-status.js +12 -2
  4. package/dist-client/pages/learning-enrollment-status.js.map +1 -1
  5. package/dist-client/pages/learning-view.d.ts +10 -0
  6. package/dist-client/pages/learning-view.js +59 -3
  7. package/dist-client/pages/learning-view.js.map +1 -1
  8. package/dist-client/tsconfig.tsbuildinfo +1 -1
  9. package/dist-server/ai-reads.d.ts +1 -0
  10. package/dist-server/ai-reads.js +26 -2
  11. package/dist-server/ai-reads.js.map +1 -1
  12. package/dist-server/service/enrollment/enrollment-mutation.d.ts +2 -0
  13. package/dist-server/service/enrollment/enrollment-mutation.js +18 -0
  14. package/dist-server/service/enrollment/enrollment-mutation.js.map +1 -1
  15. package/dist-server/service/enrollment/enrollment-query.d.ts +2 -0
  16. package/dist-server/service/enrollment/enrollment-query.js +15 -0
  17. package/dist-server/service/enrollment/enrollment-query.js.map +1 -1
  18. package/dist-server/service/enrollment/enrollment-service.d.ts +4 -0
  19. package/dist-server/service/enrollment/enrollment-service.js +1 -0
  20. package/dist-server/service/enrollment/enrollment-service.js.map +1 -1
  21. package/dist-server/service/enrollment/index.d.ts +3 -1
  22. package/dist-server/service/enrollment/index.js +3 -1
  23. package/dist-server/service/enrollment/index.js.map +1 -1
  24. package/dist-server/service/enrollment/lesson-note-service.d.ts +15 -0
  25. package/dist-server/service/enrollment/lesson-note-service.js +86 -0
  26. package/dist-server/service/enrollment/lesson-note-service.js.map +1 -0
  27. package/dist-server/service/enrollment/lesson-note.d.ts +35 -0
  28. package/dist-server/service/enrollment/lesson-note.js +107 -0
  29. package/dist-server/service/enrollment/lesson-note.js.map +1 -0
  30. package/dist-server/service/index.d.ts +2 -1
  31. package/dist-server/service/index.js +1 -0
  32. package/dist-server/service/index.js.map +1 -1
  33. package/dist-server/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +14 -14
  35. package/translations/en.json +22 -16
  36. package/translations/es.json +7 -1
  37. package/translations/ja.json +7 -1
  38. package/translations/ko.json +23 -17
  39. package/translations/ms.json +7 -1
  40. 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-grist class="page-fill" search-mode="term" .mode=${isMobileDevice() ? 'CARD' : 'GRID'} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}></ox-grist>`;
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: false, handlers: { click: (_c, _d, _col, record) => open(record) } },
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
- list: { fields: ['name', 'category'], details: ['format', 'expected', 'rate'] },
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
- column('name', t('field.learning-course-name'), 240),
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 `<ox-grist
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: 'row-selector', multiple: true }] : []),
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);