@things-factory/auth-ui 10.1.111 → 10.1.113

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.
@@ -85,7 +85,7 @@ export default async function bootstrap() {
85
85
  .category=${detail.category}
86
86
  .privilege=${detail.privilege}
87
87
  .operationName=${detail.operationName}
88
- ></access-request-popup>`, { size: 'small', title: i18next.t('access-request.title.request access') }))
88
+ ></access-request-popup>`, { size: 'medium', title: i18next.t('access-request.title.request access') }))
89
89
  };
90
90
  });
91
91
  auth.on('profile', ({ credential }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../client/bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/i18n/ox-i18n.js'\nimport { i18next } from '@operato/i18n'\nimport '@operato/attribute/grist-editor' /* for register data-grist editor type 'attributes' */\n\nimport { html } from 'lit-html'\n\nimport { navigate, removeDraftsOf, store } from '@operato/shell'\nimport { openPopup, registerNotifyAction, TOOL_POSITION } from '@operato/layout'\nimport { OxGristEditorPrivilege } from '@operato/app/grist-editor/ox-grist-editor-privilege.js'\nimport {\n registerEditor as registerGristEditor,\n registerRenderer as registerGristRenderer,\n OxGristRendererJson5\n} from '@operato/data-grist'\n\nimport { auth } from '@things-factory/auth-base/dist-client/auth.js'\nimport { ADD_MORENDA } from '@things-factory/more-base/client'\nimport { registerPersonKind } from './entity360/person-360.js'\nimport { requestableFrom } from './utils/access-request.js'\n\nexport default async function bootstrap() {\n /* the person 360 - a User value anywhere opens it (ADR-0067) */\n registerPersonKind()\n\n registerGristEditor('privilege', OxGristEditorPrivilege)\n registerGristRenderer('privilege', OxGristRendererJson5)\n\n /* add user profile morenda */\n store.dispatch({\n type: ADD_MORENDA,\n morenda: {\n icon: html` <md-icon>account_circle</md-icon> `,\n name: html` <ox-i18n msgid=\"text.auth profile\"></ox-i18n> `,\n position: TOOL_POSITION.FRONT_END,\n action: () => {\n navigate('profile')\n }\n }\n })\n\n /* add sign-out morenda */\n store.dispatch({\n type: ADD_MORENDA,\n morenda: {\n icon: html` <md-icon>exit_to_app</md-icon> `,\n name: html` <ox-i18n msgid=\"field.sign out\"></ox-i18n> `,\n position: TOOL_POSITION.FRONT_END,\n action: () => {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem('i18nextLng')\n localStorage.removeItem('i18next')\n }\n\n ;['i18next', 'i18nextLng'].forEach(name => {\n try {\n document.cookie = `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT;`\n } catch (e) {\n // ignore\n }\n })\n } catch (e) {\n // ignore\n }\n\n auth.signout()\n }\n }\n })\n\n /*\n * Unsaved drafts stay in this browser (ADR-0098). Signing out takes this user's away, so a shared\n * computer keeps none of their edits. Awaited before the page leaves; a browser that keeps no drafts\n * has none to take.\n */\n auth.on('presignout', async () => {\n const user = (auth.credential as any)?.id\n if (user) await removeDraftsOf(user).catch(() => {})\n })\n\n /*\n * A refusal a role would open, of a change the person made, gets one quiet \"request access…\" on its toast. The toast\n * does not know what an error means and @operato/graphql only reports it; this package knows, so it puts the button\n * on (registerNotifyAction, things-factory ADR-0103 decision 5). Where auth-ui is not loaded there is no button.\n */\n registerNotifyAction('access-request', notification => {\n const refusal = Array.isArray(notification.ex) ? notification.ex[0] : undefined\n const detail = requestableFrom(refusal?.extensions, Array.isArray(refusal?.path) ? String(refusal.path[0]) : undefined)\n if (!detail) return null\n return {\n label: i18next.t('access-request.button.request'),\n emphasis: 'quiet',\n callback: () =>\n import('./components/access-request-popup.js').then(() =>\n openPopup(\n html`<access-request-popup\n .category=${detail.category}\n .privilege=${detail.privilege}\n .operationName=${detail.operationName}\n ></access-request-popup>`,\n { size: 'small', title: i18next.t('access-request.title.request access') }\n )\n )\n }\n })\n\n auth.on('profile', ({ credential }) => {\n if (credential && credential.locale) {\n i18next.changeLanguage(credential.locale)\n }\n })\n\n /* \n Get user profile information from server. \n As soon as response received, auth.on('profile', ...) handlers start to work.\n It's very important point to build UI for the user.\n */\n await auth.profile()\n}\n"]}
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../client/bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/i18n/ox-i18n.js'\nimport { i18next } from '@operato/i18n'\nimport '@operato/attribute/grist-editor' /* for register data-grist editor type 'attributes' */\n\nimport { html } from 'lit-html'\n\nimport { navigate, removeDraftsOf, store } from '@operato/shell'\nimport { openPopup, registerNotifyAction, TOOL_POSITION } from '@operato/layout'\nimport { OxGristEditorPrivilege } from '@operato/app/grist-editor/ox-grist-editor-privilege.js'\nimport {\n registerEditor as registerGristEditor,\n registerRenderer as registerGristRenderer,\n OxGristRendererJson5\n} from '@operato/data-grist'\n\nimport { auth } from '@things-factory/auth-base/dist-client/auth.js'\nimport { ADD_MORENDA } from '@things-factory/more-base/client'\nimport { registerPersonKind } from './entity360/person-360.js'\nimport { requestableFrom } from './utils/access-request.js'\n\nexport default async function bootstrap() {\n /* the person 360 - a User value anywhere opens it (ADR-0067) */\n registerPersonKind()\n\n registerGristEditor('privilege', OxGristEditorPrivilege)\n registerGristRenderer('privilege', OxGristRendererJson5)\n\n /* add user profile morenda */\n store.dispatch({\n type: ADD_MORENDA,\n morenda: {\n icon: html` <md-icon>account_circle</md-icon> `,\n name: html` <ox-i18n msgid=\"text.auth profile\"></ox-i18n> `,\n position: TOOL_POSITION.FRONT_END,\n action: () => {\n navigate('profile')\n }\n }\n })\n\n /* add sign-out morenda */\n store.dispatch({\n type: ADD_MORENDA,\n morenda: {\n icon: html` <md-icon>exit_to_app</md-icon> `,\n name: html` <ox-i18n msgid=\"field.sign out\"></ox-i18n> `,\n position: TOOL_POSITION.FRONT_END,\n action: () => {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem('i18nextLng')\n localStorage.removeItem('i18next')\n }\n\n ;['i18next', 'i18nextLng'].forEach(name => {\n try {\n document.cookie = `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT;`\n } catch (e) {\n // ignore\n }\n })\n } catch (e) {\n // ignore\n }\n\n auth.signout()\n }\n }\n })\n\n /*\n * Unsaved drafts stay in this browser (ADR-0098). Signing out takes this user's away, so a shared\n * computer keeps none of their edits. Awaited before the page leaves; a browser that keeps no drafts\n * has none to take.\n */\n auth.on('presignout', async () => {\n const user = (auth.credential as any)?.id\n if (user) await removeDraftsOf(user).catch(() => {})\n })\n\n /*\n * A refusal a role would open, of a change the person made, gets one quiet \"request access…\" on its toast. The toast\n * does not know what an error means and @operato/graphql only reports it; this package knows, so it puts the button\n * on (registerNotifyAction, things-factory ADR-0103 decision 5). Where auth-ui is not loaded there is no button.\n */\n registerNotifyAction('access-request', notification => {\n const refusal = Array.isArray(notification.ex) ? notification.ex[0] : undefined\n const detail = requestableFrom(refusal?.extensions, Array.isArray(refusal?.path) ? String(refusal.path[0]) : undefined)\n if (!detail) return null\n return {\n label: i18next.t('access-request.button.request'),\n emphasis: 'quiet',\n callback: () =>\n import('./components/access-request-popup.js').then(() =>\n openPopup(\n html`<access-request-popup\n .category=${detail.category}\n .privilege=${detail.privilege}\n .operationName=${detail.operationName}\n ></access-request-popup>`,\n { size: 'medium', title: i18next.t('access-request.title.request access') }\n )\n )\n }\n })\n\n auth.on('profile', ({ credential }) => {\n if (credential && credential.locale) {\n i18next.changeLanguage(credential.locale)\n }\n })\n\n /* \n Get user profile information from server. \n As soon as response received, auth.on('profile', ...) handlers start to work.\n It's very important point to build UI for the user.\n */\n await auth.profile()\n}\n"]}
@@ -10,7 +10,7 @@ import { customElement, property, state } from 'lit/decorators.js';
10
10
  import { client, gqlContext } from '@operato/graphql';
