@things-factory/oauth2-client 8.0.0-beta.9 → 8.0.2

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.
@@ -0,0 +1 @@
1
+ export default function bootstrap() {}
File without changes
@@ -0,0 +1,87 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@operato/data-grist'
3
+
4
+ import gql from 'graphql-tag'
5
+ import { css, html, LitElement } from 'lit'
6
+ import { property } from 'lit/decorators.js'
7
+
8
+ import { client } from '@operato/graphql'
9
+ import { i18next } from '@operato/i18n'
10
+ import { isMobileDevice } from '@operato/utils'
11
+ import { CommonHeaderStyles } from '@operato/styles'
12
+
13
+ export class Oauth2ClientImporter extends LitElement {
14
+ static styles = [
15
+ CommonHeaderStyles,
16
+ css`
17
+ :host {
18
+ display: flex;
19
+ flex-direction: column;
20
+
21
+ background-color: var(--md-sys-color-surface);
22
+ }
23
+
24
+ ox-grist {
25
+ flex: 1;
26
+ }
27
+ `
28
+ ]
29
+
30
+ @property({ type: Array }) oauth2Clients: any[] = []
31
+ @property({ type: Object }) columns = {
32
+ list: { fields: ['name', 'description'] },
33
+ pagination: { infinite: true },
34
+ columns: [
35
+ {
36
+ type: 'string',
37
+ name: 'name',
38
+ header: i18next.t('field.name'),
39
+ width: 150
40
+ },
41
+ {
42
+ type: 'string',
43
+ name: 'description',
44
+ header: i18next.t('field.description'),
45
+ width: 200
46
+ },
47
+ {
48
+ type: 'checkbox',
49
+ name: 'active',
50
+ header: i18next.t('field.active'),
51
+ width: 60
52
+ }
53
+ ]
54
+ }
55
+
56
+ render() {
57
+ return html`
58
+ <ox-grist
59
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
60
+ .config=${this.columns}
61
+ .data=${{
62
+ records: this.oauth2Clients
63
+ }}
64
+ ></ox-grist>
65
+
66
+ <div class="footer">
67
+ <div filler></div>
68
+ <button @click=${this.save.bind(this)} done><md-icon>save</md-icon>${i18next.t('button.save')}</button>
69
+ </div>
70
+ `
71
+ }
72
+
73
+ async save() {
74
+ const response = await client.mutate({
75
+ mutation: gql`
76
+ mutation importOauth2Clients($oauth2Clients: [Oauth2ClientPatch!]!) {
77
+ importOauth2Clients(oauth2Clients: $oauth2Clients)
78
+ }
79
+ `,
80
+ variables: { oauth2Clients: this.oauth2Clients }
81
+ })
82
+
83
+ if (response.errors?.length) return
84
+
85
+ this.dispatchEvent(new CustomEvent('imported'))
86
+ }
87
+ }
@@ -0,0 +1,312 @@
1
+ import '@operato/data-grist'
2
+
3
+ import { CommonButtonStyles, CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
4
+ import { PageView, store } from '@operato/shell'
5
+ import { css, html } from 'lit'
6
+ import { customElement, property, query } from 'lit/decorators.js'
7
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
8
+ import { ColumnConfig, DataGrist, FetchOption } from '@operato/data-grist'
9
+ import { client } from '@operato/graphql'
10
+ import { i18next, localize } from '@operato/i18n'
11
+ import { notify, openPopup } from '@operato/layout'
12
+ import { isMobileDevice } from '@operato/utils'
13
+
14
+ import { connect } from 'pwa-helpers/connect-mixin'
15
+ import gql from 'graphql-tag'
16
+
17
+ import { Oauth2ClientImporter } from './oauth2-client-importer'
18
+
19
+ @customElement('oauth2-client-list-page')
20
+ export class Oauth2ClientListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
21
+ static styles = [
22
+ ScrollbarStyles,
23
+ CommonGristStyles,
24
+ CommonHeaderStyles,
25
+ css`
26
+ :host {
27
+ display: flex;
28
+
29
+ width: 100%;
30
+
31
+ --grid-record-emphasized-background-color: #8b0000;
32
+ --grid-record-emphasized-color: #ff6b6b;
33
+ }
34
+
35
+ ox-grist {
36
+ overflow-y: auto;
37
+ flex: 1;
38
+ }
39
+
40
+ ox-filters-form {
41
+ flex: 1;
42
+ }
43
+ `
44
+ ]
45
+
46
+ static get scopedElements() {
47
+ return {
48
+ 'oauth2-client-importer': Oauth2ClientImporter
49
+ }
50
+ }
51
+
52
+ @property({ type: Object }) gristConfig: any
53
+ @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
54
+
55
+ @query('ox-grist') private grist!: DataGrist
56
+
57
+ get context() {
58
+ return {
59
+ title: i18next.t('title.oauth2-client list'),
60
+ help: 'oauth2-client/oauth2-client',
61
+ actions: [
62
+ {
63
+ title: i18next.t('button.save'),
64
+ action: this._updateOauth2Client.bind(this),
65
+ ...CommonButtonStyles.save
66
+ },
67
+ {
68
+ title: i18next.t('button.delete'),
69
+ action: this._deleteOauth2Client.bind(this),
70
+ ...CommonButtonStyles.delete
71
+ }
72
+ ],
73
+ exportable: {
74
+ name: i18next.t('title.oauth2-client list'),
75
+ data: this.exportHandler.bind(this)
76
+ },
77
+ importable: {
78
+ handler: this.importHandler.bind(this)
79
+ }
80
+ }
81
+ }
82
+
83
+ render() {
84
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
85
+
86
+ return html`
87
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
88
+ <div slot="headroom" class="header">
89
+ <div class="filters">
90
+ <ox-filters-form autofocus></ox-filters-form>
91
+
92
+ <div id="modes">
93
+ <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
94
+ <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
95
+ <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </ox-grist>
100
+ `
101
+ }
102
+
103
+ async pageInitialized(lifecycle: any) {
104
+ this.gristConfig = {
105
+ list: {
106
+ fields: ['name', 'description'],
107
+ details: ['active', 'updatedAt']
108
+ },
109
+ columns: [
110
+ { type: 'gutter', gutterName: 'sequence' },
111
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
112
+ {
113
+ type: 'string',
114
+ name: 'name',
115
+ header: i18next.t('field.name'),
116
+ record: {
117
+ editable: true
118
+ },
119
+ filter: 'search',
120
+ sortable: true,
121
+ width: 150
122
+ },
123
+ {
124
+ type: 'string',
125
+ name: 'description',
126
+ header: i18next.t('field.description'),
127
+ record: {
128
+ editable: true
129
+ },
130
+ filter: 'search',
131
+ width: 200
132
+ },
133
+ {
134
+ type: 'checkbox',
135
+ name: 'active',
136
+ label: true,
137
+ header: i18next.t('field.active'),
138
+ record: {
139
+ editable: true
140
+ },
141
+ filter: true,
142
+ sortable: true,
143
+ width: 60
144
+ },
145
+ {
146
+ type: 'resource-object',
147
+ name: 'updater',
148
+ header: i18next.t('field.updater'),
149
+ record: {
150
+ editable: false
151
+ },
152
+ sortable: true,
153
+ width: 120
154
+ },
155
+ {
156
+ type: 'datetime',
157
+ name: 'updatedAt',
158
+ header: i18next.t('field.updated_at'),
159
+ record: {
160
+ editable: false
161
+ },
162
+ sortable: true,
163
+ width: 180
164
+ }
165
+ ],
166
+ rows: {
167
+ selectable: {
168
+ multiple: true
169
+ }
170
+ },
171
+ sorters: [
172
+ {
173
+ name: 'name'
174
+ }
175
+ ]
176
+ }
177
+ }
178
+
179
+ async pageUpdated(changes: any, lifecycle: any) {
180
+ if (this.active) {
181
+ // do something here when this page just became as active
182
+ }
183
+ }
184
+
185
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
186
+ const response = await client.query({
187
+ query: gql`
188
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
189
+ responses: oauth2Clients(filters: $filters, pagination: $pagination, sortings: $sortings) {
190
+ items {
191
+ id
192
+ name
193
+ description
194
+ active
195
+ updater {
196
+ id
197
+ name
198
+ }
199
+ updatedAt
200
+ }
201
+ total
202
+ }
203
+ }
204
+ `,
205
+ variables: {
206
+ filters,
207
+ pagination: { page, limit },
208
+ sortings
209
+ }
210
+ })
211
+
212
+ return {
213
+ total: response.data.responses.total || 0,
214
+ records: response.data.responses.items || []
215
+ }
216
+ }
217
+
218
+ async _deleteOauth2Client() {
219
+ if (confirm(i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }))) {
220
+ const ids = this.grist.selected.map(record => record.id)
221
+ if (ids && ids.length > 0) {
222
+ const response = await client.mutate({
223
+ mutation: gql`
224
+ mutation ($ids: [String!]!) {
225
+ deleteOauth2Clients(ids: $ids)
226
+ }
227
+ `,
228
+ variables: {
229
+ ids
230
+ }
231
+ })
232
+
233
+ if (!response.errors) {
234
+ this.grist.fetch()
235
+ notify({
236
+ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
237
+ })
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ async _updateOauth2Client() {
244
+ let patches = this.grist.dirtyRecords
245
+ if (patches && patches.length) {
246
+ patches = patches.map(patch => {
247
+ let patchField: any = patch.id ? { id: patch.id } : {}
248
+ const dirtyFields = patch.__dirtyfields__
249
+ for (let key in dirtyFields) {
250
+ patchField[key] = dirtyFields[key].after
251
+ }
252
+ patchField.cuFlag = patch.__dirty__
253
+
254
+ return patchField
255
+ })
256
+
257
+ const response = await client.mutate({
258
+ mutation: gql`
259
+ mutation ($patches: [Oauth2ClientPatch!]!) {
260
+ updateMultipleOauth2Client(patches: $patches) {
261
+ name
262
+ }
263
+ }
264
+ `,
265
+ variables: {
266
+ patches
267
+ }
268
+ })
269
+
270
+ if (!response.errors) {
271
+ this.grist.fetch()
272
+ }
273
+ }
274
+ }
275
+
276
+ async exportHandler() {
277
+ const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
278
+ const targetFieldSet = new Set(['id', 'name', 'description', 'active'])
279
+
280
+ return exportTargets.map(oauth2Client => {
281
+ let tempObj = {}
282
+ for (const field of targetFieldSet) {
283
+ tempObj[field] = oauth2Client[field]
284
+ }
285
+
286
+ return tempObj
287
+ })
288
+ }
289
+
290
+ async importHandler(records) {
291
+ const popup = openPopup(
292
+ html`
293
+ <oauth2-client-importer
294
+ .oauth2Clients=${records}
295
+ @imported=${() => {
296
+ history.back()
297
+ this.grist.fetch()
298
+ }}
299
+ ></oauth2-client-importer>
300
+ `,
301
+ {
302
+ backdrop: true,
303
+ size: 'large',
304
+ title: i18next.t('title.import oauth2-client')
305
+ }
306
+ )
307
+
308
+ popup.onclosed = () => {
309
+ this.grist.fetch()
310
+ }
311
+ }
312
+ }
@@ -0,0 +1,189 @@
1
+ import '@material/web/button/elevated-button.js'
2
+
3
+ import gql from 'graphql-tag'
4
+ import { css, html } from 'lit'
5
+ import { customElement, property, query } from 'lit/decorators.js'
6
+ import { connect } from 'pwa-helpers/connect-mixin.js'
7
+
8
+ import { client } from '@operato/graphql'
9
+ import { notify } from '@operato/layout'
10
+ import { navigate, PageView, store } from '@operato/shell'
11
+
12
+ @customElement('oauth2-client-register')
13
+ export class Oauth2ClientRegister extends connect(store)(PageView) {
14
+ static styles = [
15
+ css`
16
+ :host {
17
+ display: flex;
18
+ flex-direction: column;
19
+ background-color: var(--md-sys-color-background);
20
+ padding: var(--spacing-large);
21
+
22
+ position: relative;
23
+
24
+ overflow: auto;
25
+ }
26
+ h2 {
27
+ margin: var(--title-margin);
28
+ font: var(--title-font);
29
+ color: var(--title-text-color);
30
+ }
31
+ [page-description] {
32
+ margin: var(--page-description-margin);
33
+ font: var(--page-description-font);
34
+ color: var(--page-description-color);
35
+ }
36
+ [icon] {
37
+ position: absolute;
38
+ top: 10px;
39
+ right: 10px;
40
+
41
+ max-width: 80px;
42
+ }
43
+ [icon] img {
44
+ max-width: 100%;
45
+ max-height: 100%;
46
+ }
47
+
48
+ :host * {
49
+ display: block;
50
+ }
51
+ label {
52
+ font: var(--label-font);
53
+ color: var(--label-color, var(--md-sys-color-on-surface));
54
+ text-transform: var(--label-text-transform);
55
+ }
56
+ input,
57
+ select {
58
+ border: var(--border-dim-color);
59
+ border-radius: var(--border-radius);
60
+ margin: var(--input-margin);
61
+ padding: var(--input-padding);
62
+ font: var(--input-font);
63
+
64
+ flex: 1;
65
+ }
66
+ select:focus,
67
+ input:focus {
68
+ outline: none;
69
+ }
70
+
71
+ [field-2column] {
72
+ display: grid;
73
+ grid-template-columns: 1fr 1fr;
74
+ grid-gap: 15px;
75
+ }
76
+ [field] {
77
+ display: flex;
78
+ flex-direction: column;
79
+ }
80
+ [grid-span] {
81
+ grid-column: span 2;
82
+ }
83
+ @media screen and (max-width: 480px) {
84
+ [field] {
85
+ grid-column: span 2;
86
+ }
87
+ }
88
+ `
89
+ ]
90
+
91
+ @property({ type: Object }) application: any
92
+ @property({ type: String }) _icon?: string
93
+
94
+ @query('form') form!: HTMLFormElement
95
+
96
+ get context() {
97
+ return {
98
+ title: `oauth2 client registration`
99
+ }
100
+ }
101
+
102
+ render() {
103
+ return html`
104
+ <h2>Register new oauth2 client</h2>
105
+ <p page-description>You can register new oauth2 client here</p>
106
+
107
+ ${this._icon
108
+ ? html`
109
+ <div icon>
110
+ <img src=${this._icon} />
111
+ </div>
112
+ `
113
+ : html``}
114
+
115
+ <form>
116
+ <div field-2column>
117
+ <div field grid-span>
118
+ <label for="name">name</label>
119
+ <input type="text" id="name" name="name" />
120
+ </div>
121
+
122
+ <div field grid-span>
123
+ <label for="description">description</label>
124
+ <input type="text" id="description" name="description" />
125
+ </div>
126
+
127
+ <div field grid-span>
128
+ <label for="icon">icon</label>
129
+ <input type="text" id="icon" name="icon" @change=${e => (this._icon = e.target.value)} />
130
+ </div>
131
+
132
+ <div field grid-span>
133
+ <label for="client-id">client id</label>
134
+ <input type="text" id="client-id" name="clientId" />
135
+ The client identifier issued to the client during the application registration process.
136
+ </div>
137
+
138
+ <div field grid-span>
139
+ <label for="client-secret">client secret</label>
140
+ <input type="text" id="client-secret" name="clientSecret" />
141
+ The client secret issued to the client during the application registration process.
142
+ </div>
143
+ </div>
144
+
145
+ <md-elevated-button @click=${this.createOauth2Client.bind(this)}>register</md-elevated-button>
146
+ </form>
147
+ `
148
+ }
149
+
150
+ async createOauth2Client(e) {
151
+ e.preventDefault()
152
+
153
+ const formData = new FormData(this.form)
154
+
155
+ const oauth2Client = Array.from(formData.entries()).reduce((oauth2Client, [key, value]) => {
156
+ oauth2Client[key] = value
157
+ return oauth2Client
158
+ }, {})
159
+
160
+ const response = await client.mutate({
161
+ mutation: gql`
162
+ mutation ($oauth2Client: NewOauth2Client!) {
163
+ createOauth2Client(oauth2Client: $oauth2Client) {
164
+ id
165
+ }
166
+ }
167
+ `,
168
+ variables: {
169
+ oauth2Client
170
+ }
171
+ })
172
+
173
+ if (response.errors) {
174
+ notify({
175
+ level: 'error',
176
+ message: 'create oauth2 client fail'
177
+ })
178
+ } else {
179
+ const id = response.data.createOauth2Client.id
180
+ navigate(`oauth2-client/${id}`)
181
+ }
182
+ }
183
+
184
+ async pageUpdated(changes, lifecycle, before) {
185
+ if (this.active) {
186
+ this.form.reset()
187
+ }
188
+ }
189
+ }