@things-factory/organization 8.0.0-beta.8 → 8.0.0

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 (94) hide show
  1. package/client/bootstrap.ts +23 -0
  2. package/client/component/approval-line-brief.ts +119 -0
  3. package/client/component/approval-line-items-editor-popup.ts +91 -0
  4. package/client/component/approval-line-items-editor.ts +325 -0
  5. package/client/component/approval-line-selector.ts +235 -0
  6. package/client/component/approval-line-templates-manager.ts +229 -0
  7. package/client/component/approval-line-view.ts +122 -0
  8. package/client/component/assignees-editor-popup.ts +79 -0
  9. package/client/component/assignees-editor.ts +217 -0
  10. package/client/component/assignees-view.ts +55 -0
  11. package/client/component/department-selector.ts +151 -0
  12. package/client/component/department-view.ts +107 -0
  13. package/client/component/index.ts +16 -0
  14. package/client/component/recipients-editor-popup.ts +79 -0
  15. package/client/component/recipients-editor.ts +212 -0
  16. package/client/component/recipients-view.ts +55 -0
  17. package/client/grist-editor/grist-editor-approval-line.ts +70 -0
  18. package/client/grist-editor/grist-editor-assignees.ts +69 -0
  19. package/client/grist-editor/grist-editor-department-object.ts +78 -0
  20. package/client/grist-editor/grist-editor-recipients.ts +69 -0
  21. package/client/grist-editor/grist-renderer-approval-line.ts +13 -0
  22. package/client/grist-editor/grist-renderer-assignees.ts +13 -0
  23. package/client/grist-editor/grist-renderer-department-object.ts +13 -0
  24. package/client/grist-editor/grist-renderer-recipients.ts +13 -0
  25. package/client/index.ts +2 -0
  26. package/client/pages/approval-line/common-approval-line-templates-page.ts +382 -0
  27. package/client/pages/approval-line/my-approval-line-templates-page.ts +385 -0
  28. package/client/pages/department/department-importer.ts +87 -0
  29. package/client/pages/department/department-list-page.ts +450 -0
  30. package/client/pages/department/department-tree-page.ts +379 -0
  31. package/client/pages/employee/employee-importer.ts +87 -0
  32. package/client/pages/employee/employee-list-page.ts +772 -0
  33. package/client/pages/employee/employees-by-department.ts +519 -0
  34. package/client/route.ts +27 -0
  35. package/client/tsconfig.json +13 -0
  36. package/client/types/approval-line.ts +52 -0
  37. package/client/types/contact.ts +51 -0
  38. package/client/types/department.ts +29 -0
  39. package/client/types/employee.ts +50 -0
  40. package/client/types/index.ts +5 -0
  41. package/client/types/org-member.ts +27 -0
  42. package/dist-client/bootstrap.js +1 -8
  43. package/dist-client/bootstrap.js.map +1 -1
  44. package/dist-client/pages/employee/employee-list-page.js +3 -3
  45. package/dist-client/pages/employee/employee-list-page.js.map +1 -1
  46. package/dist-client/pages/employee/employees-by-department.js +2 -2
  47. package/dist-client/pages/employee/employees-by-department.js.map +1 -1
  48. package/dist-client/tsconfig.tsbuildinfo +1 -1
  49. package/dist-server/service/employee/employee-history.d.ts +2 -6
  50. package/dist-server/service/employee/employee-history.js +3 -23
  51. package/dist-server/service/employee/employee-history.js.map +1 -1
  52. package/dist-server/service/employee/employee-query.js +1 -1
  53. package/dist-server/service/employee/employee-query.js.map +1 -1
  54. package/dist-server/service/employee/employee-type.d.ts +5 -13
  55. package/dist-server/service/employee/employee-type.js +7 -39
  56. package/dist-server/service/employee/employee-type.js.map +1 -1
  57. package/dist-server/service/employee/employee.d.ts +2 -6
  58. package/dist-server/service/employee/employee.js +3 -23
  59. package/dist-server/service/employee/employee.js.map +1 -1
  60. package/dist-server/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +12 -12
  62. package/server/controllers/register-employee-as-system-user.ts +136 -0
  63. package/server/index.ts +3 -0
  64. package/server/migrations/1723861013111-seed-organization-codes.ts +127 -0
  65. package/server/migrations/index.ts +9 -0
  66. package/server/routes.ts +26 -0
  67. package/server/service/approval-line/approval-line-item.ts +42 -0
  68. package/server/service/approval-line/approval-line-mutation.ts +394 -0
  69. package/server/service/approval-line/approval-line-query.ts +208 -0
  70. package/server/service/approval-line/approval-line-type.ts +63 -0
  71. package/server/service/approval-line/approval-line.ts +123 -0
  72. package/server/service/approval-line/index.ts +7 -0
  73. package/server/service/department/department-history.ts +141 -0
  74. package/server/service/department/department-mutation.ts +231 -0
  75. package/server/service/department/department-query.ts +131 -0
  76. package/server/service/department/department-type.ts +74 -0
  77. package/server/service/department/department.ts +116 -0
  78. package/server/service/department/event-subscriber.ts +17 -0
  79. package/server/service/department/index.ts +9 -0
  80. package/server/service/employee/employee-history.ts +173 -0
  81. package/server/service/employee/employee-mutation.ts +386 -0
  82. package/server/service/employee/employee-query.ts +172 -0
  83. package/server/service/employee/employee-type.ts +176 -0
  84. package/server/service/employee/employee.ts +177 -0
  85. package/server/service/employee/event-subscriber.ts +17 -0
  86. package/server/service/employee/index.ts +9 -0
  87. package/server/service/index.ts +39 -0
  88. package/server/tsconfig.json +10 -0
  89. package/dist-client/filters-form/filter-department-object.d.ts +0 -3
  90. package/dist-client/filters-form/filter-department-object.js +0 -8
  91. package/dist-client/filters-form/filter-department-object.js.map +0 -1
  92. package/dist-client/filters-form/ox-filter-department-object.d.ts +0 -15
  93. package/dist-client/filters-form/ox-filter-department-object.js +0 -130
  94. package/dist-client/filters-form/ox-filter-department-object.js.map +0 -1
