@lblod/ember-rdfa-editor-lblod-plugins 38.3.0 → 39.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.
- package/CHANGELOG.md +24 -0
- package/addon/components/lmb-plugin/{insert.ts → insert.gts} +28 -1
- package/addon/components/lmb-plugin/list.gts +85 -0
- package/addon/components/lmb-plugin/search-modal.gts +347 -0
- package/addon/components/variable-plugin/date/floating-date-container.gts +1 -1
- package/addon/plugins/lmb-plugin/index.ts +2 -1
- package/addon/plugins/lmb-plugin/utils/fetchElectees.ts +132 -20
- package/addon/plugins/variable-plugin/contextual-actions/person.ts +1 -1
- package/declarations/addon/components/lmb-plugin/insert.d.ts +1 -1
- package/declarations/addon/components/lmb-plugin/list.d.ts +17 -1
- package/declarations/addon/components/lmb-plugin/search-modal.d.ts +18 -11
- package/declarations/addon/plugins/lmb-plugin/index.d.ts +2 -1
- package/declarations/addon/plugins/lmb-plugin/utils/fetchElectees.d.ts +19 -3
- package/package.json +1 -1
- package/translations/en-US.yaml +1 -0
- package/translations/nl-BE.yaml +1 -0
- package/addon/components/lmb-plugin/insert.hbs +0 -18
- package/addon/components/lmb-plugin/list.hbs +0 -60
- package/addon/components/lmb-plugin/search-modal.hbs +0 -100
- package/addon/components/lmb-plugin/search-modal.ts +0 -189
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @lblod/ember-rdfa-editor-lblod-plugins
|
|
2
2
|
|
|
3
|
+
## 39.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#682](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/682) [`370378d`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/370378d9718bf1d757e5026134eecce579280503) Thanks [@elpoelma](https://github.com/elpoelma)! - LMB-plugin: breaking change to the `LMBPluginConfig` interface:
|
|
8
|
+
the `defaultAdminUnit` now expects an object of the shape
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
{
|
|
12
|
+
uri: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The administrative unit input field of the `lmb-plugin` `SearchModal` component is now implement through a dynamic `PowerSelect` component rather than a free-text input field.
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- [#682](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/682) [`370378d`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/370378d9718bf1d757e5026134eecce579280503) Thanks [@elpoelma](https://github.com/elpoelma)! - LMB-plugin: improve SPARQL query performance
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- [#679](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/679) [`e08902b`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/e08902b088d4e649abe0f4396341e581d8949fa2) Thanks [@lagartoverde](https://github.com/lagartoverde)! - Fix floating window position on date variable
|
|
26
|
+
|
|
3
27
|
## 38.3.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
|
@@ -15,6 +15,10 @@ import { service } from '@ember/service';
|
|
|
15
15
|
import IntlService from 'ember-intl/services/intl';
|
|
16
16
|
import { Person } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/variable-plugin/variables';
|
|
17
17
|
import { createPersonVariable } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/variable-plugin/actions/create-person-variable';
|
|
18
|
+
import AuButton from '@appuniversum/ember-appuniversum/components/au-button';
|
|
19
|
+
import SearchModal from './search-modal';
|
|
20
|
+
import { on } from '@ember/modifier';
|
|
21
|
+
import t from 'ember-intl/helpers/t';
|
|
18
22
|
|
|
19
23
|
interface Args {
|
|
20
24
|
controller: SayController;
|
|
@@ -27,7 +31,10 @@ export default class LmbPluginInsertComponent extends Component<Args> {
|
|
|
27
31
|
AddIcon = AddIcon;
|
|
28
32
|
|
|
29
33
|
get showModal() {
|
|
30
|
-
return
|
|
34
|
+
return (
|
|
35
|
+
getLmbModalsPluginState(this.controller.mainEditorState)?.modalOpen ??
|
|
36
|
+
false
|
|
37
|
+
);
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
get controller() {
|
|
@@ -66,4 +73,24 @@ export default class LmbPluginInsertComponent extends Component<Args> {
|
|
|
66
73
|
view: this.controller.mainEditorView,
|
|
67
74
|
});
|
|
68
75
|
}
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<li class='au-c-list__item'>
|
|
79
|
+
<AuButton
|
|
80
|
+
@icon={{this.AddIcon}}
|
|
81
|
+
@iconAlignment='left'
|
|
82
|
+
@skin='link'
|
|
83
|
+
{{on 'click' this.openModal}}
|
|
84
|
+
>
|
|
85
|
+
{{t 'lmb-plugin.insert.title'}}
|
|
86
|
+
</AuButton>
|
|
87
|
+
</li>
|
|
88
|
+
|
|
89
|
+
<SearchModal
|
|
90
|
+
@open={{this.showModal}}
|
|
91
|
+
@closeModal={{this.closeModal}}
|
|
92
|
+
@config={{@config}}
|
|
93
|
+
@onInsert={{this.onInsert}}
|
|
94
|
+
/>
|
|
95
|
+
</template>
|
|
69
96
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { TOC } from '@ember/component/template-only';
|
|
2
|
+
import AuTable from '@appuniversum/ember-appuniversum/components/au-table';
|
|
3
|
+
import SortableTableHeader from '@lblod/ember-rdfa-editor-lblod-plugins/components/common/sort/sortable-table-header';
|
|
4
|
+
import { SearchSort } from './search-modal';
|
|
5
|
+
import t from 'ember-intl/helpers/t';
|
|
6
|
+
import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
|
|
7
|
+
import AuButton from '@appuniversum/ember-appuniversum/components/au-button';
|
|
8
|
+
import { fn } from '@ember/helper';
|
|
9
|
+
import { on } from '@ember/modifier';
|
|
10
|
+
import AuLoader from '@appuniversum/ember-appuniversum/components/au-loader';
|
|
11
|
+
import { TaskInstance } from 'reactiveweb/ember-concurrency';
|
|
12
|
+
|
|
13
|
+
interface Signature {
|
|
14
|
+
Args: {
|
|
15
|
+
sort: SearchSort;
|
|
16
|
+
setSort: (sort: SearchSort) => void;
|
|
17
|
+
services: TaskInstance<{ results: Electee[]; totalCount: number }>;
|
|
18
|
+
insert: (electee: Electee) => unknown;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const List: TOC<Signature> = <template>
|
|
23
|
+
<AuTable>
|
|
24
|
+
<:header>
|
|
25
|
+
<tr class='au-c-data-table__header-title'>
|
|
26
|
+
<th>
|
|
27
|
+
<SortableTableHeader
|
|
28
|
+
@field='fullName'
|
|
29
|
+
@label={{t 'lmb-plugin.modal.fields.name'}}
|
|
30
|
+
@sort={{@sort}}
|
|
31
|
+
@setSort={{@setSort}}
|
|
32
|
+
/>
|
|
33
|
+
</th>
|
|
34
|
+
<th>
|
|
35
|
+
<SortableTableHeader
|
|
36
|
+
@field='kandidatenlijst'
|
|
37
|
+
@label={{t 'lmb-plugin.modal.fields.kandidatenlijst'}}
|
|
38
|
+
@sort={{@sort}}
|
|
39
|
+
@setSort={{@setSort}}
|
|
40
|
+
/>
|
|
41
|
+
</th>
|
|
42
|
+
<th />
|
|
43
|
+
</tr>
|
|
44
|
+
</:header>
|
|
45
|
+
<:body>
|
|
46
|
+
{{#if @services.isRunning}}
|
|
47
|
+
<tr>
|
|
48
|
+
<td colspan='100%'>
|
|
49
|
+
<AuLoader @centered={{true}}>
|
|
50
|
+
{{t 'common.search.loading'}}
|
|
51
|
+
</AuLoader>
|
|
52
|
+
</td>
|
|
53
|
+
</tr>
|
|
54
|
+
{{else}}
|
|
55
|
+
{{#if @services.value.totalCount}}
|
|
56
|
+
{{#each @services.value.results as |row|}}
|
|
57
|
+
<tr>
|
|
58
|
+
<td>{{row.fullName}}</td>
|
|
59
|
+
<td>
|
|
60
|
+
{{#if row.kandidatenlijst}}
|
|
61
|
+
{{row.kandidatenlijst}}
|
|
62
|
+
{{else}}
|
|
63
|
+
<span class='au-u-italic'>{{t 'common.not-applicable'}}</span>
|
|
64
|
+
{{/if}}
|
|
65
|
+
</td>
|
|
66
|
+
<td class='au-u-text-center'>
|
|
67
|
+
<AuButton {{on 'click' (fn @insert row)}}>
|
|
68
|
+
{{t 'lmb-plugin.modal.insert'}}
|
|
69
|
+
</AuButton>
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
72
|
+
{{/each}}
|
|
73
|
+
{{else}}
|
|
74
|
+
<tr>
|
|
75
|
+
<td colspan='100%'>
|
|
76
|
+
{{t 'common.search.no-results'}}
|
|
77
|
+
</td>
|
|
78
|
+
</tr>
|
|
79
|
+
{{/if}}
|
|
80
|
+
{{/if}}
|
|
81
|
+
</:body>
|
|
82
|
+
</AuTable>
|
|
83
|
+
</template>;
|
|
84
|
+
|
|
85
|
+
export default List;
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { assert } from '@ember/debug';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
import { restartableTask, timeout } from 'ember-concurrency';
|
|
6
|
+
import { task as trackedTask } from 'reactiveweb/ember-concurrency';
|
|
7
|
+
import { LmbPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin';
|
|
8
|
+
|
|
9
|
+
import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
|
|
10
|
+
import {
|
|
11
|
+
FetchMandateesArgs,
|
|
12
|
+
fetchAdministrativeUnits,
|
|
13
|
+
fetchElectees,
|
|
14
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin/utils/fetchElectees';
|
|
15
|
+
import {
|
|
16
|
+
BESTUURSPERIODES,
|
|
17
|
+
BestuursperiodeLabel,
|
|
18
|
+
BestuursperiodeURI,
|
|
19
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
|
|
20
|
+
import { isSome } from '@lblod/ember-rdfa-editor/utils/_private/option';
|
|
21
|
+
import type { AdministrativeUnit } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/worship-plugin';
|
|
22
|
+
import { localCopy } from 'tracked-toolbox';
|
|
23
|
+
import AuMainContainer from '@appuniversum/ember-appuniversum/components/au-main-container';
|
|
24
|
+
import AuModal from '@appuniversum/ember-appuniversum/components/au-modal';
|
|
25
|
+
import AuHeading from '@appuniversum/ember-appuniversum/components/au-heading';
|
|
26
|
+
import AuLabel from '@appuniversum/ember-appuniversum/components/au-label';
|
|
27
|
+
import AuNativeInput from '../au-native-input';
|
|
28
|
+
import PowerSelect from 'ember-power-select/components/power-select';
|
|
29
|
+
import perform from 'ember-concurrency/helpers/perform';
|
|
30
|
+
import List from './list';
|
|
31
|
+
import pagination from '@lblod/ember-rdfa-editor-lblod-plugins/helpers/pagination';
|
|
32
|
+
import PaginationView from '../pagination/pagination-view';
|
|
33
|
+
import { not } from 'ember-truth-helpers';
|
|
34
|
+
import t from 'ember-intl/helpers/t';
|
|
35
|
+
import AlertLoadError from '../common/search/alert-load-error';
|
|
36
|
+
import { on } from '@ember/modifier';
|
|
37
|
+
export type SearchSort = [keyof Electee, 'ASC' | 'DESC'] | false;
|
|
38
|
+
|
|
39
|
+
interface Signature {
|
|
40
|
+
Args: {
|
|
41
|
+
config: LmbPluginConfig;
|
|
42
|
+
open: boolean;
|
|
43
|
+
closeModal: () => void;
|
|
44
|
+
onInsert: (electee: Electee) => void;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface AdminPeriodOption {
|
|
49
|
+
label: BestuursperiodeLabel;
|
|
50
|
+
uri: BestuursperiodeURI;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default class SearchModal extends Component<Signature> {
|
|
54
|
+
// Display
|
|
55
|
+
@tracked error: unknown;
|
|
56
|
+
@tracked inputSearchText: string | null = null;
|
|
57
|
+
@tracked sort: SearchSort = false;
|
|
58
|
+
|
|
59
|
+
// Pagination
|
|
60
|
+
@tracked pageNumber = 0;
|
|
61
|
+
@tracked pageSize = 20;
|
|
62
|
+
@tracked totalCount = 0;
|
|
63
|
+
|
|
64
|
+
// Admin periods
|
|
65
|
+
@tracked selectedAdminPeriod: AdminPeriodOption;
|
|
66
|
+
adminPeriods: AdminPeriodOption[];
|
|
67
|
+
// Admin units
|
|
68
|
+
@localCopy('args.config.defaultAdminUnit')
|
|
69
|
+
selectedAdministrativeUnit?: AdministrativeUnit;
|
|
70
|
+
// tracks whether the user has just typed a character
|
|
71
|
+
// doesn't need to be reactive
|
|
72
|
+
typing = false;
|
|
73
|
+
constructor(owner: unknown, args: Signature['Args']) {
|
|
74
|
+
super(owner, args);
|
|
75
|
+
this.adminPeriods = Object.entries(BESTUURSPERIODES).map(
|
|
76
|
+
([key, value]: [BestuursperiodeLabel, BestuursperiodeURI]) => ({
|
|
77
|
+
label: key,
|
|
78
|
+
uri: value,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
this.selectedAdminPeriod =
|
|
82
|
+
this.adminPeriods.find(
|
|
83
|
+
(entry) =>
|
|
84
|
+
isSome(args.config.defaultPeriod) &&
|
|
85
|
+
entry.label === args.config.defaultPeriod,
|
|
86
|
+
) ?? this.adminPeriods[this.adminPeriods.length - 1];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get config() {
|
|
90
|
+
return this.args.config;
|
|
91
|
+
}
|
|
92
|
+
selectAdminPeriod = (value: AdminPeriodOption) => {
|
|
93
|
+
this.selectedAdminPeriod = value;
|
|
94
|
+
this.pageNumber = 0;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
@action
|
|
98
|
+
async closeModal() {
|
|
99
|
+
this.typing = false;
|
|
100
|
+
this.inputSearchText = null;
|
|
101
|
+
this.sort = false;
|
|
102
|
+
await this.servicesResource.cancel();
|
|
103
|
+
this.args.closeModal();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
search = restartableTask(
|
|
107
|
+
async ({
|
|
108
|
+
endpoint,
|
|
109
|
+
searchString,
|
|
110
|
+
page,
|
|
111
|
+
pageSize,
|
|
112
|
+
sort,
|
|
113
|
+
period,
|
|
114
|
+
administrativeUnit,
|
|
115
|
+
}: FetchMandateesArgs) => {
|
|
116
|
+
// debounce, but only when the input fields are being used
|
|
117
|
+
if (this.typing) {
|
|
118
|
+
this.typing = false;
|
|
119
|
+
await timeout(250);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!this.args.open) {
|
|
123
|
+
return {
|
|
124
|
+
results: [],
|
|
125
|
+
totalCount: 0,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const abortController = new AbortController();
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const result = await fetchElectees({
|
|
133
|
+
endpoint,
|
|
134
|
+
searchString,
|
|
135
|
+
page,
|
|
136
|
+
pageSize,
|
|
137
|
+
sort,
|
|
138
|
+
period,
|
|
139
|
+
administrativeUnit,
|
|
140
|
+
abortSignal: abortController.signal,
|
|
141
|
+
});
|
|
142
|
+
const { count, electees } = result;
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
results: electees,
|
|
146
|
+
totalCount: count,
|
|
147
|
+
};
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error('Got an error fetching electees', err);
|
|
150
|
+
this.error = err;
|
|
151
|
+
} finally {
|
|
152
|
+
abortController.abort();
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
results: [],
|
|
156
|
+
totalCount: 0,
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
servicesResource = trackedTask<{ results: Electee[]; totalCount: number }>(
|
|
162
|
+
this,
|
|
163
|
+
this.search,
|
|
164
|
+
() => [
|
|
165
|
+
{
|
|
166
|
+
endpoint: this.args.config.endpoint,
|
|
167
|
+
searchString: this.inputSearchText ?? '',
|
|
168
|
+
sort: this.sort,
|
|
169
|
+
page: this.pageNumber,
|
|
170
|
+
pageSize: this.pageSize,
|
|
171
|
+
open: this.args.open,
|
|
172
|
+
period: this.selectedAdminPeriod.uri,
|
|
173
|
+
administrativeUnit: this.selectedAdministrativeUnit,
|
|
174
|
+
} satisfies Partial<FetchMandateesArgs> & { open: boolean },
|
|
175
|
+
],
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
searchAdministrativeUnits = restartableTask(async (search: string) => {
|
|
179
|
+
await timeout(200);
|
|
180
|
+
|
|
181
|
+
const abortController = new AbortController();
|
|
182
|
+
try {
|
|
183
|
+
const units = await fetchAdministrativeUnits({
|
|
184
|
+
endpoint: this.config.endpoint,
|
|
185
|
+
searchString: search,
|
|
186
|
+
lmbPeriod: this.selectedAdminPeriod.uri,
|
|
187
|
+
abortSignal: abortController.signal,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return units;
|
|
191
|
+
} catch (err) {
|
|
192
|
+
// ember-power-select doesn't seem to have a way to display errors.
|
|
193
|
+
console.error(
|
|
194
|
+
'Error occurred when searching for administrative units',
|
|
195
|
+
err,
|
|
196
|
+
);
|
|
197
|
+
// We just re-throw to keep TS happy, ember-concurrency just swallows it.
|
|
198
|
+
throw err;
|
|
199
|
+
} finally {
|
|
200
|
+
abortController.abort();
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
selectAdministrativeUnit = (administrativeUnit: AdministrativeUnit) => {
|
|
205
|
+
this.selectedAdministrativeUnit = administrativeUnit;
|
|
206
|
+
this.pageNumber = 0;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
@action
|
|
210
|
+
setSort(sort: SearchSort) {
|
|
211
|
+
this.sort = sort;
|
|
212
|
+
}
|
|
213
|
+
@action
|
|
214
|
+
setInputSearchText(event: InputEvent) {
|
|
215
|
+
assert(
|
|
216
|
+
'inputSearchText must be bound to an input element',
|
|
217
|
+
event.target instanceof HTMLInputElement,
|
|
218
|
+
);
|
|
219
|
+
this.typing = true;
|
|
220
|
+
|
|
221
|
+
this.inputSearchText = event.target.value;
|
|
222
|
+
this.pageNumber = 0;
|
|
223
|
+
}
|
|
224
|
+
@action
|
|
225
|
+
previousPage() {
|
|
226
|
+
--this.pageNumber;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@action
|
|
230
|
+
nextPage() {
|
|
231
|
+
++this.pageNumber;
|
|
232
|
+
}
|
|
233
|
+
@action
|
|
234
|
+
async onInsert(electee: Electee) {
|
|
235
|
+
this.args.onInsert(electee);
|
|
236
|
+
await this.closeModal();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
<template>
|
|
240
|
+
<AuModal
|
|
241
|
+
class='worship-modal'
|
|
242
|
+
@modalOpen={{@open}}
|
|
243
|
+
@closeModal={{this.closeModal}}
|
|
244
|
+
@title={{t 'lmb-plugin.modal.title'}}
|
|
245
|
+
@size='large'
|
|
246
|
+
@padding='none'
|
|
247
|
+
as |modal|
|
|
248
|
+
>
|
|
249
|
+
<modal.Body>
|
|
250
|
+
<AuMainContainer class='worship-modal--main-container' as |mc|>
|
|
251
|
+
<mc.sidebar>
|
|
252
|
+
<div class='au-c-sidebar'>
|
|
253
|
+
<div class='au-c-sidebar__content au-u-padding'>
|
|
254
|
+
<AuHeading
|
|
255
|
+
@level='3'
|
|
256
|
+
@skin='4'
|
|
257
|
+
class='au-u-padding-bottom-small'
|
|
258
|
+
>
|
|
259
|
+
{{t 'lmb-plugin.modal.search.title'}}
|
|
260
|
+
</AuHeading>
|
|
261
|
+
<AuLabel class='au-margin-bottom-small' for='searchTerm'>
|
|
262
|
+
{{t 'lmb-plugin.modal.fields.name'}}
|
|
263
|
+
</AuLabel>
|
|
264
|
+
<AuNativeInput
|
|
265
|
+
@type='text'
|
|
266
|
+
@width='block'
|
|
267
|
+
id='searchTerm'
|
|
268
|
+
value={{this.inputSearchText}}
|
|
269
|
+
placeholder={{t 'lmb-plugin.modal.nameSearch.placeholder'}}
|
|
270
|
+
{{on 'input' this.setInputSearchText}}
|
|
271
|
+
/>
|
|
272
|
+
<AuLabel class='au-margin-bottom-small' for='periodSelect'>
|
|
273
|
+
{{t 'lmb-plugin.modal.fields.period'}}
|
|
274
|
+
</AuLabel>
|
|
275
|
+
<PowerSelect
|
|
276
|
+
@allowClear={{false}}
|
|
277
|
+
@searchEnabled={{false}}
|
|
278
|
+
@options={{this.adminPeriods}}
|
|
279
|
+
@selected={{this.selectedAdminPeriod}}
|
|
280
|
+
@onChange={{this.selectAdminPeriod}}
|
|
281
|
+
as |period|
|
|
282
|
+
>
|
|
283
|
+
{{period.label}}
|
|
284
|
+
</PowerSelect>
|
|
285
|
+
|
|
286
|
+
<AuLabel class='au-margin-bottom-small' for='searchAdminUnit'>
|
|
287
|
+
{{t 'lmb-plugin.modal.fields.adminUnit'}}
|
|
288
|
+
</AuLabel>
|
|
289
|
+
<PowerSelect
|
|
290
|
+
@allowClear={{true}}
|
|
291
|
+
@searchEnabled={{true}}
|
|
292
|
+
@search={{perform this.searchAdministrativeUnits}}
|
|
293
|
+
@selected={{this.selectedAdministrativeUnit}}
|
|
294
|
+
@onChange={{this.selectAdministrativeUnit}}
|
|
295
|
+
@loadingMessage={{t 'common.search.loading'}}
|
|
296
|
+
@placeholder={{t
|
|
297
|
+
'lmb-plugin.modal.adminUnitSearch.placeholder'
|
|
298
|
+
}}
|
|
299
|
+
@searchMessage={{t 'common.search.type-to-search'}}
|
|
300
|
+
as |unit|
|
|
301
|
+
>
|
|
302
|
+
{{unit.label}}
|
|
303
|
+
</PowerSelect>
|
|
304
|
+
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
</mc.sidebar>
|
|
308
|
+
<mc.content @scroll={{true}}>
|
|
309
|
+
<div class='worship-modal--list-container'>
|
|
310
|
+
{{#if this.error}}
|
|
311
|
+
<AlertLoadError @error={{this.error}} />
|
|
312
|
+
{{else}}
|
|
313
|
+
<List
|
|
314
|
+
@services={{this.servicesResource}}
|
|
315
|
+
@sort={{this.sort}}
|
|
316
|
+
@setSort={{this.setSort}}
|
|
317
|
+
@insert={{this.onInsert}}
|
|
318
|
+
/>
|
|
319
|
+
{{/if}}
|
|
320
|
+
</div>
|
|
321
|
+
{{#if this.servicesResource.value.totalCount}}
|
|
322
|
+
{{#let
|
|
323
|
+
(pagination
|
|
324
|
+
page=this.pageNumber
|
|
325
|
+
pageSize=this.pageSize
|
|
326
|
+
count=this.servicesResource.value.totalCount
|
|
327
|
+
)
|
|
328
|
+
as |pg|
|
|
329
|
+
}}
|
|
330
|
+
<PaginationView
|
|
331
|
+
@totalCount={{pg.count}}
|
|
332
|
+
@rangeStart={{pg.pageStart}}
|
|
333
|
+
@rangeEnd={{pg.pageEnd}}
|
|
334
|
+
@onNextPage={{this.nextPage}}
|
|
335
|
+
@onPreviousPage={{this.previousPage}}
|
|
336
|
+
@isFirstPage={{not pg.hasPreviousPage}}
|
|
337
|
+
@isLastPage={{not pg.hasNextPage}}
|
|
338
|
+
/>
|
|
339
|
+
{{/let}}
|
|
340
|
+
{{/if}}
|
|
341
|
+
|
|
342
|
+
</mc.content>
|
|
343
|
+
</AuMainContainer>
|
|
344
|
+
</modal.Body>
|
|
345
|
+
</AuModal>
|
|
346
|
+
</template>
|
|
347
|
+
}
|
|
@@ -8,11 +8,12 @@ import {
|
|
|
8
8
|
EditorView,
|
|
9
9
|
Command,
|
|
10
10
|
} from '@lblod/ember-rdfa-editor';
|
|
11
|
+
import type { AdministrativeUnit } from '../worship-plugin';
|
|
11
12
|
|
|
12
13
|
export type LmbPluginConfig = {
|
|
13
14
|
endpoint: string;
|
|
14
15
|
defaultPeriod?: BestuursperiodeLabel;
|
|
15
|
-
defaultAdminUnit?:
|
|
16
|
+
defaultAdminUnit?: AdministrativeUnit;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
export function createPersonNode(controller: SayController, person: Person) {
|
|
@@ -4,26 +4,30 @@ import { BESTUURSPERIODES } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/c
|
|
|
4
4
|
import {
|
|
5
5
|
executeQuery,
|
|
6
6
|
sparqlEscapeString,
|
|
7
|
+
sparqlEscapeUri,
|
|
7
8
|
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/sparql-helpers';
|
|
9
|
+
import { AdministrativeUnit } from '../../worship-plugin';
|
|
8
10
|
|
|
9
11
|
export type FetchMandateesArgs = {
|
|
10
12
|
endpoint: string;
|
|
11
13
|
searchString: string;
|
|
12
|
-
|
|
14
|
+
administrativeUnit?: AdministrativeUnit;
|
|
13
15
|
page: number;
|
|
14
16
|
pageSize: number;
|
|
15
17
|
sort: SearchSort;
|
|
16
18
|
period: (typeof BESTUURSPERIODES)[keyof typeof BESTUURSPERIODES];
|
|
19
|
+
abortSignal?: AbortSignal;
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
export async function countElectees({
|
|
20
23
|
endpoint,
|
|
21
24
|
searchString,
|
|
22
|
-
|
|
25
|
+
administrativeUnit,
|
|
23
26
|
period,
|
|
27
|
+
abortSignal,
|
|
24
28
|
}: Pick<
|
|
25
29
|
FetchMandateesArgs,
|
|
26
|
-
'searchString' | 'endpoint' | 'period' | '
|
|
30
|
+
'searchString' | 'endpoint' | 'period' | 'administrativeUnit' | 'abortSignal'
|
|
27
31
|
>) {
|
|
28
32
|
const query = /* sparql */ `
|
|
29
33
|
PREFIX besluit: <http://data.vlaanderen.be/ns/besluit#>
|
|
@@ -36,15 +40,22 @@ export async function countElectees({
|
|
|
36
40
|
PREFIX org: <http://www.w3.org/ns/org#>
|
|
37
41
|
|
|
38
42
|
SELECT (COUNT(DISTINCT ?person) as ?count) WHERE {
|
|
39
|
-
?person
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
?person
|
|
44
|
+
a person:Person;
|
|
45
|
+
foaf:familyName ?lastName;
|
|
46
|
+
persoon:gebruikteVoornaam ?firstName.
|
|
47
|
+
|
|
48
|
+
?bestuursorgaanIT lmb:heeftBestuursperiode ${sparqlEscapeUri(period)}.
|
|
49
|
+
${
|
|
50
|
+
administrativeUnit
|
|
51
|
+
? `?bestuursorgaanIT mandaat:isTijdspecialisatieVan/besluit:bestuurt ${sparqlEscapeUri(administrativeUnit.uri)}.`
|
|
52
|
+
: ''
|
|
53
|
+
}
|
|
42
54
|
|
|
43
|
-
?bestuursorgaanIT lmb:heeftBestuursperiode <${period}>;
|
|
44
|
-
mandaat:isTijdspecialisatieVan/besluit:bestuurt/skos:prefLabel ?adminUnitName.
|
|
45
55
|
{
|
|
46
56
|
?verkiezing mandaat:steltSamen ?bestuursorgaanIT.
|
|
47
57
|
?kandidatenlijst mandaat:behoortTot ?verkiezing.
|
|
58
|
+
?kandidatenlijst skos:prefLabel ?kandidatenlijstLabel.
|
|
48
59
|
|
|
49
60
|
?verkiezingsresultaat mandaat:isResultaatVoor ?kandidatenlijst.
|
|
50
61
|
?verkiezingsresultaat mandaat:isResultaatVan ?person.
|
|
@@ -55,15 +66,33 @@ export async function countElectees({
|
|
|
55
66
|
org:holds ?mandaat;
|
|
56
67
|
mandaat:isBestuurlijkeAliasVan ?person.
|
|
57
68
|
?bestuursorgaanIT org:hasPost ?mandaat.
|
|
58
|
-
}
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
FILTER NOT EXISTS {
|
|
71
|
+
?_bestuursorgaanIT lmb:heeftBestuursperiode <${period}>.
|
|
72
|
+
?verkiezing mandaat:steltSamen ?_bestuursorgaanIT.
|
|
73
|
+
?kandidatenlijst mandaat:behoortTot ?verkiezing.
|
|
74
|
+
?kandidatenlijst skos:prefLabel ?kandidatenlijstLabel.
|
|
75
|
+
|
|
76
|
+
?verkiezingsresultaat mandaat:isResultaatVoor ?kandidatenlijst.
|
|
77
|
+
?verkiezingsresultaat mandaat:isResultaatVan ?person.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
${
|
|
83
|
+
searchString.length
|
|
84
|
+
? `
|
|
85
|
+
BIND(CONCAT(?firstName, " ", ?lastName) AS ?name)
|
|
86
|
+
FILTER(contains(lcase(?name), lcase(${sparqlEscapeString(searchString)}) )).`
|
|
87
|
+
: ''
|
|
88
|
+
}
|
|
89
|
+
|
|
62
90
|
}
|
|
63
91
|
`;
|
|
64
92
|
const response = await executeQuery({
|
|
65
93
|
query,
|
|
66
94
|
endpoint,
|
|
95
|
+
abortSignal,
|
|
67
96
|
});
|
|
68
97
|
return Number(response.results.bindings[0].count.value);
|
|
69
98
|
}
|
|
@@ -73,15 +102,17 @@ export async function fetchElectees({
|
|
|
73
102
|
page,
|
|
74
103
|
pageSize,
|
|
75
104
|
searchString,
|
|
76
|
-
|
|
105
|
+
administrativeUnit,
|
|
77
106
|
sort,
|
|
78
107
|
period,
|
|
108
|
+
abortSignal,
|
|
79
109
|
}: FetchMandateesArgs) {
|
|
80
110
|
const count = await countElectees({
|
|
81
111
|
endpoint,
|
|
82
112
|
searchString,
|
|
83
113
|
period,
|
|
84
|
-
|
|
114
|
+
administrativeUnit,
|
|
115
|
+
abortSignal,
|
|
85
116
|
});
|
|
86
117
|
let sortString = '?lastName ?firstName';
|
|
87
118
|
if (sort) {
|
|
@@ -116,12 +147,15 @@ export async function fetchElectees({
|
|
|
116
147
|
|
|
117
148
|
SELECT DISTINCT ?person ?firstName ?lastName ?kandidatenlijstLabel WHERE {
|
|
118
149
|
?person a person:Person;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
150
|
+
foaf:familyName ?lastName;
|
|
151
|
+
persoon:gebruikteVoornaam ?firstName.
|
|
122
152
|
|
|
123
|
-
?bestuursorgaanIT lmb:heeftBestuursperiode <${period}
|
|
124
|
-
|
|
153
|
+
?bestuursorgaanIT lmb:heeftBestuursperiode <${period}>.
|
|
154
|
+
${
|
|
155
|
+
administrativeUnit
|
|
156
|
+
? `?bestuursorgaanIT mandaat:isTijdspecialisatieVan/besluit:bestuurt ${sparqlEscapeUri(administrativeUnit.uri)}.`
|
|
157
|
+
: ''
|
|
158
|
+
}
|
|
125
159
|
{
|
|
126
160
|
?verkiezing mandaat:steltSamen ?bestuursorgaanIT.
|
|
127
161
|
?kandidatenlijst mandaat:behoortTot ?verkiezing.
|
|
@@ -148,8 +182,13 @@ export async function fetchElectees({
|
|
|
148
182
|
}
|
|
149
183
|
}
|
|
150
184
|
|
|
151
|
-
${
|
|
152
|
-
|
|
185
|
+
${
|
|
186
|
+
searchString.length
|
|
187
|
+
? `
|
|
188
|
+
BIND(CONCAT(?firstName, " ", ?lastName) AS ?name)
|
|
189
|
+
FILTER(contains(lcase(?name), lcase(${sparqlEscapeString(searchString)}) )).`
|
|
190
|
+
: ''
|
|
191
|
+
}
|
|
153
192
|
}
|
|
154
193
|
ORDER BY ${sortString}
|
|
155
194
|
LIMIT ${pageSize} OFFSET ${page * pageSize}
|
|
@@ -157,7 +196,80 @@ export async function fetchElectees({
|
|
|
157
196
|
const response = await executeQuery({
|
|
158
197
|
query,
|
|
159
198
|
endpoint,
|
|
199
|
+
abortSignal,
|
|
160
200
|
});
|
|
161
201
|
const electees = response.results.bindings.map(Electee.fromBinding);
|
|
162
202
|
return { electees, count };
|
|
163
203
|
}
|
|
204
|
+
|
|
205
|
+
type FetchAdministrativeUnitsArgs = {
|
|
206
|
+
endpoint: string;
|
|
207
|
+
searchString?: string;
|
|
208
|
+
lmbPeriod?: (typeof BESTUURSPERIODES)[keyof typeof BESTUURSPERIODES];
|
|
209
|
+
classificationCodes?: string[];
|
|
210
|
+
limit?: number;
|
|
211
|
+
abortSignal?: AbortSignal;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Function which fetches an alphabetically ordered series of administrative units (the top-level ones, not the tijdspecialisatie ones)
|
|
216
|
+
* Additionally allows to filter by period, to only return administrative units which have a tijdsspecialisatie in the given period.
|
|
217
|
+
*/
|
|
218
|
+
export async function fetchAdministrativeUnits({
|
|
219
|
+
endpoint,
|
|
220
|
+
searchString,
|
|
221
|
+
lmbPeriod,
|
|
222
|
+
classificationCodes,
|
|
223
|
+
limit,
|
|
224
|
+
abortSignal,
|
|
225
|
+
}: FetchAdministrativeUnitsArgs) {
|
|
226
|
+
const query = /* sparql */ `
|
|
227
|
+
PREFIX besluit: <http://data.vlaanderen.be/ns/besluit#>
|
|
228
|
+
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
|
|
229
|
+
PREFIX mandaat: <http://data.vlaanderen.be/ns/mandaat#>
|
|
230
|
+
PREFIX lmb: <http://lblod.data.gift/vocabularies/lmb/>
|
|
231
|
+
|
|
232
|
+
SELECT DISTINCT ?uri ?label WHERE {
|
|
233
|
+
?uri
|
|
234
|
+
a besluit:Bestuurseenheid;
|
|
235
|
+
skos:prefLabel ?bestuurseenheidLabel;
|
|
236
|
+
besluit:classificatie ?classificatie.
|
|
237
|
+
|
|
238
|
+
?classificatie skos:prefLabel ?classificatieLabel.
|
|
239
|
+
BIND(CONCAT(?classificatieLabel, " ", ?bestuurseenheidLabel) AS ?label)
|
|
240
|
+
|
|
241
|
+
${
|
|
242
|
+
classificationCodes?.length
|
|
243
|
+
? `
|
|
244
|
+
VALUES ?classificatie {
|
|
245
|
+
${classificationCodes.map(sparqlEscapeUri).join(`\n`)}
|
|
246
|
+
}
|
|
247
|
+
`
|
|
248
|
+
: ''
|
|
249
|
+
}
|
|
250
|
+
${
|
|
251
|
+
lmbPeriod
|
|
252
|
+
? `
|
|
253
|
+
?bestuursorgaanIT mandaat:isTijdspecialisatieVan/besluit:bestuurt ?uri.
|
|
254
|
+
?bestuursorgaanIT lmb:heeftBestuursperiode <${lmbPeriod}>.
|
|
255
|
+
`
|
|
256
|
+
: ''
|
|
257
|
+
}
|
|
258
|
+
${searchString?.length ? `FILTER(contains(lcase(?label), lcase(${sparqlEscapeString(searchString)}) )).` : ''}
|
|
259
|
+
}
|
|
260
|
+
ORDER BY ?label
|
|
261
|
+
${limit ? `LIMIT ${limit}` : ''}
|
|
262
|
+
`;
|
|
263
|
+
const response = await executeQuery({
|
|
264
|
+
query,
|
|
265
|
+
endpoint,
|
|
266
|
+
abortSignal,
|
|
267
|
+
});
|
|
268
|
+
const administrativeUnits = response.results.bindings.map<AdministrativeUnit>(
|
|
269
|
+
(binding) => ({
|
|
270
|
+
uri: binding.uri.value,
|
|
271
|
+
label: binding.label.value,
|
|
272
|
+
}),
|
|
273
|
+
);
|
|
274
|
+
return administrativeUnits;
|
|
275
|
+
}
|
|
@@ -75,7 +75,7 @@ function getAsyncSearchActionsGetter(args: LmbPluginConfig) {
|
|
|
75
75
|
page: 0,
|
|
76
76
|
pageSize: SEARCH_PAGE_SIZE,
|
|
77
77
|
searchString: searchQuery,
|
|
78
|
-
|
|
78
|
+
administrativeUnit: args.defaultAdminUnit,
|
|
79
79
|
sort: false,
|
|
80
80
|
period:
|
|
81
81
|
args.defaultPeriod ?? Object.values(BESTUURSPERIODES).at(-1) ?? '',
|
|
@@ -11,7 +11,7 @@ interface Args {
|
|
|
11
11
|
export default class LmbPluginInsertComponent extends Component<Args> {
|
|
12
12
|
intl: IntlService;
|
|
13
13
|
AddIcon: import("@ember/component/template-only").TOC<import("@appuniversum/ember-appuniversum/components/icons/add").AddIconSignature>;
|
|
14
|
-
get showModal(): boolean
|
|
14
|
+
get showModal(): boolean;
|
|
15
15
|
get controller(): SayController;
|
|
16
16
|
openModal(): void;
|
|
17
17
|
closeModal(): void;
|
|
@@ -1 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import type { TOC } from '@ember/component/template-only';
|
|
2
|
+
import { SearchSort } from './search-modal';
|
|
3
|
+
import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
|
|
4
|
+
import { TaskInstance } from 'reactiveweb/ember-concurrency';
|
|
5
|
+
interface Signature {
|
|
6
|
+
Args: {
|
|
7
|
+
sort: SearchSort;
|
|
8
|
+
setSort: (sort: SearchSort) => void;
|
|
9
|
+
services: TaskInstance<{
|
|
10
|
+
results: Electee[];
|
|
11
|
+
totalCount: number;
|
|
12
|
+
}>;
|
|
13
|
+
insert: (electee: Electee) => unknown;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
declare const List: TOC<Signature>;
|
|
17
|
+
export default List;
|
|
@@ -3,18 +3,21 @@ import { LmbPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/
|
|
|
3
3
|
import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
|
|
4
4
|
import { FetchMandateesArgs } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin/utils/fetchElectees';
|
|
5
5
|
import { BestuursperiodeLabel, BestuursperiodeURI } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
|
|
6
|
+
import type { AdministrativeUnit } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/worship-plugin';
|
|
6
7
|
export type SearchSort = [keyof Electee, 'ASC' | 'DESC'] | false;
|
|
7
|
-
interface
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
interface Signature {
|
|
9
|
+
Args: {
|
|
10
|
+
config: LmbPluginConfig;
|
|
11
|
+
open: boolean;
|
|
12
|
+
closeModal: () => void;
|
|
13
|
+
onInsert: (electee: Electee) => void;
|
|
14
|
+
};
|
|
12
15
|
}
|
|
13
16
|
interface AdminPeriodOption {
|
|
14
17
|
label: BestuursperiodeLabel;
|
|
15
18
|
uri: BestuursperiodeURI;
|
|
16
19
|
}
|
|
17
|
-
export default class
|
|
20
|
+
export default class SearchModal extends Component<Signature> {
|
|
18
21
|
error: unknown;
|
|
19
22
|
inputSearchText: string | null;
|
|
20
23
|
sort: SearchSort;
|
|
@@ -23,18 +26,22 @@ export default class LmbPluginSearchModalComponent extends Component<Args> {
|
|
|
23
26
|
totalCount: number;
|
|
24
27
|
selectedAdminPeriod: AdminPeriodOption;
|
|
25
28
|
adminPeriods: AdminPeriodOption[];
|
|
26
|
-
|
|
29
|
+
selectedAdministrativeUnit?: AdministrativeUnit;
|
|
27
30
|
typing: boolean;
|
|
28
|
-
constructor(owner: unknown, args: Args);
|
|
31
|
+
constructor(owner: unknown, args: Signature['Args']);
|
|
29
32
|
get config(): LmbPluginConfig;
|
|
30
33
|
selectAdminPeriod: (value: AdminPeriodOption) => void;
|
|
31
|
-
setAdminUnitSearch: (event: InputEvent) => void;
|
|
32
34
|
closeModal(): Promise<void>;
|
|
33
|
-
search: import("ember-concurrency").TaskForAsyncTaskFunction<unknown, ({ endpoint, searchString, page, pageSize, sort, period,
|
|
35
|
+
search: import("ember-concurrency").TaskForAsyncTaskFunction<unknown, ({ endpoint, searchString, page, pageSize, sort, period, administrativeUnit, }: FetchMandateesArgs) => Promise<{
|
|
34
36
|
results: Electee[];
|
|
35
37
|
totalCount: number;
|
|
36
38
|
}>>;
|
|
37
|
-
servicesResource: import("reactiveweb/ember-concurrency").TaskInstance<
|
|
39
|
+
servicesResource: import("reactiveweb/ember-concurrency").TaskInstance<{
|
|
40
|
+
results: Electee[];
|
|
41
|
+
totalCount: number;
|
|
42
|
+
}>;
|
|
43
|
+
searchAdministrativeUnits: import("ember-concurrency").TaskForAsyncTaskFunction<unknown, (search: string) => Promise<AdministrativeUnit[]>>;
|
|
44
|
+
selectAdministrativeUnit: (administrativeUnit: AdministrativeUnit) => void;
|
|
38
45
|
setSort(sort: SearchSort): void;
|
|
39
46
|
setInputSearchText(event: InputEvent): void;
|
|
40
47
|
previousPage(): void;
|
|
@@ -2,10 +2,11 @@ import { SayController } from '@lblod/ember-rdfa-editor';
|
|
|
2
2
|
import { BestuursperiodeLabel } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
|
|
3
3
|
import { Person } from '../variable-plugin/variables';
|
|
4
4
|
import { ProsePlugin, PluginKey, EditorState, EditorView, Command } from '@lblod/ember-rdfa-editor';
|
|
5
|
+
import type { AdministrativeUnit } from '../worship-plugin';
|
|
5
6
|
export type LmbPluginConfig = {
|
|
6
7
|
endpoint: string;
|
|
7
8
|
defaultPeriod?: BestuursperiodeLabel;
|
|
8
|
-
defaultAdminUnit?:
|
|
9
|
+
defaultAdminUnit?: AdministrativeUnit;
|
|
9
10
|
};
|
|
10
11
|
export declare function createPersonNode(controller: SayController, person: Person): import("prosemirror-model").Node;
|
|
11
12
|
type PluginState = {
|
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
import { SearchSort } from '@lblod/ember-rdfa-editor-lblod-plugins/components/lmb-plugin/search-modal';
|
|
2
2
|
import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
|
|
3
3
|
import { BESTUURSPERIODES } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
|
|
4
|
+
import { AdministrativeUnit } from '../../worship-plugin';
|
|
4
5
|
export type FetchMandateesArgs = {
|
|
5
6
|
endpoint: string;
|
|
6
7
|
searchString: string;
|
|
7
|
-
|
|
8
|
+
administrativeUnit?: AdministrativeUnit;
|
|
8
9
|
page: number;
|
|
9
10
|
pageSize: number;
|
|
10
11
|
sort: SearchSort;
|
|
11
12
|
period: (typeof BESTUURSPERIODES)[keyof typeof BESTUURSPERIODES];
|
|
13
|
+
abortSignal?: AbortSignal;
|
|
12
14
|
};
|
|
13
|
-
export declare function countElectees({ endpoint, searchString,
|
|
14
|
-
export declare function fetchElectees({ endpoint, page, pageSize, searchString,
|
|
15
|
+
export declare function countElectees({ endpoint, searchString, administrativeUnit, period, abortSignal, }: Pick<FetchMandateesArgs, 'searchString' | 'endpoint' | 'period' | 'administrativeUnit' | 'abortSignal'>): Promise<number>;
|
|
16
|
+
export declare function fetchElectees({ endpoint, page, pageSize, searchString, administrativeUnit, sort, period, abortSignal, }: FetchMandateesArgs): Promise<{
|
|
15
17
|
electees: Electee[];
|
|
16
18
|
count: number;
|
|
17
19
|
}>;
|
|
20
|
+
type FetchAdministrativeUnitsArgs = {
|
|
21
|
+
endpoint: string;
|
|
22
|
+
searchString?: string;
|
|
23
|
+
lmbPeriod?: (typeof BESTUURSPERIODES)[keyof typeof BESTUURSPERIODES];
|
|
24
|
+
classificationCodes?: string[];
|
|
25
|
+
limit?: number;
|
|
26
|
+
abortSignal?: AbortSignal;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Function which fetches an alphabetically ordered series of administrative units (the top-level ones, not the tijdspecialisatie ones)
|
|
30
|
+
* Additionally allows to filter by period, to only return administrative units which have a tijdsspecialisatie in the given period.
|
|
31
|
+
*/
|
|
32
|
+
export declare function fetchAdministrativeUnits({ endpoint, searchString, lmbPeriod, classificationCodes, limit, abortSignal, }: FetchAdministrativeUnitsArgs): Promise<AdministrativeUnit[]>;
|
|
33
|
+
export {};
|
package/package.json
CHANGED
package/translations/en-US.yaml
CHANGED
package/translations/nl-BE.yaml
CHANGED
|
@@ -7,6 +7,7 @@ common:
|
|
|
7
7
|
error-title: Probleem bij het laden
|
|
8
8
|
error-intro: 'Er is een fout opgetreden tijdens het laden van de resultaten:'
|
|
9
9
|
error-outro: Moest dit probleem aanhouden, neem contact op met
|
|
10
|
+
type-to-search: Typ om te zoeken
|
|
10
11
|
sort:
|
|
11
12
|
sort: Sorteren
|
|
12
13
|
asc: Oplopend sorteren
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{{! @glint-nocheck: not typesafe yet }}
|
|
2
|
-
<li class='au-c-list__item'>
|
|
3
|
-
<AuButton
|
|
4
|
-
@icon={{this.AddIcon}}
|
|
5
|
-
@iconAlignment='left'
|
|
6
|
-
@skin='link'
|
|
7
|
-
{{on 'click' this.openModal}}
|
|
8
|
-
>
|
|
9
|
-
{{t 'lmb-plugin.insert.title'}}
|
|
10
|
-
</AuButton>
|
|
11
|
-
</li>
|
|
12
|
-
|
|
13
|
-
<LmbPlugin::SearchModal
|
|
14
|
-
@open={{this.showModal}}
|
|
15
|
-
@closeModal={{this.closeModal}}
|
|
16
|
-
@config={{@config}}
|
|
17
|
-
@onInsert={{this.onInsert}}
|
|
18
|
-
/>
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
<AuTable>
|
|
2
|
-
<:header>
|
|
3
|
-
<tr class='au-c-data-table__header-title'>
|
|
4
|
-
<th>
|
|
5
|
-
<Common::Sort::SortableTableHeader
|
|
6
|
-
@field='fullName'
|
|
7
|
-
@label={{t 'lmb-plugin.modal.fields.name'}}
|
|
8
|
-
@sort={{@sort}}
|
|
9
|
-
@setSort={{@setSort}}
|
|
10
|
-
/>
|
|
11
|
-
</th>
|
|
12
|
-
<th>
|
|
13
|
-
<Common::Sort::SortableTableHeader
|
|
14
|
-
@field='kandidatenlijst'
|
|
15
|
-
@label={{t 'lmb-plugin.modal.fields.kandidatenlijst'}}
|
|
16
|
-
@sort={{@sort}}
|
|
17
|
-
@setSort={{@setSort}}
|
|
18
|
-
/>
|
|
19
|
-
</th>
|
|
20
|
-
<th />
|
|
21
|
-
</tr>
|
|
22
|
-
</:header>
|
|
23
|
-
<:body>
|
|
24
|
-
{{#if @services.isRunning}}
|
|
25
|
-
<tr>
|
|
26
|
-
<td colspan='100%'>
|
|
27
|
-
<AuLoader @centered={{true}}>
|
|
28
|
-
{{t 'common.search.loading'}}
|
|
29
|
-
</AuLoader>
|
|
30
|
-
</td>
|
|
31
|
-
</tr>
|
|
32
|
-
{{else}}
|
|
33
|
-
{{#if @services.value.totalCount}}
|
|
34
|
-
{{#each @services.value.results as |row|}}
|
|
35
|
-
<tr>
|
|
36
|
-
<td>{{row.fullName}}</td>
|
|
37
|
-
<td>
|
|
38
|
-
{{#if row.kandidatenlijst}}
|
|
39
|
-
{{row.kandidatenlijst}}
|
|
40
|
-
{{else}}
|
|
41
|
-
<span class='au-u-italic'>{{t 'common.not-applicable'}}</span>
|
|
42
|
-
{{/if}}
|
|
43
|
-
</td>
|
|
44
|
-
<td class='au-u-text-center'>
|
|
45
|
-
<AuButton {{on 'click' (fn @insert row)}}>
|
|
46
|
-
{{t 'lmb-plugin.modal.insert'}}
|
|
47
|
-
</AuButton>
|
|
48
|
-
</td>
|
|
49
|
-
</tr>
|
|
50
|
-
{{/each}}
|
|
51
|
-
{{else}}
|
|
52
|
-
<tr>
|
|
53
|
-
<td colspan='100%'>
|
|
54
|
-
{{t 'common.search.no-results'}}
|
|
55
|
-
</td>
|
|
56
|
-
</tr>
|
|
57
|
-
{{/if}}
|
|
58
|
-
{{/if}}
|
|
59
|
-
</:body>
|
|
60
|
-
</AuTable>
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
{{! @glint-nocheck: not typesafe yet }}
|
|
2
|
-
<AuModal
|
|
3
|
-
class='worship-modal'
|
|
4
|
-
@modalOpen={{@open}}
|
|
5
|
-
@closeModal={{this.closeModal}}
|
|
6
|
-
@title={{t 'lmb-plugin.modal.title'}}
|
|
7
|
-
@size='large'
|
|
8
|
-
@padding='none'
|
|
9
|
-
as |modal|
|
|
10
|
-
>
|
|
11
|
-
<modal.Body>
|
|
12
|
-
<AuMainContainer
|
|
13
|
-
@scroll={{true}}
|
|
14
|
-
class='worship-modal--main-container'
|
|
15
|
-
as |mc|
|
|
16
|
-
>
|
|
17
|
-
<mc.sidebar>
|
|
18
|
-
<div class='au-c-sidebar'>
|
|
19
|
-
<div class='au-c-sidebar__content au-u-padding'>
|
|
20
|
-
<AuHeading @level='3' @skin='4' class='au-u-padding-bottom-small'>
|
|
21
|
-
{{t 'lmb-plugin.modal.search.title'}}
|
|
22
|
-
</AuHeading>
|
|
23
|
-
<AuLabel class='au-margin-bottom-small' for='searchTerm'>
|
|
24
|
-
{{t 'lmb-plugin.modal.fields.name'}}
|
|
25
|
-
</AuLabel>
|
|
26
|
-
<AuNativeInput
|
|
27
|
-
@type='text'
|
|
28
|
-
@width='block'
|
|
29
|
-
id='searchTerm'
|
|
30
|
-
value={{this.searchText}}
|
|
31
|
-
placeholder={{t 'lmb-plugin.modal.nameSearch.placeholder'}}
|
|
32
|
-
{{on 'input' this.setInputSearchText}}
|
|
33
|
-
/>
|
|
34
|
-
<AuLabel class='au-margin-bottom-small' for='periodSelect'>
|
|
35
|
-
{{t 'lmb-plugin.modal.fields.period'}}
|
|
36
|
-
</AuLabel>
|
|
37
|
-
<PowerSelect
|
|
38
|
-
@allowClear={{false}}
|
|
39
|
-
@searchEnabled={{false}}
|
|
40
|
-
@options={{this.adminPeriods}}
|
|
41
|
-
@selected={{this.selectedAdminPeriod}}
|
|
42
|
-
@onChange={{this.selectAdminPeriod}}
|
|
43
|
-
as |period|
|
|
44
|
-
>
|
|
45
|
-
{{period.label}}
|
|
46
|
-
</PowerSelect>
|
|
47
|
-
|
|
48
|
-
<AuLabel class='au-margin-bottom-small' for='searchAdminUnit'>
|
|
49
|
-
{{t 'lmb-plugin.modal.fields.adminUnit'}}
|
|
50
|
-
</AuLabel>
|
|
51
|
-
<AuNativeInput
|
|
52
|
-
@type='text'
|
|
53
|
-
@width='block'
|
|
54
|
-
id='searchAdminUnit'
|
|
55
|
-
value={{this.adminUnitSearch}}
|
|
56
|
-
placeholder={{t 'lmb-plugin.modal.adminUnitSearch.placeholder'}}
|
|
57
|
-
{{on 'input' this.setAdminUnitSearch}}
|
|
58
|
-
/>
|
|
59
|
-
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
</mc.sidebar>
|
|
63
|
-
<mc.content @scroll={{true}}>
|
|
64
|
-
<div class='worship-modal--list-container'>
|
|
65
|
-
{{#if this.error}}
|
|
66
|
-
<Common::Search::AlertLoadError @error={{this.error}} />
|
|
67
|
-
{{else}}
|
|
68
|
-
<LmbPlugin::List
|
|
69
|
-
@services={{this.servicesResource}}
|
|
70
|
-
@sort={{this.sort}}
|
|
71
|
-
@setSort={{this.setSort}}
|
|
72
|
-
@insert={{this.onInsert}}
|
|
73
|
-
/>
|
|
74
|
-
{{/if}}
|
|
75
|
-
</div>
|
|
76
|
-
{{#if this.servicesResource.value.totalCount}}
|
|
77
|
-
{{#let
|
|
78
|
-
(pagination
|
|
79
|
-
page=this.pageNumber
|
|
80
|
-
pageSize=this.pageSize
|
|
81
|
-
count=this.servicesResource.value.totalCount
|
|
82
|
-
)
|
|
83
|
-
as |pg|
|
|
84
|
-
}}
|
|
85
|
-
<Pagination::PaginationView
|
|
86
|
-
@totalCount={{pg.count}}
|
|
87
|
-
@rangeStart={{pg.pageStart}}
|
|
88
|
-
@rangeEnd={{pg.pageEnd}}
|
|
89
|
-
@onNextPage={{this.nextPage}}
|
|
90
|
-
@onPreviousPage={{this.previousPage}}
|
|
91
|
-
@isFirstPage={{not pg.hasPreviousPage}}
|
|
92
|
-
@isLastPage={{not pg.hasNextPage}}
|
|
93
|
-
/>
|
|
94
|
-
{{/let}}
|
|
95
|
-
{{/if}}
|
|
96
|
-
|
|
97
|
-
</mc.content>
|
|
98
|
-
</AuMainContainer>
|
|
99
|
-
</modal.Body>
|
|
100
|
-
</AuModal>
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import Component from '@glimmer/component';
|
|
2
|
-
import { tracked } from '@glimmer/tracking';
|
|
3
|
-
import { assert } from '@ember/debug';
|
|
4
|
-
import { action } from '@ember/object';
|
|
5
|
-
import { restartableTask, timeout } from 'ember-concurrency';
|
|
6
|
-
import { task as trackedTask } from 'reactiveweb/ember-concurrency';
|
|
7
|
-
import { LmbPluginConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin';
|
|
8
|
-
|
|
9
|
-
import Electee from '@lblod/ember-rdfa-editor-lblod-plugins/models/electee';
|
|
10
|
-
import {
|
|
11
|
-
FetchMandateesArgs,
|
|
12
|
-
fetchElectees,
|
|
13
|
-
} from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/lmb-plugin/utils/fetchElectees';
|
|
14
|
-
import {
|
|
15
|
-
BESTUURSPERIODES,
|
|
16
|
-
BestuursperiodeLabel,
|
|
17
|
-
BestuursperiodeURI,
|
|
18
|
-
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/constants';
|
|
19
|
-
import { isSome } from '@lblod/ember-rdfa-editor/utils/_private/option';
|
|
20
|
-
export type SearchSort = [keyof Electee, 'ASC' | 'DESC'] | false;
|
|
21
|
-
|
|
22
|
-
interface Args {
|
|
23
|
-
config: LmbPluginConfig;
|
|
24
|
-
open: boolean;
|
|
25
|
-
closeModal: () => void;
|
|
26
|
-
onInsert: (electee: Electee) => void;
|
|
27
|
-
}
|
|
28
|
-
interface AdminPeriodOption {
|
|
29
|
-
label: BestuursperiodeLabel;
|
|
30
|
-
uri: BestuursperiodeURI;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export default class LmbPluginSearchModalComponent extends Component<Args> {
|
|
34
|
-
// Display
|
|
35
|
-
@tracked error: unknown;
|
|
36
|
-
@tracked inputSearchText: string | null = null;
|
|
37
|
-
@tracked sort: SearchSort = false;
|
|
38
|
-
|
|
39
|
-
// Pagination
|
|
40
|
-
@tracked pageNumber = 0;
|
|
41
|
-
@tracked pageSize = 20;
|
|
42
|
-
@tracked totalCount = 0;
|
|
43
|
-
|
|
44
|
-
// Admin periods
|
|
45
|
-
@tracked selectedAdminPeriod: AdminPeriodOption;
|
|
46
|
-
adminPeriods: AdminPeriodOption[];
|
|
47
|
-
// Admin units
|
|
48
|
-
@tracked adminUnitSearch: string;
|
|
49
|
-
// tracks whether the user has just typed a character
|
|
50
|
-
// doesn't need to be reactive
|
|
51
|
-
typing = false;
|
|
52
|
-
constructor(owner: unknown, args: Args) {
|
|
53
|
-
super(owner, args);
|
|
54
|
-
this.adminPeriods = Object.entries(BESTUURSPERIODES).map(
|
|
55
|
-
([key, value]: [BestuursperiodeLabel, BestuursperiodeURI]) => ({
|
|
56
|
-
label: key,
|
|
57
|
-
uri: value,
|
|
58
|
-
}),
|
|
59
|
-
);
|
|
60
|
-
this.selectedAdminPeriod =
|
|
61
|
-
this.adminPeriods.find(
|
|
62
|
-
(entry) =>
|
|
63
|
-
isSome(args.config.defaultPeriod) &&
|
|
64
|
-
entry.label === args.config.defaultPeriod,
|
|
65
|
-
) ?? this.adminPeriods[this.adminPeriods.length - 1];
|
|
66
|
-
this.adminUnitSearch = args.config.defaultAdminUnit ?? '';
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
get config() {
|
|
70
|
-
return this.args.config;
|
|
71
|
-
}
|
|
72
|
-
selectAdminPeriod = (value: AdminPeriodOption) => {
|
|
73
|
-
this.selectedAdminPeriod = value;
|
|
74
|
-
this.pageNumber = 0;
|
|
75
|
-
};
|
|
76
|
-
setAdminUnitSearch = (event: InputEvent) => {
|
|
77
|
-
this.typing = true;
|
|
78
|
-
assert(
|
|
79
|
-
'setAdminUnitSearch must be bound to an input element',
|
|
80
|
-
event.target instanceof HTMLInputElement,
|
|
81
|
-
);
|
|
82
|
-
this.adminUnitSearch = event.target.value;
|
|
83
|
-
this.pageNumber = 0;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
@action
|
|
87
|
-
async closeModal() {
|
|
88
|
-
this.typing = false;
|
|
89
|
-
this.inputSearchText = null;
|
|
90
|
-
this.sort = false;
|
|
91
|
-
await this.servicesResource.cancel();
|
|
92
|
-
this.args.closeModal();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// TODO Either make this a trackedFunction or do filtering on the query and correctly pass an
|
|
96
|
-
// AbortController
|
|
97
|
-
search = restartableTask(
|
|
98
|
-
async ({
|
|
99
|
-
endpoint,
|
|
100
|
-
searchString,
|
|
101
|
-
page,
|
|
102
|
-
pageSize,
|
|
103
|
-
sort,
|
|
104
|
-
period,
|
|
105
|
-
adminUnitSearch,
|
|
106
|
-
}: FetchMandateesArgs) => {
|
|
107
|
-
// debounce, but only when the input fields are being used
|
|
108
|
-
if (this.typing) {
|
|
109
|
-
this.typing = false;
|
|
110
|
-
await timeout(250);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (!this.args.open) {
|
|
114
|
-
return {
|
|
115
|
-
results: [],
|
|
116
|
-
totalCount: 0,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const result = await fetchElectees({
|
|
122
|
-
endpoint,
|
|
123
|
-
searchString,
|
|
124
|
-
page,
|
|
125
|
-
pageSize,
|
|
126
|
-
sort,
|
|
127
|
-
period,
|
|
128
|
-
adminUnitSearch,
|
|
129
|
-
});
|
|
130
|
-
const { count, electees } = result;
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
results: electees,
|
|
134
|
-
totalCount: count,
|
|
135
|
-
};
|
|
136
|
-
} catch (err) {
|
|
137
|
-
console.error('Got an error fetching electees', err);
|
|
138
|
-
this.error = err;
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
results: [],
|
|
142
|
-
totalCount: 0,
|
|
143
|
-
};
|
|
144
|
-
},
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
servicesResource = trackedTask(this, this.search, () => [
|
|
148
|
-
{
|
|
149
|
-
endpoint: this.args.config.endpoint,
|
|
150
|
-
searchString: this.inputSearchText ?? '',
|
|
151
|
-
sort: this.sort,
|
|
152
|
-
page: this.pageNumber,
|
|
153
|
-
pageSize: this.pageSize,
|
|
154
|
-
open: this.args.open,
|
|
155
|
-
period: this.selectedAdminPeriod.uri,
|
|
156
|
-
adminUnitSearch: this.adminUnitSearch,
|
|
157
|
-
} satisfies Partial<FetchMandateesArgs> & { open: boolean },
|
|
158
|
-
]);
|
|
159
|
-
|
|
160
|
-
@action
|
|
161
|
-
setSort(sort: SearchSort) {
|
|
162
|
-
this.sort = sort;
|
|
163
|
-
}
|
|
164
|
-
@action
|
|
165
|
-
setInputSearchText(event: InputEvent) {
|
|
166
|
-
assert(
|
|
167
|
-
'inputSearchText must be bound to an input element',
|
|
168
|
-
event.target instanceof HTMLInputElement,
|
|
169
|
-
);
|
|
170
|
-
this.typing = true;
|
|
171
|
-
|
|
172
|
-
this.inputSearchText = event.target.value;
|
|
173
|
-
this.pageNumber = 0;
|
|
174
|
-
}
|
|
175
|
-
@action
|
|
176
|
-
previousPage() {
|
|
177
|
-
--this.pageNumber;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
@action
|
|
181
|
-
nextPage() {
|
|
182
|
-
++this.pageNumber;
|
|
183
|
-
}
|
|
184
|
-
@action
|
|
185
|
-
async onInsert(electee: Electee) {
|
|
186
|
-
this.args.onInsert(electee);
|
|
187
|
-
await this.closeModal();
|
|
188
|
-
}
|
|
189
|
-
}
|