@things-factory/auth-ui 10.1.18 → 10.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/components/app-binding-detail.js +61 -57
- package/dist-client/components/app-binding-detail.js.map +1 -1
- package/dist-client/components/appliance-editor.js +67 -43
- package/dist-client/components/appliance-editor.js.map +1 -1
- package/dist-client/components/application-editor.js +112 -91
- package/dist-client/components/application-editor.js.map +1 -1
- package/dist-client/components/create-domain-popup.js +2 -1
- package/dist-client/components/create-domain-popup.js.map +1 -1
- package/dist-client/components/my-login-history.js +16 -12
- package/dist-client/components/my-login-history.js.map +1 -1
- package/dist-client/components/ownership-transfer-popup.js +2 -0
- package/dist-client/components/ownership-transfer-popup.js.map +1 -1
- package/dist-client/components/user-detail.js +8 -7
- package/dist-client/components/user-detail.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -8,7 +8,7 @@ import { customElement, property, query, state } from 'lit/decorators.js';
|
|
|
8
8
|
import { client } from '@operato/graphql';
|
|
9
9
|
import { i18next, localize } from '@operato/i18n';
|
|
10
10
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
11
|
-
import { CommonHeaderStyles, CommonPopupStyles } from '@operato/styles';
|
|
11
|
+
import { CommonHeaderStyles, CommonPopupStyles, ScrollbarStyles } from '@operato/styles';
|
|
12
12
|
import './secret-field.js';
|
|
13
13
|
import { APPLICATION_TYPES } from '../constants/application.js';
|
|
14
14
|
import { changedValues, filledValues, missingRequired } from '../utils/form-values.js';
|
|
@@ -67,27 +67,62 @@ let ApplicationEditor = class ApplicationEditor extends localize(i18next)(LitEle
|
|
|
67
67
|
this.failed = false;
|
|
68
68
|
this.loadFailed = false;
|
|
69
69
|
}
|
|
70
|
+
/*
|
|
71
|
+
* ── Where the scrolling happens ─────────────────────────────────────────
|
|
72
|
+
* The button row stays put and only the middle scrolls. That is the house shape for a
|
|
73
|
+
* popup, and this had it wrong: the host itself scrolled, so the row you press to finish
|
|
74
|
+
* walked off the bottom of the card along with everything else.
|
|
75
|
+
*
|
|
76
|
+
* So the host clips, and the content area between the title and the buttons is the one
|
|
77
|
+
* thing with overflow. Two details carry it:
|
|
78
|
+
*
|
|
79
|
+
* min-height: 0 A flex item defaults to min-height auto, which refuses to shrink below
|
|
80
|
+
* its own content. Without this the overflow rule sits there doing
|
|
81
|
+
* nothing and the card grows instead — it reads as a working rule.
|
|
82
|
+
* no padding CommonPopupStyles already pads the host. Padding here again doubles the
|
|
83
|
+
* inset and pushes the scrollbar in off the card edge.
|
|
84
|
+
*
|
|
85
|
+
* ScrollbarStyles gives the scrollbar the product's own shape rather than the browser's.
|
|
86
|
+
*
|
|
87
|
+
* ── Why the in-content buttons need a rule of their own ─────────────────
|
|
88
|
+
* CommonPopupStyles makes controls pill-shaped because a popup is a rounded card and the
|
|
89
|
+
* things inside it follow the card. But it says that through `.buttons`, so only the commit
|
|
90
|
+
* row got it: the actions sitting inside a fieldset — generate a new token, generate a new
|
|
91
|
+
* secret — kept the page radius and read as a different kind of button on the same card.
|
|
92
|
+
* Measured on a live popup: 5px inside the content, 13986px in the row below it.
|
|
93
|
+
*/
|
|
70
94
|
static { this.styles = [
|
|
71
95
|
CommonHeaderStyles,
|
|
72
96
|
CommonPopupStyles,
|
|
97
|
+
ScrollbarStyles,
|
|
73
98
|
css `
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
/* in-content actions follow the card too, not just the commit row. see the note on styles. */
|
|
100
|
+
fieldset md-text-button,
|
|
101
|
+
fieldset md-filled-button {
|
|
102
|
+
--md-text-button-container-shape: var(--popup-control-radius);
|
|
103
|
+
--md-filled-button-container-shape: var(--popup-control-radius);
|
|
104
|
+
}
|
|
77
105
|
|
|
78
|
-
|
|
79
|
-
|
|
106
|
+
/* the host does not scroll; the content area below does. see the note on styles. */
|
|
107
|
+
:host {
|
|
108
|
+
overflow: hidden;
|
|
80
109
|
}
|
|
81
110
|
|
|
111
|
+
/* min-height:0 is load-bearing — see the note on styles. */
|
|
82
112
|
form {
|
|
83
113
|
flex: 1;
|
|
84
|
-
|
|
114
|
+
min-height: 0;
|
|
115
|
+
overflow-y: auto;
|
|
116
|
+
|
|
117
|
+
display: flex;
|
|
118
|
+
flex-direction: column;
|
|
119
|
+
gap: var(--popup-gap);
|
|
85
120
|
}
|
|
86
121
|
|
|
87
122
|
fieldset {
|
|
88
123
|
border-radius: var(--border-radius);
|
|
89
124
|
border: var(--border-dim-color);
|
|
90
|
-
margin: 0
|
|
125
|
+
margin: 0;
|
|
91
126
|
padding: var(--popup-padding);
|
|
92
127
|
}
|
|
93
128
|
|
|
@@ -97,19 +132,11 @@ let ApplicationEditor = class ApplicationEditor extends localize(i18next)(LitEle
|
|
|
97
132
|
color: var(--legend-color);
|
|
98
133
|
}
|
|
99
134
|
|
|
100
|
-
|
|
101
|
-
display: grid;
|
|
102
|
-
grid-template-columns: 1fr 1fr;
|
|
103
|
-
grid-gap: var(--popup-gap);
|
|
104
|
-
}
|
|
105
|
-
|
|
135
|
+
/* one labelled box. .row puts two of them side by side; alone it spans the card. */
|
|
106
136
|
[field] {
|
|
107
137
|
display: flex;
|
|
108
138
|
flex-direction: column;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
[grid-span] {
|
|
112
|
-
grid-column: span 2;
|
|
139
|
+
flex: 1;
|
|
113
140
|
}
|
|
114
141
|
|
|
115
142
|
label {
|
|
@@ -180,60 +207,58 @@ let ApplicationEditor = class ApplicationEditor extends localize(i18next)(LitEle
|
|
|
180
207
|
<form @submit=${(e) => e.preventDefault()}>
|
|
181
208
|
<fieldset>
|
|
182
209
|
<legend>${i18next.t('text.application')}</legend>
|
|
183
|
-
<div field
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
${this.missing.includes('name')
|
|
210
|
+
<div field>
|
|
211
|
+
<label for="name">${i18next.t('field.name')}</label>
|
|
212
|
+
<input
|
|
213
|
+
id="name"
|
|
214
|
+
type="text"
|
|
215
|
+
name="name"
|
|
216
|
+
.value=${app.name || ''}
|
|
217
|
+
required
|
|
218
|
+
autofocus
|
|
219
|
+
?invalid=${this.missing.includes('name')}
|
|
220
|
+
/>
|
|
221
|
+
${this.missing.includes('name')
|
|
196
222
|
? html `<div field-error>${i18next.t('text.this field is required')}</div>`
|
|
197
223
|
: ''}
|
|
198
|
-
|
|
224
|
+
</div>
|
|
199
225
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
226
|
+
<div field>
|
|
227
|
+
<label for="description">${i18next.t('field.description')}</label>
|
|
228
|
+
<input id="description" type="text" name="description" .value=${app.description || ''} />
|
|
229
|
+
</div>
|
|
204
230
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
231
|
+
<!-- type=email so the browser checks the shape the server's scalar will check. -->
|
|
232
|
+
<div field>
|
|
233
|
+
<label for="email">${i18next.t('field.contact email')}</label>
|
|
234
|
+
<input id="email" type="email" name="email" .value=${app.email || ''} />
|
|
235
|
+
</div>
|
|
210
236
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
237
|
+
<div field>
|
|
238
|
+
<label for="type">${i18next.t('field.type')}</label>
|
|
239
|
+
<select id="type" name="type">
|
|
240
|
+
${APPLICATION_TYPES.map(type => html `<option value=${type} ?selected=${type === (app.type || DEFAULT_TYPE)}>${type}</option>`)}
|
|
241
|
+
</select>
|
|
242
|
+
</div>
|
|
217
243
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
244
|
+
<div field>
|
|
245
|
+
<label for="url">${i18next.t('field.application url')}</label>
|
|
246
|
+
<input id="url" type="url" name="url" .value=${app.url || ''} />
|
|
247
|
+
</div>
|
|
222
248
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
249
|
+
<div field>
|
|
250
|
+
<label for="redirect-url">${i18next.t('field.redirect url')}</label>
|
|
251
|
+
<input id="redirect-url" type="url" name="redirectUrl" .value=${app.redirectUrl || ''} />
|
|
252
|
+
</div>
|
|
227
253
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
254
|
+
<div field>
|
|
255
|
+
<label for="icon">${i18next.t('field.application icon')}</label>
|
|
256
|
+
<input id="icon" type="url" name="icon" .value=${app.icon || ''} />
|
|
257
|
+
</div>
|
|
232
258
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
</div>
|
|
259
|
+
<div field>
|
|
260
|
+
<label for="webhook">${i18next.t('field.webhook')}</label>
|
|
261
|
+
<input id="webhook" type="url" name="webhook" .value=${app.webhook || ''} />
|
|
237
262
|
</div>
|
|
238
263
|
</fieldset>
|
|
239
264
|
|
|
@@ -265,23 +290,21 @@ let ApplicationEditor = class ApplicationEditor extends localize(i18next)(LitEle
|
|
|
265
290
|
return html `
|
|
266
291
|
<fieldset>
|
|
267
292
|
<legend>${i18next.t('text.endpoints')}</legend>
|
|
268
|
-
<div field
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
</div>
|
|
293
|
+
<div field>
|
|
294
|
+
<label for="auth-url">${i18next.t('field.auth url')}</label>
|
|
295
|
+
<input id="auth-url" type="text" .value=${app.authUrl || ''} readonly />
|
|
296
|
+
<div class="note">${i18next.t('text.where an integration asks for an authorization code')}</div>
|
|
297
|
+
</div>
|
|
274
298
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
299
|
+
<div field>
|
|
300
|
+
<label for="access-token-url">${i18next.t('field.access token url')}</label>
|
|
301
|
+
<input id="access-token-url" type="text" .value=${app.accessTokenUrl || ''} readonly />
|
|
302
|
+
<div class="note">${i18next.t('text.where that code is exchanged for an access token')}</div>
|
|
303
|
+
</div>
|
|
280
304
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
</div>
|
|
305
|
+
<div field>
|
|
306
|
+
<label for="available-scopes">${i18next.t('field.available scopes')}</label>
|
|
307
|
+
<input id="available-scopes" type="text" .value=${app.availableScopes || ''} readonly />
|
|
285
308
|
</div>
|
|
286
309
|
</fieldset>
|
|
287
310
|
`;
|
|
@@ -295,20 +318,18 @@ let ApplicationEditor = class ApplicationEditor extends localize(i18next)(LitEle
|
|
|
295
318
|
return html `
|
|
296
319
|
<fieldset>
|
|
297
320
|
<legend>${i18next.t('text.credentials')}</legend>
|
|
298
|
-
<div field
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
</div>
|
|
321
|
+
<div field>
|
|
322
|
+
<label for="app-key">${i18next.t('field.app key')}</label>
|
|
323
|
+
<input id="app-key" type="text" .value=${app.appKey || ''} readonly />
|
|
324
|
+
</div>
|
|
303
325
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
</div>
|
|
326
|
+
<div field>
|
|
327
|
+
<label for="app-secret">${i18next.t('field.client-secret')}</label>
|
|
328
|
+
<secret-field id="app-secret" .value=${app.appSecret || ''}></secret-field>
|
|
329
|
+
<div class="row">
|
|
330
|
+
<md-text-button @click=${this.generateSecret.bind(this)}>
|
|
331
|
+
${i18next.t('button.generate new secret')}
|
|
332
|
+
</md-text-button>
|
|
312
333
|
</div>
|
|
313
334
|
</div>
|
|
314
335
|
</fieldset>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application-editor.js","sourceRoot":"","sources":["../../client/components/application-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/button/filled-button.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { CommonHeaderStyles, CommonPopupStyles } from '@operato/styles'\n\nimport './secret-field.js'\nimport { APPLICATION_TYPES } from '../constants/application.js'\nimport { changedValues, filledValues, missingRequired } from '../utils/form-values.js'\n\n/**\n * Registering and editing an application, in a popup.\n *\n * ── Why this replaced two pages ─────────────────────────────────────────────\n * Same reason as `appliance-editor`: `application-register` and `application/{id}` were the\n * only routes of their kind left in a package where every other resource is a list with a\n * popup over it, and nothing outside auth-ui linked to either.\n *\n * ── Three defects the old detail page carried ───────────────────────────────\n * 1. **The type could not be changed.** Every option in the dropdown was written as\n * `<option value=\"${app.type}\">`, so all six options submitted the *current* value. The\n * label differed, the value did not.\n * 2. **`generate new secret` did nothing.** The button had no handler, while\n * `generateApplicationSecret` had been on the server the whole time.\n * 3. **Saving with an empty contact email was refused.** The page sent every box, so an\n * untouched `email` went as `''`, and `ApplicationPatch.email` is a GraphQLEmailAddress —\n * `''` is not \"no email\" to it, it is an invalid one. The screen printed `update fail` to\n * the console.\n *\n * The type list itself had drifted too: the client constant named five types, the server enum\n * declares six. `application-type-parity.test.ts` now fails when they part again.\n */\n\n/** The one field `NewApplication` declares non-null. The schema's list, not a house rule. */\nconst REQUIRED = ['name']\n\n/*\n * What a new application is when nobody chooses. A `<select>` always has a value, so leaving\n * this out would not mean \"unset\" — it would mean whichever option happens to be listed\n * first. `Application.type` declares `default: ApplicationType.OTHERS`, so that is the one to\n * preselect; anything else would quietly disagree with the server about untouched records.\n */\nconst DEFAULT_TYPE = 'OTHERS'\n\nconst APPLICATION_FIELDS = `\n id\n name\n description\n email\n url\n icon\n redirectUrl\n authUrl\n accessTokenUrl\n webhook\n availableScopes\n appKey\n appSecret\n type\n`\n\n@customElement('application-editor')\nexport class ApplicationEditor extends localize(i18next)(LitElement) {\n static styles = [\n CommonHeaderStyles,\n CommonPopupStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n background-color: var(--md-sys-color-surface);\n overflow: auto;\n }\n\n form {\n flex: 1;\n padding: var(--popup-padding);\n }\n\n fieldset {\n border-radius: var(--border-radius);\n border: var(--border-dim-color);\n margin: 0 0 var(--popup-gap) 0;\n padding: var(--popup-padding);\n }\n\n legend {\n padding: var(--legend-padding);\n font-weight: bold;\n color: var(--legend-color);\n }\n\n [field-2column] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-gap: var(--popup-gap);\n }\n\n [field] {\n display: flex;\n flex-direction: column;\n }\n\n [grid-span] {\n grid-column: span 2;\n }\n\n label {\n font: var(--label-font);\n color: var(--label-color, var(--md-sys-color-on-surface));\n text-transform: var(--label-text-transform);\n }\n\n input,\n select {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n border: var(--border-dim-color);\n border-radius: var(--border-radius);\n margin: var(--input-margin);\n padding: var(--input-padding);\n font: var(--input-font);\n }\n\n input:focus,\n select:focus {\n outline: none;\n }\n\n input[readonly] {\n opacity: 0.7;\n }\n\n input[invalid] {\n border-color: var(--md-sys-color-error);\n }\n\n [field-error] {\n color: var(--md-sys-color-error);\n font-size: var(--popup-font-size-small);\n margin-top: var(--spacing-tiny, 2px);\n }\n\n [field] .note {\n margin-top: var(--spacing-tiny, 2px);\n }\n\n .buttons .left {\n margin-right: auto;\n }\n\n @media screen and (max-width: 480px) {\n [field] {\n grid-column: span 2;\n }\n }\n `\n ]\n\n /** The application to edit. Absent means this popup is registering a new one. */\n @property({ type: String }) applicationId?: string\n\n @query('form') form!: HTMLFormElement\n\n @state() private application: any = null\n\n @state() private missing: string[] = []\n\n @state() private failed: boolean = false\n\n @state() private loadFailed: boolean = false\n\n private get editing(): boolean {\n return !!this.applicationId\n }\n\n /*\n * ⚠ The type dropdown: each option carries its OWN value. The page this replaced wrote the\n * current type as the value of every option, so all six submitted the same thing and the\n * type could not be changed from this screen at all.\n */\n render() {\n const app = this.application || {}\n\n if (this.editing && this.loadFailed) {\n return html`<div class=\"failure\">${i18next.t('text.the application could not be loaded')}</div>`\n }\n\n return html`\n <form @submit=${(e: Event) => e.preventDefault()}>\n <fieldset>\n <legend>${i18next.t('text.application')}</legend>\n <div field-2column>\n <div field grid-span>\n <label for=\"name\">${i18next.t('field.name')}</label>\n <input\n id=\"name\"\n type=\"text\"\n name=\"name\"\n .value=${app.name || ''}\n required\n autofocus\n ?invalid=${this.missing.includes('name')}\n />\n ${\n this.missing.includes('name')\n ? html`<div field-error>${i18next.t('text.this field is required')}</div>`\n : ''\n }\n </div>\n\n <div field grid-span>\n <label for=\"description\">${i18next.t('field.description')}</label>\n <input id=\"description\" type=\"text\" name=\"description\" .value=${app.description || ''} />\n </div>\n\n <!-- type=email so the browser checks the shape the server's scalar will check. -->\n <div field grid-span>\n <label for=\"email\">${i18next.t('field.contact email')}</label>\n <input id=\"email\" type=\"email\" name=\"email\" .value=${app.email || ''} />\n </div>\n\n <div field grid-span>\n <label for=\"type\">${i18next.t('field.type')}</label>\n <select id=\"type\" name=\"type\">\n ${APPLICATION_TYPES.map(\n type => html`<option value=${type} ?selected=${type === (app.type || DEFAULT_TYPE)}>${type}</option>`\n )}\n </select>\n </div>\n\n <div field grid-span>\n <label for=\"url\">${i18next.t('field.application url')}</label>\n <input id=\"url\" type=\"url\" name=\"url\" .value=${app.url || ''} />\n </div>\n\n <div field grid-span>\n <label for=\"redirect-url\">${i18next.t('field.redirect url')}</label>\n <input id=\"redirect-url\" type=\"url\" name=\"redirectUrl\" .value=${app.redirectUrl || ''} />\n </div>\n\n <div field grid-span>\n <label for=\"icon\">${i18next.t('field.application icon')}</label>\n <input id=\"icon\" type=\"url\" name=\"icon\" .value=${app.icon || ''} />\n </div>\n\n <div field grid-span>\n <label for=\"webhook\">${i18next.t('field.webhook')}</label>\n <input id=\"webhook\" type=\"url\" name=\"webhook\" .value=${app.webhook || ''} />\n </div>\n </div>\n </fieldset>\n\n ${this.editing ? this.renderEndpoints(app) : ''} ${this.editing ? this.renderCredential(app) : ''}\n ${this.failed ? html`<div class=\"failure\">${i18next.t('text.could not be saved')}</div>` : ''}\n </form>\n\n <div class=\"buttons\">\n ${\n this.editing\n ? html`\n <md-text-button class=\"left\" @click=${this.deleteApplication.bind(this)}>\n ${i18next.t('button.delete')}\n </md-text-button>\n `\n : ''\n }\n <md-text-button @click=${this.cancel.bind(this)}>${i18next.t('button.cancel')}</md-text-button>\n <md-filled-button @click=${this.save.bind(this)}>\n ${this.editing ? i18next.t('button.update') : i18next.t('button.register')}\n </md-filled-button>\n </div>\n `\n }\n\n /*\n * What the server hands back rather than what the person types: the OAuth endpoints and the\n * scopes this application may ask for. Read-only and outside the form's named inputs, so\n * they are never part of a patch.\n */\n private renderEndpoints(app: any) {\n return html`\n <fieldset>\n <legend>${i18next.t('text.endpoints')}</legend>\n <div field-2column>\n <div field grid-span>\n <label for=\"auth-url\">${i18next.t('field.auth url')}</label>\n <input id=\"auth-url\" type=\"text\" .value=${app.authUrl || ''} readonly />\n <div class=\"note\">${i18next.t('text.where an integration asks for an authorization code')}</div>\n </div>\n\n <div field grid-span>\n <label for=\"access-token-url\">${i18next.t('field.access token url')}</label>\n <input id=\"access-token-url\" type=\"text\" .value=${app.accessTokenUrl || ''} readonly />\n <div class=\"note\">${i18next.t('text.where that code is exchanged for an access token')}</div>\n </div>\n\n <div field grid-span>\n <label for=\"available-scopes\">${i18next.t('field.available scopes')}</label>\n <input id=\"available-scopes\" type=\"text\" .value=${app.availableScopes || ''} readonly />\n </div>\n </div>\n </fieldset>\n `\n }\n\n /*\n * appKey stays a plain field: it is the client identifier, meant to be read and quoted.\n * appSecret is the other half — it carries its own @privilege for security:query, and it is\n * the value an integration signs with.\n */\n private renderCredential(app: any) {\n return html`\n <fieldset>\n <legend>${i18next.t('text.credentials')}</legend>\n <div field-2column>\n <div field grid-span>\n <label for=\"app-key\">${i18next.t('field.app key')}</label>\n <input id=\"app-key\" type=\"text\" .value=${app.appKey || ''} readonly />\n </div>\n\n <div field grid-span>\n <label for=\"app-secret\">${i18next.t('field.client-secret')}</label>\n <secret-field id=\"app-secret\" .value=${app.appSecret || ''}></secret-field>\n <div class=\"row\">\n <md-text-button @click=${this.generateSecret.bind(this)}>\n ${i18next.t('button.generate new secret')}\n </md-text-button>\n </div>\n </div>\n </div>\n </fieldset>\n `\n }\n\n async connectedCallback() {\n super.connectedCallback()\n\n if (this.editing) {\n await this.fetchApplication()\n }\n }\n\n async fetchApplication() {\n const response = await client.query({\n query: gql`\n query ($id: String!) {\n application(id: $id) { ${APPLICATION_FIELDS} }\n }\n `,\n variables: { id: this.applicationId }\n })\n\n /*\n * A refusal arrives in `errors` rather than throwing. An unchecked read would render an\n * empty form that looks like a record with every field blank, and saving it would write\n * those blanks.\n */\n if (response.errors || !response.data?.application) {\n this.loadFailed = true\n return\n }\n\n this.loadFailed = false\n this.application = response.data.application\n }\n\n private cancel(e: Event) {\n e.preventDefault()\n this.close()\n }\n\n async save(e: Event) {\n e.preventDefault()\n\n const formData = new FormData(this.form)\n\n const missing = missingRequired(formData, REQUIRED)\n\n if (missing.length) {\n this.missing = missing\n return\n }\n\n this.missing = []\n\n return this.editing ? this.updateApplication(formData) : this.createApplication(formData)\n }\n\n private async createApplication(formData: FormData) {\n const response = await client.mutate({\n mutation: gql`\n mutation ($application: NewApplication!) {\n createApplication(application: $application) {\n id\n }\n }\n `,\n variables: { application: filledValues(formData) }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n this.saved()\n }\n\n private async updateApplication(formData: FormData) {\n const patch = changedValues(formData, this.application)\n\n if (!Object.keys(patch).length) {\n this.close()\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: ApplicationPatch!) {\n updateApplication(id: $id, patch: $patch) { ${APPLICATION_FIELDS} }\n }\n `,\n variables: { id: this.applicationId, patch }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n this.saved()\n }\n\n private async generateSecret(e: Event) {\n e.preventDefault()\n\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('button.generate new secret'),\n text: i18next.t('text.the current secret stops working immediately'),\n confirmButton: { text: i18next.t('button.confirm') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n\n if (!confirmed) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n generateApplicationSecret(id: $id) { ${APPLICATION_FIELDS} }\n }\n `,\n variables: { id: this.applicationId }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n /*\n * Stays open on purpose — a fresh secret is shown once and has to be copied before it is\n * out of reach.\n */\n this.failed = false\n this.application = response.data.generateApplicationSecret\n this.dispatchEvent(new CustomEvent('application-changed', { bubbles: true, composed: true }))\n }\n\n private async deleteApplication(e: Event) {\n e.preventDefault()\n\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('button.delete'),\n text: i18next.t('text.are you sure you want to delete x', { x: this.application?.name || '' }),\n confirmButton: { text: i18next.t('button.delete') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n\n if (!confirmed) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteApplication(id: $id)\n }\n `,\n variables: { id: this.applicationId }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n this.saved()\n }\n\n private saved() {\n this.failed = false\n this.dispatchEvent(new CustomEvent('application-changed', { bubbles: true, composed: true }))\n this.close()\n }\n\n private close() {\n this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"application-editor.js","sourceRoot":"","sources":["../../client/components/application-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/button/filled-button.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { CommonHeaderStyles, CommonPopupStyles, ScrollbarStyles } from '@operato/styles'\n\nimport './secret-field.js'\nimport { APPLICATION_TYPES } from '../constants/application.js'\nimport { changedValues, filledValues, missingRequired } from '../utils/form-values.js'\n\n/**\n * Registering and editing an application, in a popup.\n *\n * ── Why this replaced two pages ─────────────────────────────────────────────\n * Same reason as `appliance-editor`: `application-register` and `application/{id}` were the\n * only routes of their kind left in a package where every other resource is a list with a\n * popup over it, and nothing outside auth-ui linked to either.\n *\n * ── Three defects the old detail page carried ───────────────────────────────\n * 1. **The type could not be changed.** Every option in the dropdown was written as\n * `<option value=\"${app.type}\">`, so all six options submitted the *current* value. The\n * label differed, the value did not.\n * 2. **`generate new secret` did nothing.** The button had no handler, while\n * `generateApplicationSecret` had been on the server the whole time.\n * 3. **Saving with an empty contact email was refused.** The page sent every box, so an\n * untouched `email` went as `''`, and `ApplicationPatch.email` is a GraphQLEmailAddress —\n * `''` is not \"no email\" to it, it is an invalid one. The screen printed `update fail` to\n * the console.\n *\n * The type list itself had drifted too: the client constant named five types, the server enum\n * declares six. `application-type-parity.test.ts` now fails when they part again.\n */\n\n/** The one field `NewApplication` declares non-null. The schema's list, not a house rule. */\nconst REQUIRED = ['name']\n\n/*\n * What a new application is when nobody chooses. A `<select>` always has a value, so leaving\n * this out would not mean \"unset\" — it would mean whichever option happens to be listed\n * first. `Application.type` declares `default: ApplicationType.OTHERS`, so that is the one to\n * preselect; anything else would quietly disagree with the server about untouched records.\n */\nconst DEFAULT_TYPE = 'OTHERS'\n\nconst APPLICATION_FIELDS = `\n id\n name\n description\n email\n url\n icon\n redirectUrl\n authUrl\n accessTokenUrl\n webhook\n availableScopes\n appKey\n appSecret\n type\n`\n\n@customElement('application-editor')\nexport class ApplicationEditor extends localize(i18next)(LitElement) {\n /*\n * ── Where the scrolling happens ─────────────────────────────────────────\n * The button row stays put and only the middle scrolls. That is the house shape for a\n * popup, and this had it wrong: the host itself scrolled, so the row you press to finish\n * walked off the bottom of the card along with everything else.\n *\n * So the host clips, and the content area between the title and the buttons is the one\n * thing with overflow. Two details carry it:\n *\n * min-height: 0 A flex item defaults to min-height auto, which refuses to shrink below\n * its own content. Without this the overflow rule sits there doing\n * nothing and the card grows instead — it reads as a working rule.\n * no padding CommonPopupStyles already pads the host. Padding here again doubles the\n * inset and pushes the scrollbar in off the card edge.\n *\n * ScrollbarStyles gives the scrollbar the product's own shape rather than the browser's.\n *\n * ── Why the in-content buttons need a rule of their own ─────────────────\n * CommonPopupStyles makes controls pill-shaped because a popup is a rounded card and the\n * things inside it follow the card. But it says that through `.buttons`, so only the commit\n * row got it: the actions sitting inside a fieldset — generate a new token, generate a new\n * secret — kept the page radius and read as a different kind of button on the same card.\n * Measured on a live popup: 5px inside the content, 13986px in the row below it.\n */\n static styles = [\n CommonHeaderStyles,\n CommonPopupStyles,\n ScrollbarStyles,\n css`\n /* in-content actions follow the card too, not just the commit row. see the note on styles. */\n fieldset md-text-button,\n fieldset md-filled-button {\n --md-text-button-container-shape: var(--popup-control-radius);\n --md-filled-button-container-shape: var(--popup-control-radius);\n }\n\n /* the host does not scroll; the content area below does. see the note on styles. */\n :host {\n overflow: hidden;\n }\n\n /* min-height:0 is load-bearing — see the note on styles. */\n form {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n\n display: flex;\n flex-direction: column;\n gap: var(--popup-gap);\n }\n\n fieldset {\n border-radius: var(--border-radius);\n border: var(--border-dim-color);\n margin: 0;\n padding: var(--popup-padding);\n }\n\n legend {\n padding: var(--legend-padding);\n font-weight: bold;\n color: var(--legend-color);\n }\n\n /* one labelled box. .row puts two of them side by side; alone it spans the card. */\n [field] {\n display: flex;\n flex-direction: column;\n flex: 1;\n }\n\n label {\n font: var(--label-font);\n color: var(--label-color, var(--md-sys-color-on-surface));\n text-transform: var(--label-text-transform);\n }\n\n input,\n select {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n border: var(--border-dim-color);\n border-radius: var(--border-radius);\n margin: var(--input-margin);\n padding: var(--input-padding);\n font: var(--input-font);\n }\n\n input:focus,\n select:focus {\n outline: none;\n }\n\n input[readonly] {\n opacity: 0.7;\n }\n\n input[invalid] {\n border-color: var(--md-sys-color-error);\n }\n\n [field-error] {\n color: var(--md-sys-color-error);\n font-size: var(--popup-font-size-small);\n margin-top: var(--spacing-tiny, 2px);\n }\n\n [field] .note {\n margin-top: var(--spacing-tiny, 2px);\n }\n\n .buttons .left {\n margin-right: auto;\n }\n\n @media screen and (max-width: 480px) {\n [field] {\n grid-column: span 2;\n }\n }\n `\n ]\n\n /** The application to edit. Absent means this popup is registering a new one. */\n @property({ type: String }) applicationId?: string\n\n @query('form') form!: HTMLFormElement\n\n @state() private application: any = null\n\n @state() private missing: string[] = []\n\n @state() private failed: boolean = false\n\n @state() private loadFailed: boolean = false\n\n private get editing(): boolean {\n return !!this.applicationId\n }\n\n /*\n * ⚠ The type dropdown: each option carries its OWN value. The page this replaced wrote the\n * current type as the value of every option, so all six submitted the same thing and the\n * type could not be changed from this screen at all.\n */\n render() {\n const app = this.application || {}\n\n if (this.editing && this.loadFailed) {\n return html`<div class=\"failure\">${i18next.t('text.the application could not be loaded')}</div>`\n }\n\n return html`\n <form @submit=${(e: Event) => e.preventDefault()}>\n <fieldset>\n <legend>${i18next.t('text.application')}</legend>\n <div field>\n <label for=\"name\">${i18next.t('field.name')}</label>\n <input\n id=\"name\"\n type=\"text\"\n name=\"name\"\n .value=${app.name || ''}\n required\n autofocus\n ?invalid=${this.missing.includes('name')}\n />\n ${\n this.missing.includes('name')\n ? html`<div field-error>${i18next.t('text.this field is required')}</div>`\n : ''\n }\n </div>\n\n <div field>\n <label for=\"description\">${i18next.t('field.description')}</label>\n <input id=\"description\" type=\"text\" name=\"description\" .value=${app.description || ''} />\n </div>\n\n <!-- type=email so the browser checks the shape the server's scalar will check. -->\n <div field>\n <label for=\"email\">${i18next.t('field.contact email')}</label>\n <input id=\"email\" type=\"email\" name=\"email\" .value=${app.email || ''} />\n </div>\n\n <div field>\n <label for=\"type\">${i18next.t('field.type')}</label>\n <select id=\"type\" name=\"type\">\n ${APPLICATION_TYPES.map(\n type => html`<option value=${type} ?selected=${type === (app.type || DEFAULT_TYPE)}>${type}</option>`\n )}\n </select>\n </div>\n\n <div field>\n <label for=\"url\">${i18next.t('field.application url')}</label>\n <input id=\"url\" type=\"url\" name=\"url\" .value=${app.url || ''} />\n </div>\n\n <div field>\n <label for=\"redirect-url\">${i18next.t('field.redirect url')}</label>\n <input id=\"redirect-url\" type=\"url\" name=\"redirectUrl\" .value=${app.redirectUrl || ''} />\n </div>\n\n <div field>\n <label for=\"icon\">${i18next.t('field.application icon')}</label>\n <input id=\"icon\" type=\"url\" name=\"icon\" .value=${app.icon || ''} />\n </div>\n\n <div field>\n <label for=\"webhook\">${i18next.t('field.webhook')}</label>\n <input id=\"webhook\" type=\"url\" name=\"webhook\" .value=${app.webhook || ''} />\n </div>\n </fieldset>\n\n ${this.editing ? this.renderEndpoints(app) : ''} ${this.editing ? this.renderCredential(app) : ''}\n ${this.failed ? html`<div class=\"failure\">${i18next.t('text.could not be saved')}</div>` : ''}\n </form>\n\n <div class=\"buttons\">\n ${\n this.editing\n ? html`\n <md-text-button class=\"left\" @click=${this.deleteApplication.bind(this)}>\n ${i18next.t('button.delete')}\n </md-text-button>\n `\n : ''\n }\n <md-text-button @click=${this.cancel.bind(this)}>${i18next.t('button.cancel')}</md-text-button>\n <md-filled-button @click=${this.save.bind(this)}>\n ${this.editing ? i18next.t('button.update') : i18next.t('button.register')}\n </md-filled-button>\n </div>\n `\n }\n\n /*\n * What the server hands back rather than what the person types: the OAuth endpoints and the\n * scopes this application may ask for. Read-only and outside the form's named inputs, so\n * they are never part of a patch.\n */\n private renderEndpoints(app: any) {\n return html`\n <fieldset>\n <legend>${i18next.t('text.endpoints')}</legend>\n <div field>\n <label for=\"auth-url\">${i18next.t('field.auth url')}</label>\n <input id=\"auth-url\" type=\"text\" .value=${app.authUrl || ''} readonly />\n <div class=\"note\">${i18next.t('text.where an integration asks for an authorization code')}</div>\n </div>\n\n <div field>\n <label for=\"access-token-url\">${i18next.t('field.access token url')}</label>\n <input id=\"access-token-url\" type=\"text\" .value=${app.accessTokenUrl || ''} readonly />\n <div class=\"note\">${i18next.t('text.where that code is exchanged for an access token')}</div>\n </div>\n\n <div field>\n <label for=\"available-scopes\">${i18next.t('field.available scopes')}</label>\n <input id=\"available-scopes\" type=\"text\" .value=${app.availableScopes || ''} readonly />\n </div>\n </fieldset>\n `\n }\n\n /*\n * appKey stays a plain field: it is the client identifier, meant to be read and quoted.\n * appSecret is the other half — it carries its own @privilege for security:query, and it is\n * the value an integration signs with.\n */\n private renderCredential(app: any) {\n return html`\n <fieldset>\n <legend>${i18next.t('text.credentials')}</legend>\n <div field>\n <label for=\"app-key\">${i18next.t('field.app key')}</label>\n <input id=\"app-key\" type=\"text\" .value=${app.appKey || ''} readonly />\n </div>\n\n <div field>\n <label for=\"app-secret\">${i18next.t('field.client-secret')}</label>\n <secret-field id=\"app-secret\" .value=${app.appSecret || ''}></secret-field>\n <div class=\"row\">\n <md-text-button @click=${this.generateSecret.bind(this)}>\n ${i18next.t('button.generate new secret')}\n </md-text-button>\n </div>\n </div>\n </fieldset>\n `\n }\n\n async connectedCallback() {\n super.connectedCallback()\n\n if (this.editing) {\n await this.fetchApplication()\n }\n }\n\n async fetchApplication() {\n const response = await client.query({\n query: gql`\n query ($id: String!) {\n application(id: $id) { ${APPLICATION_FIELDS} }\n }\n `,\n variables: { id: this.applicationId }\n })\n\n /*\n * A refusal arrives in `errors` rather than throwing. An unchecked read would render an\n * empty form that looks like a record with every field blank, and saving it would write\n * those blanks.\n */\n if (response.errors || !response.data?.application) {\n this.loadFailed = true\n return\n }\n\n this.loadFailed = false\n this.application = response.data.application\n }\n\n private cancel(e: Event) {\n e.preventDefault()\n this.close()\n }\n\n async save(e: Event) {\n e.preventDefault()\n\n const formData = new FormData(this.form)\n\n const missing = missingRequired(formData, REQUIRED)\n\n if (missing.length) {\n this.missing = missing\n return\n }\n\n this.missing = []\n\n return this.editing ? this.updateApplication(formData) : this.createApplication(formData)\n }\n\n private async createApplication(formData: FormData) {\n const response = await client.mutate({\n mutation: gql`\n mutation ($application: NewApplication!) {\n createApplication(application: $application) {\n id\n }\n }\n `,\n variables: { application: filledValues(formData) }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n this.saved()\n }\n\n private async updateApplication(formData: FormData) {\n const patch = changedValues(formData, this.application)\n\n if (!Object.keys(patch).length) {\n this.close()\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: ApplicationPatch!) {\n updateApplication(id: $id, patch: $patch) { ${APPLICATION_FIELDS} }\n }\n `,\n variables: { id: this.applicationId, patch }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n this.saved()\n }\n\n private async generateSecret(e: Event) {\n e.preventDefault()\n\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('button.generate new secret'),\n text: i18next.t('text.the current secret stops working immediately'),\n confirmButton: { text: i18next.t('button.confirm') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n\n if (!confirmed) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n generateApplicationSecret(id: $id) { ${APPLICATION_FIELDS} }\n }\n `,\n variables: { id: this.applicationId }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n /*\n * Stays open on purpose — a fresh secret is shown once and has to be copied before it is\n * out of reach.\n */\n this.failed = false\n this.application = response.data.generateApplicationSecret\n this.dispatchEvent(new CustomEvent('application-changed', { bubbles: true, composed: true }))\n }\n\n private async deleteApplication(e: Event) {\n e.preventDefault()\n\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('button.delete'),\n text: i18next.t('text.are you sure you want to delete x', { x: this.application?.name || '' }),\n confirmButton: { text: i18next.t('button.delete') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n\n if (!confirmed) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteApplication(id: $id)\n }\n `,\n variables: { id: this.applicationId }\n })\n\n if (response.errors) {\n this.failed = true\n return\n }\n\n this.saved()\n }\n\n private saved() {\n this.failed = false\n this.dispatchEvent(new CustomEvent('application-changed', { bubbles: true, composed: true }))\n this.close()\n }\n\n private close() {\n this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }))\n }\n}\n"]}
|
|
@@ -6,9 +6,10 @@ import { customElement, query } from 'lit/decorators.js';
|
|
|
6
6
|
import { client } from '@operato/graphql';
|
|
7
7
|
import { i18next, localize } from '@operato/i18n';
|
|
8
8
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
9
|
-
import { CommonHeaderStyles } from '@operato/styles';
|
|
9
|
+
import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles';
|
|
10
10
|
let CreateDomainPopup = class CreateDomainPopup extends localize(i18next)(LitElement) {
|
|
11
11
|
static { this.styles = [
|
|
12
|
+
ScrollbarStyles,
|
|
12
13
|
CommonHeaderStyles,
|
|
13
14
|
css `
|
|
14
15
|
:host {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-domain-popup.js","sourceRoot":"","sources":["../../client/components/create-domain-popup.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AAEnC,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAExD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"create-domain-popup.js","sourceRoot":"","sources":["../../client/components/create-domain-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, query } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'\n\n@customElement('create-domain-popup')\nclass CreateDomainPopup extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n CommonHeaderStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n background-color: var(--md-sys-color-background);\n overflow: auto;\n }\n\n form {\n flex: 1;\n padding: var(--spacing-large);\n }\n\n input.checkValidName {\n background-color: #fce6e6;\n }\n\n label {\n display: flex;\n flex-direction: column;\n\n font: var(--label-font);\n color: var(--label-color, var(--md-sys-color-on-surface));\n text-transform: var(--label-text-transform);\n }\n\n input {\n border: var(--border-dim-color);\n border-radius: var(--border-radius);\n margin: var(--input-margin);\n padding: var(--input-padding);\n background-color: var(--md-sys-color-surface);\n font: var(--input-font);\n }\n\n [field] {\n grid-column: span 2;\n }\n `\n ]\n\n @query('input[name=\"name\"]') nameInput!: HTMLInputElement\n\n render() {\n return html`\n <form>\n <div field grid-span>\n <label\n >${i18next.t('label.x name', { x: i18next.t('label.domain') })}<input\n type=\"text\"\n name=\"name\"\n @input=${this.checkValidation}\n autofocus\n /></label>\n </div>\n\n <div field grid-span>\n <label>${i18next.t('label.description')}<input type=\"text\" name=\"description\" /></label>\n </div>\n </form>\n\n <div class=\"footer\">\n <div filler></div>\n <button @click=${e => this.onCreateDomain()} done><md-icon>add</md-icon>${i18next.t('button.create')}</button>\n </div>\n `\n }\n\n firstUpdated() {\n this.nameInput.focus() // autofocus\n }\n\n get inputData() {\n return this.renderRoot.querySelectorAll('input')\n }\n\n checkValidation(e) {\n const currentInput = e.currentTarget\n const regExp = /^[a-zA-Z ]+$/\n\n if (!regExp.test(currentInput.value)) {\n currentInput.classList.add('checkValidName')\n } else {\n currentInput.classList.remove('checkValidName')\n }\n }\n\n async onCreateDomain() {\n const domainInput: { [prop: string]: string } = {}\n this.inputData.forEach(data => (domainInput[data.name] = data.value))\n const regExp = /^[a-zA-z0-9- ]+$/\n\n if (!regExp.test(domainInput.name)) {\n return this.showToast(i18next.t('error: domain name should consist only of letters or dashes'))\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation domainRegister($domainInput: DomainGeneratorInput!) {\n domainRegister(domainInput: $domainInput) {\n id\n name\n }\n }\n `,\n variables: { domainInput }\n })\n\n if (!response.errors) {\n await OxPrompt.open({\n type: 'success',\n title: i18next.t('text.completed'),\n text: i18next.t('text.x_created_successfully', { x: i18next.t('label.domain') }),\n confirmButton: { text: i18next.t('button.confirm') }\n })\n\n history.back()\n\n this.dispatchEvent(new CustomEvent('fetch-data'))\n }\n }\n\n showToast(message) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message } }))\n }\n}\n"]}
|
|
@@ -6,23 +6,27 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
6
6
|
import { client } from '@operato/graphql';
|
|
7
7
|
import { i18next } from '@operato/i18n';
|
|
8
8
|
import { isMobileDevice } from '@operato/utils';
|
|
9
|
+
import { ScrollbarStyles } from '@operato/styles';
|
|
9
10
|
let MyLoginHistory = class MyLoginHistory extends LitElement {
|
|
10
11
|
constructor() {
|
|
11
12
|
super(...arguments);
|
|
12
13
|
this.histories = [];
|
|
13
14
|
}
|
|
14
|
-
static { this.styles =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
static { this.styles = [
|
|
16
|
+
ScrollbarStyles,
|
|
17
|
+
css `
|
|
18
|
+
:host {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
background-color: var(--md-sys-color-background);
|
|
22
|
+
padding: var(--spacing-large);
|
|
23
|
+
overflow: auto;
|
|
24
|
+
}
|
|
25
|
+
ox-grist {
|
|
26
|
+
flex: 1;
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
]; }
|
|
26
30
|
render() {
|
|
27
31
|
if (!this.histories?.length)
|
|
28
32
|
return html ``;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"my-login-history.js","sourceRoot":"","sources":["../../client/components/my-login-history.ts"],"names":[],"mappings":";AAAA,OAAO,qBAAqB,CAAA;AAE5B,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"my-login-history.js","sourceRoot":"","sources":["../../client/components/my-login-history.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport { isMobileDevice } from '@operato/utils'\nimport { ScrollbarStyles } from '@operato/styles'\n\n@customElement('my-login-history')\nclass MyLoginHistory extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n background-color: var(--md-sys-color-background);\n padding: var(--spacing-large);\n overflow: auto;\n }\n ox-grist {\n flex: 1;\n }\n `\n ]\n\n @property({ type: Array }) histories: any[] = []\n @property({ type: Number }) limit?: number\n\n render() {\n if (!this.histories?.length) return html``\n\n const config = {\n rows: { appendable: false },\n pagination: { infinite: true },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'object',\n name: 'accessDomain',\n header: i18next.t('field.domain'),\n record: { editable: false },\n width: 200\n },\n {\n type: 'datetime',\n name: 'accessedAt',\n header: i18next.t('field.accessed-at'),\n record: { editable: false },\n width: 200\n },\n {\n type: 'string',\n name: 'accessorIp',\n header: i18next.t('field.ip_address'),\n record: { editable: false },\n width: 200\n }\n ]\n }\n\n return html`\n <ox-grist\n .mode=${isMobileDevice() ? 'LIST' : 'GRID'}\n .config=${config}\n .data=\"${{ records: this.histories }}\"\n ></ox-grist>\n `\n }\n\n firstUpdated() {\n this.fetchLoginHistories()\n }\n\n async fetchLoginHistories() {\n try {\n const response = await client.query({\n query: gql`\n query myLoginHistories($limit: Float!) {\n myLoginHistories(limit: $limit) {\n accessDomain {\n name\n }\n accessorIp\n accessedAt\n }\n }\n `,\n variables: { limit: this.limit || 10 }\n })\n\n if (response.errors?.length) return\n this.histories = response.data.myLoginHistories\n } catch (e: any) {\n this.showToast('message' in e ? e.message : e)\n }\n }\n\n showToast(message) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message } }))\n }\n}\n"]}
|
|
@@ -6,8 +6,10 @@ import { customElement, property, query } from 'lit/decorators.js';
|
|
|
6
6
|
import { client, gqlContext } from '@operato/graphql';
|
|
7
7
|
import { i18next, localize } from '@operato/i18n';
|
|
8
8
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
9
|
+
import { ScrollbarStyles } from '@operato/styles';
|
|
9
10
|
let OwnershipTransferPopup = class OwnershipTransferPopup extends localize(i18next)(LitElement) {
|
|
10
11
|
static { this.styles = [
|
|
12
|
+
ScrollbarStyles,
|
|
11
13
|
css `
|
|
12
14
|
:host {
|
|
13
15
|
display: flex;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ownership-transfer-popup.js","sourceRoot":"","sources":["../../client/components/ownership-transfer-popup.ts"],"names":[],"mappings":";AAAA,OAAO,yCAAyC,CAAA;AAEhD,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"ownership-transfer-popup.js","sourceRoot":"","sources":["../../client/components/ownership-transfer-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/elevated-button.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { ScrollbarStyles } from '@operato/styles'\n\n@customElement('ownership-transfer-popup')\nclass OwnershipTransferPopup extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n background-color: var(--md-sys-color-background);\n padding: var(--spacing-large);\n overflow: auto;\n }\n .container {\n display: flex;\n flex-direction: column;\n flex: 1;\n }\n input {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n border: var(--border-dim-color);\n border-radius: var(--border-radius);\n margin: var(--input-margin);\n padding: var(--input-padding);\n min-width: 250px;\n font: var(--input-font);\n }\n .input-container {\n margin: auto;\n display: flex;\n }\n .input-container md-elevated-button {\n margin: auto 0px auto var(--spacing-large);\n }\n `\n ]\n\n @property({ type: Object }) user: any\n\n @query('input[name=email]') emailInput!: HTMLInputElement\n\n render() {\n return html`\n <div class=\"container\">\n <div>${i18next.t('text.please enter the email of the user you want to transfer owner')}</div>\n <div class=\"input-container\">\n <input name=\"email\" />\n <md-elevated-button @click=${this.transferOwnership}>${i18next.t('button.confirm')}</md-elevated-button>\n </div>\n </div>\n `\n }\n\n async transferOwnership() {\n if (!this.doubleCheckEmail()) {\n this.showToast(i18next.t('text.email is not matched'))\n\n return\n }\n\n if (\n await OxPrompt.open({\n title: i18next.t('text.are_you_sure'),\n text: i18next.t('text.are_you_sure_to_transfer_owner'),\n confirmButton: { text: i18next.t('button.confirm') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n ) {\n const response = await client.mutate({\n mutation: gql`\n mutation transferOwner($username: String!) {\n transferOwner(username: $username)\n }\n `,\n variables: { username: this.user.username || this.user.email },\n context: gqlContext()\n })\n\n if (!response.errors?.length) {\n await OxPrompt.open({\n type: 'success',\n title: i18next.t('text.completed'),\n text: i18next.t('text.owner_transfer_completed'),\n confirmButton: { text: i18next.t('button.confirm') }\n })\n\n history.back()\n\n this.dispatchEvent(new CustomEvent('ownershipTransferred', { bubbles: true, composed: true }))\n }\n } else {\n history.back()\n }\n }\n\n doubleCheckEmail() {\n return this.emailInput.value === this.user.email\n }\n\n showToast(message) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1000 } } }))\n }\n}\n"]}
|
|
@@ -12,7 +12,7 @@ import { i18next, localize } from '@operato/i18n';
|
|
|
12
12
|
import { openPopup } from '@operato/layout';
|
|
13
13
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
14
14
|
import { InheritedValueType, store } from '@operato/shell';
|
|
15
|
-
import { ButtonContainerStyles } from '@operato/styles';
|
|
15
|
+
import { ButtonContainerStyles, ScrollbarStyles } from '@operato/styles';
|
|
16
16
|
import { hasPrivilege } from '@things-factory/auth-base/dist-client';
|
|
17
17
|
/**
|
|
18
18
|
* 한 사람의 역할을 고치는 자리.
|
|
@@ -79,6 +79,7 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
79
79
|
this.domainRoleIds = new Set();
|
|
80
80
|
}
|
|
81
81
|
static { this.styles = [
|
|
82
|
+
ScrollbarStyles,
|
|
82
83
|
ButtonContainerStyles,
|
|
83
84
|
css `
|
|
84
85
|
:host {
|
|
@@ -248,18 +249,18 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
248
249
|
<div class="button-container">
|
|
249
250
|
${resettable
|
|
250
251
|
? html `<md-outlined-button @click=${() => this.onResetPassword()}>
|
|
251
|
-
|
|
252
|
-
|
|
252
|
+
${i18next.t('title.reset password')}
|
|
253
|
+
</md-outlined-button>`
|
|
253
254
|
: nothing}
|
|
254
255
|
${transferable
|
|
255
256
|
? html `<md-outlined-button @click=${() => this.onTransfer()}>
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
${i18next.t('button.transfer owner')}
|
|
258
|
+
</md-outlined-button>`
|
|
258
259
|
: nothing}
|
|
259
260
|
${removable
|
|
260
261
|
? html `<md-outlined-button @click=${() => this.onRemoveFromDomain()}>
|
|
261
|
-
|
|
262
|
-
|
|
262
|
+
${i18next.t('button.remove from domain')}
|
|
263
|
+
</md-outlined-button>`
|
|
263
264
|
: nothing}
|
|
264
265
|
</div>
|
|
265
266
|
</div>
|