@@ -0,0 +1,212 @@
1
+ import '@material/web/icon/icon.js'
2
+ import './recipients-view'
3
+
4
+ import { css, html, LitElement } from 'lit'
5
+ import { customElement, property, query, state } from 'lit/decorators.js'
6
+
7
+ import { i18next, localize } from '@operato/i18n'
8
+ import { DataGrist, FetchOption, getEditor, getRenderer } from '@operato/data-grist'
9
+ import { ButtonContainerStyles, CommonHeaderStyles } from '@operato/styles'
10
+ import { isMobileDevice } from '@operato/utils'
11
+
12
+ import { AssigneeItem } from '../types/org-member'
13
+
14
+ /**
15
+ * Assignee 리스트를 편집한다.
16
+ */
17
+ @customElement('recipients-editor')
18
+ export class RecipientsEditor extends localize(i18next)(LitElement) {
19
+ static styles = [
20
+ CommonHeaderStyles,
21
+ ButtonContainerStyles,
22
+ css`
23
+ :host {
24
+ display: flex;
25
+ flex-direction: column;
26
+
27
+ background-color: var(--md-sys-color-surface);
28
+ }
29
+
30
+ recipients-view {
31
+ min-height: 100px;
32
+ }
33
+
34
+ ox-grist {
35
+ flex: 1;
36
+ }
37
+ `
38
+ ]
39
+
40
+ @property({ type: Array }) value?: AssigneeItem[]
41
+
42
+ @state() gristConfig?: any
43
+
44
+ @query('ox-grist') grist?: DataGrist
45
+
46
+ render() {
47
+ return html`
48
+ <recipients-view .value=${this.value}></recipients-view>
49
+
50
+ <ox-grist
51
+ .mode=${isMobileDevice() ? 'CARD' : 'GRID'}
52
+ .config=${this.gristConfig}
53
+ .fetchHandler=${this.fetchHandler.bind(this)}
54
+ @record-change=${e => {
55
+ this.value = ((this.grist as any)?._data.records || [])
56
+ .map(v => {
57
+ return { type: v.type, assignee: v.assignee }
58
+ })
59
+ .filter(v => v.type)
60
+
61
+ this.dispatchEvent(
62
+ new CustomEvent('change', {
63
+ bubbles: true,
64
+ composed: true,
65
+ detail: this.value
66
+ })
67
+ )
68
+ }}
69
+ >
70
+ <div slot="headroom" class="header">
71
+ <div id="actions">
72
+ <button danger @click=${() => this.deleteDataItems()}>
73
+ <md-icon>delete</md-icon>${i18next.t('button.delete')}
74
+ </button>
75
+ </div>
76
+ </div>
77
+ </ox-grist>
78
+ `
79
+ }
80
+
81
+ async firstUpdated() {
82
+ this.gristConfig = {
83
+ list: {
84
+ fields: ['type', 'assignee']
85
+ },
86
+ columns: [
87
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
88
+ { type: 'gutter', gutterName: 'sequence' },
89
+ {
90
+ type: 'gutter',
91
+ gutterName: 'button',
92
+ icon: 'arrow_upward',
93
+ handlers: {
94
+ click: 'move-up'
95
+ }
96
+ },
97
+ {
98
+ type: 'gutter',
99
+ gutterName: 'button',
100
+ icon: 'arrow_downward',
101
+ handlers: {
102
+ click: 'move-down'
103
+ }
104
+ },
105
+ {
106
+ type: 'select',
107
+ name: 'type',
108
+ header: i18next.t('field.type'),
109
+ record: {
110
+ editable: true,
111
+ options: ['', 'Employee', 'Department', 'Role', 'MyDepartment', 'MySupervisor', 'Myself']
112
+ },
113
+ width: 140
114
+ },
115
+ {
116
+ type: 'resource-object',
117
+ name: 'assignee',
118
+ header: i18next.t('field.assignee'),
119
+ record: {
120
+ editable: true,
121
+ editor: function (value, column, record, rowIndex, field) {
122
+ var options = {}
123
+ switch (record.type) {
124
+ case 'Employee':
125
+ options = {
126
+ title: i18next.t('title.employee list'),
127
+ queryName: 'employees',
128
+ columns: [
129
+ { name: 'id', hidden: true },
130
+ {
131
+ name: 'controlNo',
132
+ header: { renderer: () => i18next.t('field.control-no') },
133
+ filter: 'search'
134
+ },
135
+ { name: 'name', header: { renderer: () => i18next.t('field.name') }, filter: 'search' }
136
+ ],
137
+ list: { fields: ['name', 'control-no'] },
138
+ valueField: 'id',
139
+ nameField: 'name',
140
+ descriptionField: 'controlNo'
141
+ }
142
+ break
143
+ case 'Department':
144
+ options = {
145
+ title: i18next.t('title.department list'),
146
+ queryName: 'departments'
147
+ }
148
+ break
149
+ case 'Role':
150
+ options = {
151
+ title: i18next.t('title.lookup role'),
152
+ queryName: 'roles'
153
+ }
154
+ break
155
+ default:
156
+ options = {}
157
+ }
158
+
159
+ var dynamicRecord = { ...column.record, options }
160
+
161
+ return getEditor(column.type)(value, { ...column, record: dynamicRecord }, record, rowIndex, field)
162
+ },
163
+ renderer: function (value, column, record, rowIndex, field) {
164
+ var options = {}
165
+ switch (record.type) {
166
+ case 'Employee':
167
+ options = {
168
+ valueField: 'id',
169
+ nameField: 'name',
170
+ descriptionField: 'controlNo'
171
+ }
172
+ break
173
+ case 'Department':
174
+ case 'Role':
175
+ default:
176
+ break
177
+ }
178
+
179
+ var dynamicRecord = { ...column.record, options }
180
+
181
+ return getRenderer(column.type)(value, { ...column, record: dynamicRecord }, record, rowIndex, field)
182
+ }
183
+ },
184
+ width: 180
185
+ }
186
+ ],
187
+ rows: {
188
+ selectable: {
189
+ multiple: true
190
+ }
191
+ },
192
+ pagination: {
193
+ infinite: true
194
+ },
195
+ sorters: []
196
+ }
197
+ }
198
+
199
+ async fetchHandler({ filters, page, limit, sortings = [] }: FetchOption) {
200
+ const value = [...(this.value || [])]
201
+ this.value = value
202
+
203
+ return {
204
+ total: value.length,
205
+ records: value
206
+ }
207
+ }
208
+
209
+ async deleteDataItems() {
210
+ this.grist?.deleteSelectedRecords(false)
211
+ }
212
+ }
@@ -0,0 +1,55 @@
1
+ import { css, html, LitElement, TemplateResult } from 'lit'
2
+ import { customElement, property, query } from 'lit/decorators.js'
3
+
4
+ import { i18next, localize } from '@operato/i18n'
5
+ import { AssigneeItem } from '../types/org-member'
6
+
7
+ @customElement('recipients-view')
8
+ export class RecipientsView extends localize(i18next)(LitElement) {
9
+ static styles = [
10
+ css`
11
+ :host {
12
+ display: flex;
13
+ flex-direction: row;
14
+ flex-wrap: wrap;
15
+
16
+ gap: 10px;
17
+ padding: 10px;
18
+
19
+ align-items: center;
20
+
21
+ background-color: var(--md-sys-color-surface);
22
+
23
+ --md-icon-size: 3em;
24
+ color: var(--secondary-color, black);
25
+ }
26
+
27
+ div[assignee] {
28
+ padding: 5px;
29
+ border-radius: 5px;
30
+ border: 2px solid var(--primary-color, black);
31
+ }
32
+ `
33
+ ]
34
+
35
+ @property({ type: Object }) value?: AssigneeItem[]
36
+
37
+ render() {
38
+ const items = this.value || []
39
+
40
+ return html` ${items.map((item, order) => this.renderItem(item, order + 1))} `
41
+ }
42
+
43
+ renderItem(item: AssigneeItem, order: number): TemplateResult {
44
+ const { type, assignee } = item
45
+ const { name, description, controlNo } = assignee || {}
46
+ const subname = (description || controlNo) && `(${description || controlNo})`
47
+
48
+ return html`
49
+ <div assignee>
50
+ <div>${type}</div>
51
+ <div><span name>${name}</span>${subname}</div>
52
+ </div>
53
+ `
54
+ }
55
+ }
@@ -0,0 +1,70 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '../component/approval-line-items-editor-popup.js'
3
+
4
+ import { html, TemplateResult } from 'lit'
5
+ import { customElement } from 'lit/decorators.js'
6
+
7
+ import { OxGristEditor } from '@operato/data-grist'
8
+ import { i18next } from '@operato/i18n'
9
+ import { openPopup, PopupHandle } from '@operato/layout'
10
+
11
+ @customElement('grist-editor-approval-line')
12
+ export class GristEditorApprovalLine extends OxGristEditor {
13
+ private popup?: PopupHandle
14
+ private template?: TemplateResult
15
+
16
+ get editorTemplate() {
17
+ const value = this.value
18
+ return !value || !(value instanceof Array) || value.length == 0
19
+ ? html``
20
+ : value.length == 1
21
+ ? html`<md-icon style="--md-icon-size:1.3em">person</md-icon>`
22
+ : html`<md-icon style="--md-icon-size:1.3em">group</md-icon>`
23
+ }
24
+
25
+ _onclick(e: Event): void {
26
+ e.stopPropagation()
27
+ this.openSelector()
28
+ }
29
+
30
+ _onkeydown(e: KeyboardEvent): void {
31
+ const key = e.key
32
+ if (key == 'Enter') {
33
+ e.stopPropagation()
34
+ this.openSelector()
35
+ }
36
+ }
37
+
38
+ openSelector() {
39
+ if (this.popup) {
40
+ delete this.popup
41
+ }
42
+
43
+ const confirmCallback = (selected?: { [field: string]: any }) => {
44
+ this.dispatchEvent(
45
+ new CustomEvent('field-change', {
46
+ bubbles: true,
47
+ composed: true,
48
+ detail: {
49
+ before: this.value,
50
+ after: selected || [],
51
+ record: this.record,
52
+ column: this.column,
53
+ row: this.row
54
+ }
55
+ })
56
+ )
57
+ }
58
+
59
+ var value = this.value || []
60
+
61
+ var template =
62
+ this.template || html` <approval-line-items-editor-popup .value=${value} .confirmCallback=${confirmCallback.bind(this)}></approval-line-items-editor-popup> `
63
+
64
+ this.popup = openPopup(template, {
65
+ backdrop: true,
66
+ size: 'large',
67
+ title: i18next.t('title.approval-line editor')
68
+ })
69
+ }
70
+ }
@@ -0,0 +1,69 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '../component/assignees-editor-popup.js'
3
+
4
+ import { html, TemplateResult } from 'lit'
5
+ import { customElement } from 'lit/decorators.js'
6
+
7
+ import { OxGristEditor } from '@operato/data-grist'
8
+ import { i18next } from '@operato/i18n'
9
+ import { openPopup, PopupHandle } from '@operato/layout'
10
+
11
+ @customElement('grist-editor-assignees')
12
+ export class GristEditorAssignees extends OxGristEditor {
13
+ private popup?: PopupHandle
14
+ private template?: TemplateResult
15
+
16
+ get editorTemplate() {
17
+ const value = this.value
18
+ return !value || !(value instanceof Array) || value.length == 0
19
+ ? html``
20
+ : value.length == 1
21
+ ? html`<md-icon style="--md-icon-size:1.3em">person</md-icon>`
22
+ : html`<md-icon style="--md-icon-size:1.3em">group</md-icon>`
23
+ }
24
+
25
+ _onclick(e: Event): void {
26
+ e.stopPropagation()
27
+ this.openSelector()
28
+ }
29
+
30
+ _onkeydown(e: KeyboardEvent): void {
31
+ const key = e.key
32
+ if (key == 'Enter') {
33
+ e.stopPropagation()
34
+ this.openSelector()
35
+ }
36
+ }
37
+
38
+ openSelector() {
39
+ if (this.popup) {
40
+ delete this.popup
41
+ }
42
+
43
+ const confirmCallback = (selected?: { [field: string]: any }) => {
44
+ this.dispatchEvent(
45
+ new CustomEvent('field-change', {
46
+ bubbles: true,
47
+ composed: true,
48
+ detail: {
49
+ before: this.value,
50
+ after: selected || [],
51
+ record: this.record,
52
+ column: this.column,
53
+ row: this.row
54
+ }
55
+ })
56
+ )
57
+ }
58
+
59
+ var value = this.value || []
60
+
61
+ var template = this.template || html` <assignees-editor-popup .value=${value} .confirmCallback=${confirmCallback.bind(this)}></assignees-editor-popup> `
62
+
63
+ this.popup = openPopup(template, {
64
+ backdrop: true,
65
+ size: 'large',
66
+ title: i18next.t('title.assignees editor')
67
+ })
68
+ }
69
+ }
@@ -0,0 +1,78 @@
1
+ import '../component/department-selector.js'
2
+
3
+ import { html, TemplateResult } from 'lit'
4
+ import { customElement } from 'lit/decorators.js'
5
+
6
+ import { OxGristEditor } from '@operato/data-grist'
7
+ import { i18next } from '@operato/i18n'
8
+ import { openPopup, PopupHandle } from '@operato/layout'
9
+
10
+ @customElement('grist-editor-department-object')
11
+ export class GristEditorDepartmentObject extends OxGristEditor {
12
+ private popup?: PopupHandle
13
+ private template?: TemplateResult
14
+
15
+ get editorTemplate() {
16
+ var { name, description } = this.value || {}
17
+
18
+ return html`
19
+ ${!this.value
20
+ ? html``
21
+ : html` <span tabindex="0" style="flex:1">${name || ''}${(description && `(${description})`) || ''}</span> `}
22
+ `
23
+ }
24
+
25
+ _onclick(e: Event): void {
26
+ e.stopPropagation()
27
+ this.openSelector()
28
+ }
29
+
30
+ _onkeydown(e: KeyboardEvent): void {
31
+ const key = e.key
32
+ if (key == 'Enter') {
33
+ e.stopPropagation()
34
+ this.openSelector()
35
+ }
36
+ }
37
+
38
+ openSelector() {
39
+ if (this.popup) {
40
+ delete this.popup
41
+ }
42
+
43
+ const confirmCallback = (selected?: { [field: string]: any }) => {
44
+ this.dispatchEvent(
45
+ new CustomEvent('field-change', {
46
+ bubbles: true,
47
+ composed: true,
48
+ detail: {
49
+ before: this.value,
50
+ after: selected
51
+ ? {
52
+ id: selected.id,
53
+ controlNo: selected.controlNo,
54
+ name: selected.name,
55
+ description: selected.description
56
+ }
57
+ : null,
58
+ record: this.record,
59
+ column: this.column,
60
+ row: this.row
61
+ }
62
+ })
63
+ )
64
+ }
65
+
66
+ var value = this.value || {}
67
+
68
+ var template =
69
+ this.template ||
70
+ html` <department-selector .value=${value} .confirmCallback=${confirmCallback.bind(this)}></department-selector> `
71
+
72
+ this.popup = openPopup(template, {
73
+ backdrop: true,
74
+ size: 'large',
75
+ title: i18next.t('title.select_item')
76
+ })
77
+ }
78
+ }
@@ -0,0 +1,69 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '../component/recipients-editor-popup.js'
3
+
4
+ import { html, TemplateResult } from 'lit'
5
+ import { customElement } from 'lit/decorators.js'
6
+
7
+ import { OxGristEditor } from '@operato/data-grist'
8
+ import { i18next } from '@operato/i18n'
9
+ import { openPopup, PopupHandle } from '@operato/layout'
10
+
11
+ @customElement('grist-editor-recipients')
12
+ export class GristEditorRecipients extends OxGristEditor {
13
+ private popup?: PopupHandle
14
+ private template?: TemplateResult
15
+
16
+ get editorTemplate() {
17
+ const value = this.value
18
+ return !value || !(value instanceof Array) || value.length == 0
19
+ ? html``
20
+ : value.length == 1
21
+ ? html`<md-icon style="--md-icon-size:1.3em">person</md-icon>`
22
+ : html`<md-icon style="--md-icon-size:1.3em">group</md-icon>`
23
+ }
24
+
25
+ _onclick(e: Event): void {
26
+ e.stopPropagation()
27
+ this.openSelector()
28
+ }
29
+
30
+ _onkeydown(e: KeyboardEvent): void {
31
+ const key = e.key
32
+ if (key == 'Enter') {
33
+ e.stopPropagation()
34
+ this.openSelector()
35
+ }
36
+ }
37
+
38
+ openSelector() {
39
+ if (this.popup) {
40
+ delete this.popup
41
+ }
42
+
43
+ const confirmCallback = (selected?: { [field: string]: any }) => {
44
+ this.dispatchEvent(
45
+ new CustomEvent('field-change', {
46
+ bubbles: true,
47
+ composed: true,
48
+ detail: {
49
+ before: this.value,
50
+ after: selected || [],
51
+ record: this.record,
52
+ column: this.column,
53
+ row: this.row
54
+ }
55
+ })
56
+ )
57
+ }
58
+
59
+ var value = this.value || []
60
+
61
+ var template = this.template || html` <recipients-editor-popup .value=${value} .confirmCallback=${confirmCallback.bind(this)}></recipients-editor-popup> `
62
+
63
+ this.popup = openPopup(template, {
64
+ backdrop: true,
65
+ size: 'large',
66
+ title: i18next.t('title.recipients editor')
67
+ })
68
+ }
69
+ }
@@ -0,0 +1,13 @@
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import { html } from 'lit-html'
4
+
5
+ import { FieldRenderer } from '@operato/data-grist'
6
+
7
+ export const GristRendererApprovalLine: FieldRenderer = (value, column, record, rowIndex, field) => {
8
+ return !value || !(value instanceof Array) || value.length == 0
9
+ ? html``
10
+ : value.length == 1
11
+ ? html`<md-icon style="--md-icon-size:1.3em">person</md-icon>`
12
+ : html`<md-icon style="--md-icon-size:1.3em">group</md-icon>`
13
+ }
@@ -0,0 +1,13 @@
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import { html } from 'lit-html'
4
+
5
+ import { FieldRenderer } from '@operato/data-grist'
6
+
7
+ export const GristRendererAssignees: FieldRenderer = (value, column, record, rowIndex, field) => {
8
+ return !value || !(value instanceof Array) || value.length == 0
9
+ ? html``
10
+ : value.length == 1
11
+ ? html`<md-icon style="--md-icon-size:1.3em">person</md-icon>`
12
+ : html`<md-icon style="--md-icon-size:1.3em">group</md-icon>`
13
+ }
@@ -0,0 +1,13 @@
1
+ import { html } from 'lit-html'
2
+
3
+ import { FieldRenderer } from '@operato/data-grist'
4
+
5
+ export const GristRendererDepartmentObject: FieldRenderer = (value, column, record, rowIndex, field) => {
6
+ if (!value) {
7
+ return ''
8
+ }
9
+
10
+ const { name, description } = value
11
+
12
+ return html`<span>${name || ''}${(description && `(${description})`) || ''}</span> `
13
+ }
@@ -0,0 +1,13 @@
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import { html } from 'lit-html'
4
+
5
+ import { FieldRenderer } from '@operato/data-grist'
6
+
7
+ export const GristRendererRecipients: FieldRenderer = (value, column, record, rowIndex, field) => {
8
+ return !value || !(value instanceof Array) || value.length == 0
9
+ ? html``
10
+ : value.length == 1
11
+ ? html`<md-icon style="--md-icon-size:1.3em">person</md-icon>`
12
+ : html`<md-icon style="--md-icon-size:1.3em">group</md-icon>`
13
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types'
2
+ export * from './component'