11
11
  import { i18next, localize } from '@operato/i18n';
12
12
  import { CommonPopupStyles, ScrollbarStyles } from '@operato/styles';
13
- import { decisionButtons, firstCandidate, issueWords, privilegeWords } from '../utils/access-request.js';
13
+ import { decisionButtons, firstCandidate, issueWords, orderedIssues, privilegeWords } from '../utils/access-request.js';
14
14
  const REVIEW = gql `
15
15
  query ($decision: AccessDecisionInput!) {
16
16
  reviewAccessDecision(decision: $decision) {
@@ -44,10 +44,12 @@ let AccessDecisionPopup = class AccessDecisionPopup extends localize(i18next)(Li
44
44
  :host {
45
45
  display: flex;
46
46
  flex-direction: column;
47
+ overflow: hidden;
47
48
  background-color: var(--md-sys-color-surface);
48
49
  }
49
50
  [body] {
50
51
  flex: 1;
52
+ min-height: 0;
51
53
  overflow: auto;
52
54
  display: flex;
53
55
  flex-direction: column;
@@ -144,7 +146,7 @@ let AccessDecisionPopup = class AccessDecisionPopup extends localize(i18next)(Li
144
146
  <span count>${t('access-request.text.privilege count', { count: candidate.privilegeCount })}</span>
145
147
  </label>
146
148
  `)}
147
- ${(this.grantIssues || []).map(issue => html `<div issue ?blocking=${issue.blocking}>${issueWords(t, issue)}</div>`)}
149
+ ${orderedIssues(this.grantIssues || []).map(issue => html `<div issue ?blocking=${issue.blocking}>${issueWords(t, issue)}</div>`)}
148
150
 
149
151
  <md-outlined-text-field
150
152
  label=${t('access-request.field.deny reason')}
@@ -1 +1 @@
1
- {"version":3,"file":"access-decision-popup.js","sourceRoot":"","sources":["../../client/components/access-decision-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/popup/ox-popup-actions.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/radio/radio.js'\nimport '@material/web/textfield/outlined-text-field.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { CommonPopupStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { decisionButtons, firstCandidate, issueWords, privilegeWords, type ReviewIssue } from '../utils/access-request.js'\n\nconst REVIEW = gql`\n query ($decision: AccessDecisionInput!) {\n reviewAccessDecision(decision: $decision) {\n code\n blocking\n field\n params\n }\n }\n`\n\n/**\n * Deciding one access request (ADR-0103 · docs/design/access-request.md §6-1).\n *\n * The findings are the server's review - the same function the AI draft card and the decision door read - so\n * this popup never judges a role itself. Each change of role or reason asks again. A blocking finding takes\n * the button away; a warning stays beside it.\n */\n@customElement('access-decision-popup')\nexport class AccessDecisionPopup extends localize(i18next)(LitElement) {\n static styles = [\n CommonPopupStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n background-color: var(--md-sys-color-surface);\n }\n [body] {\n flex: 1;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium);\n padding: var(--spacing-large);\n }\n [who],\n [why] {\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface-variant);\n }\n [needed] {\n font: var(--title-font, inherit);\n }\n label[candidate] {\n display: flex;\n align-items: center;\n gap: var(--spacing-small);\n }\n [count] {\n color: var(--md-sys-color-on-surface-variant);\n }\n [issue][blocking] {\n color: var(--md-sys-color-error);\n }\n [issue]:not([blocking]) {\n color: var(--md-sys-color-tertiary);\n }\n [none] {\n color: var(--md-sys-color-on-surface-variant);\n }\n md-outlined-text-field {\n width: 100%;\n }\n `\n ]\n\n @property({ type: Object }) request: any\n\n @state() private candidates: { id: string; name: string; privilegeCount: number }[] = []\n @state() private roleId?: string\n @state() private reason = ''\n @state() private grantIssues: ReviewIssue[] | null = null\n @state() private denyIssues: ReviewIssue[] | null = null\n\n private asked = 0\n\n async firstUpdated() {\n const response = await client.query({\n query: gql`\n query ($category: String!, $privilege: String!) {\n rolesGranting(category: $category, privilege: $privilege) {\n id\n name\n privilegeCount\n }\n }\n `,\n variables: { category: this.request.category, privilege: this.request.privilege },\n context: gqlContext(),\n fetchPolicy: 'network-only'\n })\n this.candidates = response.data?.rolesGranting || []\n this.roleId = firstCandidate(this.candidates)?.id\n await this.review()\n }\n\n /** Asks the server again; an answer to an older question is dropped. */\n private async review() {\n const asked = ++this.asked\n const ask = (decision: any) => client.query({ query: REVIEW, variables: { decision }, context: gqlContext(), fetchPolicy: 'network-only' })\n const [grant, deny] = await Promise.all([\n ask({ requestId: this.request.id, decision: 'GRANT', roleId: this.roleId }),\n ask({ requestId: this.request.id, decision: 'DENY', reason: this.reason })\n ])\n if (asked !== this.asked) return\n this.grantIssues = grant.errors?.length ? null : grant.data?.reviewAccessDecision || []\n this.denyIssues = deny.errors?.length ? null : deny.data?.reviewAccessDecision || []\n }\n\n render() {\n const t = i18next.t.bind(i18next)\n const { request } = this\n const buttons = decisionButtons(this.grantIssues, this.denyIssues)\n const reasonIssues = (this.denyIssues || []).filter(issue => issue.field === 'reason')\n\n return html`\n <div body>\n <div who>${t('access-request.text.who asks', { name: request.requester?.name || request.requester?.email || '' })}</div>\n <div needed>${privilegeWords(t, request.category, request.privilege)}</div>\n ${request.reason ? html`<div why>${request.reason}</div>` : nothing}\n\n <div>${t('access-request.text.grant with role')}</div>\n ${this.candidates.length === 0\n ? html`<div none>${t('access-request.text.no role grants it')}</div>`\n : this.candidates.map(\n candidate => html`\n <label candidate>\n <md-radio\n name=\"role\"\n .checked=${candidate.id === this.roleId}\n @change=${() => {\n this.roleId = candidate.id\n this.review()\n }}\n ></md-radio>\n <span>${candidate.name}</span>\n <span count>${t('access-request.text.privilege count', { count: candidate.privilegeCount })}</span>\n </label>\n `\n )}\n ${(this.grantIssues || []).map(issue => html`<div issue ?blocking=${issue.blocking}>${issueWords(t, issue)}</div>`)}\n\n <md-outlined-text-field\n label=${t('access-request.field.deny reason')}\n type=\"textarea\"\n rows=\"2\"\n .value=${this.reason}\n @input=${(e: any) => {\n this.reason = e.target.value\n this.review()\n }}\n ></md-outlined-text-field>\n ${reasonIssues.length && this.reason ? reasonIssues.map(issue => html`<div issue blocking>${issueWords(t, issue)}</div>`) : nothing}\n </div>\n\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => history.back()}>${t('button.cancel')}</md-text-button>\n <md-text-button slot=\"start\" ?disabled=${!buttons.deny} @click=${() => this.decide('DENY')}>\n ${t('access-request.button.deny')}\n </md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${!buttons.grant} @click=${() => this.decide('GRANT')}>\n ${t('access-request.button.grant')}\n </md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async decide(decision: 'GRANT' | 'DENY') {\n const response = await client.mutate({\n mutation: gql`\n mutation ($decision: AccessDecisionInput!) {\n decideAccessRequest(decision: $decision) {\n id\n state\n }\n }\n `,\n variables: {\n decision:\n decision === 'GRANT'\n ? { requestId: this.request.id, decision, roleId: this.roleId }\n : { requestId: this.request.id, decision, reason: this.reason }\n },\n context: gqlContext()\n })\n\n if (!response.errors?.length) {\n this.dispatchEvent(new CustomEvent('access-request-decided', { bubbles: true, composed: true }))\n history.back()\n } else {\n /* Somebody may have decided meanwhile - show what the server says now. */\n await this.review()\n }\n }\n}\n"]}
1
+ {"version":3,"file":"access-decision-popup.js","sourceRoot":"","sources":["../../client/components/access-decision-popup.ts"],"names":[],"mappings":";AAAA,OAAO,oCAAoC,CAAA;AAC3C,OAAO,qCAAqC,CAAA;AAC5C,OAAO,uCAAuC,CAAA;AAC9C,OAAO,8BAA8B,CAAA;AACrC,OAAO,gDAAgD,CAAA;AAEvD,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,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,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEpE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAoB,MAAM,4BAA4B,CAAA;AAEzI,MAAM,MAAM,GAAG,GAAG,CAAA;;;;;;;;;CASjB,CAAA;AAED;;;;;;GAMG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAA/D;;QAqDY,eAAU,GAA2D,EAAE,CAAA;QAEvE,WAAM,GAAG,EAAE,CAAA;QACX,gBAAW,GAAyB,IAAI,CAAA;QACxC,eAAU,GAAyB,IAAI,CAAA;QAEhD,UAAK,GAAG,CAAC,CAAA;IAwHnB,CAAC;aAlLQ,WAAM,GAAG;QACd,iBAAiB;QACjB,eAAe;QACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4CF;KACF,AAhDY,CAgDZ;IAYD,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;OAQT;YACD,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE;YACjF,OAAO,EAAE,UAAU,EAAE;YACrB,WAAW,EAAE,cAAc;SAC5B,CAAC,CAAA;QACF,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,aAAa,IAAI,EAAE,CAAA;QACpD,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAA;QACjD,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;IACrB,CAAC;IAED,wEAAwE;IAChE,KAAK,CAAC,MAAM;QAClB,MAAM,KAAK,GAAG,EAAE,IAAI,CAAC,KAAK,CAAA;QAC1B,MAAM,GAAG,GAAG,CAAC,QAAa,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,CAAA;QAC3I,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACtC,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YAC3E,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;SAC3E,CAAC,CAAA;QACF,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK;YAAE,OAAM;QAChC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,oBAAoB,IAAI,EAAE,CAAA;QACvF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,IAAI,EAAE,CAAA;IACtF,CAAC;IAED,MAAM;QACJ,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACjC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QACxB,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QAClE,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAA;QAEtF,OAAO,IAAI,CAAA;;mBAEI,CAAC,CAAC,8BAA8B,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;sBACnG,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC;UAClE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA,YAAY,OAAO,CAAC,MAAM,QAAQ,CAAC,CAAC,CAAC,OAAO;;eAE5D,CAAC,CAAC,qCAAqC,CAAC;UAC7C,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;YAC5B,CAAC,CAAC,IAAI,CAAA,aAAa,CAAC,CAAC,uCAAuC,CAAC,QAAQ;YACrE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CACjB,SAAS,CAAC,EAAE,CAAC,IAAI,CAAA;;;;+BAIA,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,MAAM;8BAC7B,GAAG,EAAE;gBACb,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAA;gBAC1B,IAAI,CAAC,MAAM,EAAE,CAAA;YACf,CAAC;;0BAEK,SAAS,CAAC,IAAI;gCACR,CAAC,CAAC,qCAAqC,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,cAAc,EAAE,CAAC;;eAE9F,CACF;UACH,aAAa,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAA,wBAAwB,KAAK,CAAC,QAAQ,IAAI,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC;;;kBAGtH,CAAC,CAAC,kCAAkC,CAAC;;;mBAGpC,IAAI,CAAC,MAAM;mBACX,CAAC,CAAM,EAAE,EAAE;YAClB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YAC5B,IAAI,CAAC,MAAM,EAAE,CAAA;QACf,CAAC;;UAED,YAAY,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAA,uBAAuB,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO;;;;+CAI5F,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,eAAe,CAAC;iDACxC,CAAC,OAAO,CAAC,IAAI,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;YACtF,CAAC,CAAC,4BAA4B,CAAC;;qDAEU,CAAC,OAAO,CAAC,KAAK,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;YAC5F,CAAC,CAAC,6BAA6B,CAAC;;;KAGvC,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,QAA0B;QAC7C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;OAOZ;YACD,SAAS,EAAE;gBACT,QAAQ,EACN,QAAQ,KAAK,OAAO;oBAClB,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;oBAC/D,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;aACpE;YACD,OAAO,EAAE,UAAU,EAAE;SACtB,CAAC,CAAA;QAEF,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YAC7B,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,wBAAwB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;YAChG,OAAO,CAAC,IAAI,EAAE,CAAA;QAChB,CAAC;aAAM,CAAC;YACN,0EAA0E;YAC1E,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;QACrB,CAAC;IACH,CAAC;;AA/H2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDAAa;AAEvB;IAAhB,KAAK,EAAE;;uDAAgF;AACvE;IAAhB,KAAK,EAAE;;mDAAwB;AACf;IAAhB,KAAK,EAAE;;mDAAoB;AACX;IAAhB,KAAK,EAAE;;wDAAiD;AACxC;IAAhB,KAAK,EAAE;;uDAAgD;AAzD7C,mBAAmB;IAD/B,aAAa,CAAC,uBAAuB,CAAC;GAC1B,mBAAmB,CAmL/B","sourcesContent":["import '@operato/popup/ox-popup-actions.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/radio/radio.js'\nimport '@material/web/textfield/outlined-text-field.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { CommonPopupStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { decisionButtons, firstCandidate, issueWords, orderedIssues, privilegeWords, type ReviewIssue } from '../utils/access-request.js'\n\nconst REVIEW = gql`\n query ($decision: AccessDecisionInput!) {\n reviewAccessDecision(decision: $decision) {\n code\n blocking\n field\n params\n }\n }\n`\n\n/**\n * Deciding one access request (ADR-0103 · docs/design/access-request.md §6-1).\n *\n * The findings are the server's review - the same function the AI draft card and the decision door read - so\n * this popup never judges a role itself. Each change of role or reason asks again. A blocking finding takes\n * the button away; a warning stays beside it.\n */\n@customElement('access-decision-popup')\nexport class AccessDecisionPopup extends localize(i18next)(LitElement) {\n static styles = [\n CommonPopupStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--md-sys-color-surface);\n }\n [body] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium);\n padding: var(--spacing-large);\n }\n [who],\n [why] {\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface-variant);\n }\n [needed] {\n font: var(--title-font, inherit);\n }\n label[candidate] {\n display: flex;\n align-items: center;\n gap: var(--spacing-small);\n }\n [count] {\n color: var(--md-sys-color-on-surface-variant);\n }\n [issue][blocking] {\n color: var(--md-sys-color-error);\n }\n [issue]:not([blocking]) {\n color: var(--md-sys-color-tertiary);\n }\n [none] {\n color: var(--md-sys-color-on-surface-variant);\n }\n md-outlined-text-field {\n width: 100%;\n }\n `\n ]\n\n @property({ type: Object }) request: any\n\n @state() private candidates: { id: string; name: string; privilegeCount: number }[] = []\n @state() private roleId?: string\n @state() private reason = ''\n @state() private grantIssues: ReviewIssue[] | null = null\n @state() private denyIssues: ReviewIssue[] | null = null\n\n private asked = 0\n\n async firstUpdated() {\n const response = await client.query({\n query: gql`\n query ($category: String!, $privilege: String!) {\n rolesGranting(category: $category, privilege: $privilege) {\n id\n name\n privilegeCount\n }\n }\n `,\n variables: { category: this.request.category, privilege: this.request.privilege },\n context: gqlContext(),\n fetchPolicy: 'network-only'\n })\n this.candidates = response.data?.rolesGranting || []\n this.roleId = firstCandidate(this.candidates)?.id\n await this.review()\n }\n\n /** Asks the server again; an answer to an older question is dropped. */\n private async review() {\n const asked = ++this.asked\n const ask = (decision: any) => client.query({ query: REVIEW, variables: { decision }, context: gqlContext(), fetchPolicy: 'network-only' })\n const [grant, deny] = await Promise.all([\n ask({ requestId: this.request.id, decision: 'GRANT', roleId: this.roleId }),\n ask({ requestId: this.request.id, decision: 'DENY', reason: this.reason })\n ])\n if (asked !== this.asked) return\n this.grantIssues = grant.errors?.length ? null : grant.data?.reviewAccessDecision || []\n this.denyIssues = deny.errors?.length ? null : deny.data?.reviewAccessDecision || []\n }\n\n render() {\n const t = i18next.t.bind(i18next)\n const { request } = this\n const buttons = decisionButtons(this.grantIssues, this.denyIssues)\n const reasonIssues = (this.denyIssues || []).filter(issue => issue.field === 'reason')\n\n return html`\n <div body>\n <div who>${t('access-request.text.who asks', { name: request.requester?.name || request.requester?.email || '' })}</div>\n <div needed>${privilegeWords(t, request.category, request.privilege)}</div>\n ${request.reason ? html`<div why>${request.reason}</div>` : nothing}\n\n <div>${t('access-request.text.grant with role')}</div>\n ${this.candidates.length === 0\n ? html`<div none>${t('access-request.text.no role grants it')}</div>`\n : this.candidates.map(\n candidate => html`\n <label candidate>\n <md-radio\n name=\"role\"\n .checked=${candidate.id === this.roleId}\n @change=${() => {\n this.roleId = candidate.id\n this.review()\n }}\n ></md-radio>\n <span>${candidate.name}</span>\n <span count>${t('access-request.text.privilege count', { count: candidate.privilegeCount })}</span>\n </label>\n `\n )}\n ${orderedIssues(this.grantIssues || []).map(issue => html`<div issue ?blocking=${issue.blocking}>${issueWords(t, issue)}</div>`)}\n\n <md-outlined-text-field\n label=${t('access-request.field.deny reason')}\n type=\"textarea\"\n rows=\"2\"\n .value=${this.reason}\n @input=${(e: any) => {\n this.reason = e.target.value\n this.review()\n }}\n ></md-outlined-text-field>\n ${reasonIssues.length && this.reason ? reasonIssues.map(issue => html`<div issue blocking>${issueWords(t, issue)}</div>`) : nothing}\n </div>\n\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => history.back()}>${t('button.cancel')}</md-text-button>\n <md-text-button slot=\"start\" ?disabled=${!buttons.deny} @click=${() => this.decide('DENY')}>\n ${t('access-request.button.deny')}\n </md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${!buttons.grant} @click=${() => this.decide('GRANT')}>\n ${t('access-request.button.grant')}\n </md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async decide(decision: 'GRANT' | 'DENY') {\n const response = await client.mutate({\n mutation: gql`\n mutation ($decision: AccessDecisionInput!) {\n decideAccessRequest(decision: $decision) {\n id\n state\n }\n }\n `,\n variables: {\n decision:\n decision === 'GRANT'\n ? { requestId: this.request.id, decision, roleId: this.roleId }\n : { requestId: this.request.id, decision, reason: this.reason }\n },\n context: gqlContext()\n })\n\n if (!response.errors?.length) {\n this.dispatchEvent(new CustomEvent('access-request-decided', { bubbles: true, composed: true }))\n history.back()\n } else {\n /* Somebody may have decided meanwhile - show what the server says now. */\n await this.review()\n }\n }\n}\n"]}
@@ -38,9 +38,14 @@ let AccessRequestPopup = class AccessRequestPopup extends localize(i18next)(LitE
38
38
  :host {
39
39
  display: flex;
40
40
  flex-direction: column;
41
+ overflow: hidden;
41
42
  background-color: var(--md-sys-color-surface);
42
43
  }
44
+ /* min-height: 0 lets the body scroll inside the popup, so the send button below it stays in view. */
43
45
  [body] {
46
+ flex: 1;
47
+ min-height: 0;
48
+ overflow-y: auto;
44
49
  display: flex;
45
50
  flex-direction: column;
46
51
  gap: var(--spacing-medium);
@@ -1 +1 @@
1
- {"version":3,"file":"access-request-popup.js","sourceRoot":"","sources":["../../client/components/access-request-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/popup/ox-popup-actions.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/textfield/outlined-text-field.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { CommonPopupStyles } from '@operato/styles'\n\nimport { maySend, privilegeWords, recentDenial } from '../utils/access-request.js'\n\n/**\n * Asking for a privilege the person was just refused (ADR-0103, docs/design/access-request.md §3-2).\n *\n * What was needed comes from the refusal - the person writes only why, and must (questions Q9). Before sending\n * the popup says the request is recorded and how many people it reaches; a denial of the last days is shown with\n * its reason, and the same words are not sent again. A request already waiting is shown instead of a blank form:\n * pressing again adds to it, the people deciding are not told twice.\n */\n@customElement('access-request-popup')\nexport class AccessRequestPopup extends localize(i18next)(LitElement) {\n static styles = [\n CommonPopupStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n background-color: var(--md-sys-color-surface);\n }\n [body] {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium);\n padding: var(--spacing-large);\n }\n [needed] {\n font: var(--title-font, inherit);\n color: var(--md-sys-color-on-surface);\n }\n [operation],\n [waiting],\n [recorded] {\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface-variant);\n }\n [denied] {\n font: var(--label-font, inherit);\n color: var(--md-sys-color-error);\n }\n md-outlined-text-field {\n width: 100%;\n }\n `\n ]\n\n @property({ type: String }) category: string = ''\n @property({ type: String }) privilege: string = ''\n @property({ type: String }) operationName?: string\n\n /** The request of this person and privilege that is still waiting, if any. */\n @state() private waiting: any = null\n /** A denial of the same privilege in the last days - its reason is shown before the person writes (Q9 · 5). */\n @state() private denied: any = null\n /** How many people a request reaches - said before sending (Q9 · 2). */\n @state() private deciders: number | null = null\n @state() private reason = ''\n @state() private sending = false\n\n async firstUpdated() {\n const response = await client.query({\n query: gql`\n query {\n myAccessRequests {\n id\n category\n privilege\n state\n reason\n count\n lastRequestedAt\n decidedAt\n decisionReason\n }\n accessRequestDeciderCount\n }\n `,\n context: gqlContext(),\n fetchPolicy: 'network-only'\n })\n const mine = response.data?.myAccessRequests || []\n this.waiting = mine.find((r: any) => r.state === 'PENDING' && r.category === this.category && r.privilege === this.privilege) || null\n this.denied = this.waiting ? null : recentDenial(mine, this.category, this.privilege) || null\n this.deciders = typeof response.data?.accessRequestDeciderCount === 'number' ? response.data.accessRequestDeciderCount : null\n this.reason = this.waiting?.reason || ''\n }\n\n render() {\n return html`\n <div body>\n <div needed>${privilegeWords(i18next.t.bind(i18next), this.category, this.privilege)}</div>\n ${this.operationName\n ? html`<div operation>${i18next.t('access-request.text.refused at', { operation: this.operationName })}</div>`\n : ''}\n ${this.waiting\n ? html`<div waiting>\n ${i18next.t('access-request.text.already waiting', { at: new Date(this.waiting.lastRequestedAt).toLocaleString() })}\n </div>`\n : ''}\n ${this.denied\n ? html`<div denied>\n ${i18next.t('access-request.text.denied lately', {\n at: new Date(this.denied.decidedAt).toLocaleDateString(),\n reason: this.denied.decisionReason || ''\n })}\n </div>`\n : ''}\n <md-outlined-text-field\n label=${i18next.t('access-request.field.reason')}\n required\n type=\"textarea\"\n rows=\"2\"\n .value=${this.reason}\n @input=${(e: any) => (this.reason = e.target.value)}\n ></md-outlined-text-field>\n ${this.deciders === null\n ? ''\n : html`<div recorded>\n ${this.deciders > 0\n ? i18next.t('access-request.text.recorded and told', { count: this.deciders })\n : i18next.t('access-request.text.nobody to tell')}\n </div>`}\n </div>\n\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => history.back()}>${i18next.t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.sending || !maySend(this.reason, this.denied) || this.deciders === 0} @click=${this.send}>\n ${i18next.t('access-request.button.send')}\n </md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async send() {\n this.sending = true\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($request: NewAccessRequest!) {\n requestAccess(request: $request) {\n id\n count\n }\n }\n `,\n variables: {\n request: {\n category: this.category,\n privilege: this.privilege,\n operationName: this.operationName,\n reason: this.reason.trim()\n }\n },\n context: gqlContext()\n })\n\n if (!response.errors?.length) {\n const merged = (response.data?.requestAccess?.count || 1) > 1\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: { level: 'info', message: i18next.t(merged ? 'access-request.text.added to waiting' : 'access-request.text.sent') }\n })\n )\n history.back()\n }\n } finally {\n this.sending = false\n }\n }\n}\n"]}
1
+ {"version":3,"file":"access-request-popup.js","sourceRoot":"","sources":["../../client/components/access-request-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/popup/ox-popup-actions.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/textfield/outlined-text-field.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { CommonPopupStyles } from '@operato/styles'\n\nimport { maySend, privilegeWords, recentDenial } from '../utils/access-request.js'\n\n/**\n * Asking for a privilege the person was just refused (ADR-0103, docs/design/access-request.md §3-2).\n *\n * What was needed comes from the refusal - the person writes only why, and must (questions Q9). Before sending\n * the popup says the request is recorded and how many people it reaches; a denial of the last days is shown with\n * its reason, and the same words are not sent again. A request already waiting is shown instead of a blank form:\n * pressing again adds to it, the people deciding are not told twice.\n */\n@customElement('access-request-popup')\nexport class AccessRequestPopup extends localize(i18next)(LitElement) {\n static styles = [\n CommonPopupStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--md-sys-color-surface);\n }\n /* min-height: 0 lets the body scroll inside the popup, so the send button below it stays in view. */\n [body] {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium);\n padding: var(--spacing-large);\n }\n [needed] {\n font: var(--title-font, inherit);\n color: var(--md-sys-color-on-surface);\n }\n [operation],\n [waiting],\n [recorded] {\n font: var(--label-font, inherit);\n color: var(--md-sys-color-on-surface-variant);\n }\n [denied] {\n font: var(--label-font, inherit);\n color: var(--md-sys-color-error);\n }\n md-outlined-text-field {\n width: 100%;\n }\n `\n ]\n\n @property({ type: String }) category: string = ''\n @property({ type: String }) privilege: string = ''\n @property({ type: String }) operationName?: string\n\n /** The request of this person and privilege that is still waiting, if any. */\n @state() private waiting: any = null\n /** A denial of the same privilege in the last days - its reason is shown before the person writes (Q9 · 5). */\n @state() private denied: any = null\n /** How many people a request reaches - said before sending (Q9 · 2). */\n @state() private deciders: number | null = null\n @state() private reason = ''\n @state() private sending = false\n\n async firstUpdated() {\n const response = await client.query({\n query: gql`\n query {\n myAccessRequests {\n id\n category\n privilege\n state\n reason\n count\n lastRequestedAt\n decidedAt\n decisionReason\n }\n accessRequestDeciderCount\n }\n `,\n context: gqlContext(),\n fetchPolicy: 'network-only'\n })\n const mine = response.data?.myAccessRequests || []\n this.waiting = mine.find((r: any) => r.state === 'PENDING' && r.category === this.category && r.privilege === this.privilege) || null\n this.denied = this.waiting ? null : recentDenial(mine, this.category, this.privilege) || null\n this.deciders = typeof response.data?.accessRequestDeciderCount === 'number' ? response.data.accessRequestDeciderCount : null\n this.reason = this.waiting?.reason || ''\n }\n\n render() {\n return html`\n <div body>\n <div needed>${privilegeWords(i18next.t.bind(i18next), this.category, this.privilege)}</div>\n ${this.operationName\n ? html`<div operation>${i18next.t('access-request.text.refused at', { operation: this.operationName })}</div>`\n : ''}\n ${this.waiting\n ? html`<div waiting>\n ${i18next.t('access-request.text.already waiting', { at: new Date(this.waiting.lastRequestedAt).toLocaleString() })}\n </div>`\n : ''}\n ${this.denied\n ? html`<div denied>\n ${i18next.t('access-request.text.denied lately', {\n at: new Date(this.denied.decidedAt).toLocaleDateString(),\n reason: this.denied.decisionReason || ''\n })}\n </div>`\n : ''}\n <md-outlined-text-field\n label=${i18next.t('access-request.field.reason')}\n required\n type=\"textarea\"\n rows=\"2\"\n .value=${this.reason}\n @input=${(e: any) => (this.reason = e.target.value)}\n ></md-outlined-text-field>\n ${this.deciders === null\n ? ''\n : html`<div recorded>\n ${this.deciders > 0\n ? i18next.t('access-request.text.recorded and told', { count: this.deciders })\n : i18next.t('access-request.text.nobody to tell')}\n </div>`}\n </div>\n\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => history.back()}>${i18next.t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${this.sending || !maySend(this.reason, this.denied) || this.deciders === 0} @click=${this.send}>\n ${i18next.t('access-request.button.send')}\n </md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private async send() {\n this.sending = true\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($request: NewAccessRequest!) {\n requestAccess(request: $request) {\n id\n count\n }\n }\n `,\n variables: {\n request: {\n category: this.category,\n privilege: this.privilege,\n operationName: this.operationName,\n reason: this.reason.trim()\n }\n },\n context: gqlContext()\n })\n\n if (!response.errors?.length) {\n const merged = (response.data?.requestAccess?.count || 1) > 1\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: { level: 'info', message: i18next.t(merged ? 'access-request.text.added to waiting' : 'access-request.text.sent') }\n })\n )\n history.back()\n }\n } finally {\n this.sending = false\n }\n }\n}\n"]}