@things-factory/dashboard 10.1.256 → 10.1.257

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.
@@ -221,8 +221,8 @@ let DashboardHome = class DashboardHome extends localize(i18next)(BoardViewerPag
221
221
  if (!(name && value)) {
222
222
  return;
223
223
  }
224
- await client.query({
225
- query: gql `
224
+ await client.mutate({
225
+ mutation: gql `
226
226
  mutation UpdateSetting($patch: SettingPatch!, $name: String!) {
227
227
  updateSetting(patch: $patch, name: $name) {
228
228
  name
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../client/pages/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/board/ox-board-selector.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/icon/icon.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, TemplateResult } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { store } from '@operato/shell'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { isMobileDevice } from '@operato/utils'\nimport { privileged } from '@things-factory/auth-base/dist-client/directive/privileged.js'\nimport { BoardViewerPage } from '@things-factory/board-ui/dist-client/pages/board-viewer-page.js'\n\nimport { fetchBoardSettings, UPDATE_BOARD_SETTINGS } from '../actions/board-settings'\nimport { isBoardSettingsLoaded } from '../reducers/board-settings'\nimport { DASHBOARD, DASHBOARD_MOBILE, FALLBACK_HOME_BOARD } from '../const'\n\nconst HOME_DESCRIPTION = 'home dashboard'\n\nconst FALLBACK_ORDER = isMobileDevice()\n ? [DASHBOARD_MOBILE, DASHBOARD, FALLBACK_HOME_BOARD]\n : [DASHBOARD, FALLBACK_HOME_BOARD]\nconst HOME_BOARD = isMobileDevice() ? DASHBOARD_MOBILE : DASHBOARD\n\n/**\n * 홈 대시보드를 바꿀 권한 — 서버 지시자와 글자까지 같게 둔다.\n *\n * 서버: `@privilege(category: \"setting\", privilege: \"mutation\")` (setting-mutation.ts 의 updateSetting)\n *\n * 소유자·슈퍼유저 통과가 **선언되어 있지 않다.** 즉 도메인 소유자도 `setting/mutation` 을 따로\n * 부여받아야 한다. 화면에서 임의로 넓히면 버튼은 보이는데 누르면 거절당한다.\n *\n * 화면 게이팅은 UX 이지 보안이 아니다 — 집행은 서버가 한다. 여기서 감추는 것은 할 수 없는 일을\n * 권하지 않기 위한 것이다.\n */\nconst SETTING_MUTATION = { category: 'setting', privilege: 'mutation' }\n\n/**\n * things-factory 의 랜딩 화면.\n *\n * 앱이 따로 정하지 않으면 모든 things-factory 앱이 `/dashboard` 로 들어온다. 관리자가 홈 대시보드로\n * 지정한 보드를 그대로 보여주는 자리다.\n *\n * ── 이 화면이 갈라야 하는 상태 넷 ───────────────────────────────────────────\n *\n * ```\n * 아직 모른다 설정을 받아오는 중 → 기다리는 표시\n * 지정 없음 받아왔는데 지정된 보드 없음 → 무엇을 하면 되는지, 누가 할 수 있는지\n * 열지 못함 지정은 있는데 보드가 안 열림 → 서버가 준 이유를 그대로\n * 정상 보드 표시\n * ```\n *\n * 넷 중 앞의 셋을 하나로 뭉치면 화면이 거짓말을 한다. 실제로 그랬다 — 설정을 받아오는 동안\n * 「지정된 대시보드가 없습니다」가 떴다가 보드로 바뀌었고, 지정할 권한이 없는 사용자에게도\n * 「눌러서 지정하십시오」라고 안내했다(게다가 그 문구가 가리키는 누를 곳이 없었다).\n */\n@customElement('dashboard-home')\nexport class DashboardHome extends localize(i18next)(BoardViewerPage) {\n static styles = [\n ...(Array.isArray(BoardViewerPage.styles) ? BoardViewerPage.styles : [BoardViewerPage.styles]),\n css`\n .empty {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n\n max-width: 34rem;\n padding: var(--spacing-huge, 24px);\n text-align: center;\n }\n\n .empty md-icon {\n font: normal var(--icon-size-huge, 48px) var(--md-icon-font, 'Material Symbols Outlined');\n width: var(--icon-size-huge, 48px);\n height: var(--icon-size-huge, 48px);\n color: var(--md-sys-color-outline);\n }\n\n .empty h2 {\n margin: 0;\n font: var(--subtitle-font, bold 18px var(--theme-font));\n color: var(--md-sys-color-on-surface);\n }\n\n .empty p {\n margin: 0;\n font: normal var(--fontsize-default, 14px) / 1.6 var(--theme-font);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 서버가 준 문장. 길 수 있고 줄바꿈이 들어 있을 수 있어 그대로 흘린다. */\n .empty .reason {\n margin-top: var(--spacing-small, 4px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border: var(--border-dim-color, 1px solid var(--md-sys-color-outline-variant));\n border-radius: var(--border-radius, 4px);\n\n max-width: 100%;\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n\n font: normal var(--fontsize-small, 13px) / 1.5 var(--theme-font);\n color: var(--md-sys-color-on-surface-variant);\n text-align: left;\n }\n\n .empty md-filled-button {\n margin-top: var(--spacing-medium, 8px);\n }\n `\n ]\n\n /** 설정을 한 번이라도 받아왔는지. 받아오기 전에는 「지정된 것이 없다」고 말하지 않는다. */\n @state() private settingsLoaded = false\n\n private _unsubscribe?: () => void\n\n connectedCallback() {\n super.connectedCallback()\n\n const apply = () => {\n const state = store.getState() as any\n const dashboard = state?.boardSetting\n\n this.settingsLoaded = isBoardSettingsLoaded(dashboard)\n\n const homeBoard = dashboard && dashboard[FALLBACK_ORDER.find(name => dashboard[name]) || 'maybe notfound']\n this._boardId = homeBoard?.value\n }\n\n apply()\n this._unsubscribe = store.subscribe(apply)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this._unsubscribe?.()\n }\n\n /**\n * 지정된 보드를 열지 못했을 때. 「지정이 없다」와는 다른 상태다.\n *\n * 이유는 `_loadError` 에 서버 문장이 그대로 들어 있다. 비어 있으면 이유를 모르는 것이고,\n * 그때는 모른다고 둔다 — 여기서 원인을 지어내지 않는다.\n */\n get oopsNote() {\n return {\n icon: 'error_outline',\n title: i18next.t('title.home dashboard unavailable'),\n description: i18next.t('text.home dashboard unavailable')\n }\n }\n\n get context() {\n return {\n title: i18next.t('title.home dashboard')\n } as any\n }\n\n render(): TemplateResult<1> {\n if (!this.settingsLoaded) {\n return html`<ox-oops-spinner show></ox-oops-spinner>`\n }\n\n /* 지정은 되어 있는데 열지 못했다 — 「지정이 없다」와 다른 말을 해야 한다. */\n if (this._boardId && this._loadError) {\n return html`\n <div class=\"empty\">\n <md-icon>error_outline</md-icon>\n <h2>${i18next.t('title.home dashboard unavailable')}</h2>\n <p>${i18next.t('text.home dashboard unavailable')}</p>\n <div class=\"reason\" role=\"status\">${this._loadError}</div>\n ${this.renderSettingAction()}\n </div>\n `\n }\n\n if (this._boardId) {\n return super.render() as TemplateResult<1>\n }\n\n return html`\n <div class=\"empty\">\n <md-icon>dashboard_customize</md-icon>\n <h2>${i18next.t('title.no home dashboard')}</h2>\n <p>${i18next.t('text.no home dashboard')}</p>\n ${this.renderSettingAction()}\n </div>\n `\n }\n\n /** 지정 버튼 — 권한이 없으면 버튼 대신 누가 지정할 수 있는지 알린다. */\n private renderSettingAction() {\n return privileged(\n SETTING_MUTATION,\n html`\n <md-filled-button @click=${() => this.onClickDashboardSetting(HOME_BOARD, HOME_DESCRIPTION)}>\n ${i18next.t('button.set home dashboard')}\n </md-filled-button>\n `,\n html`<p>${i18next.t('text.home dashboard ask administrator')}</p>`\n )\n }\n\n onClickDashboardSetting(name, description) {\n var popup = openPopup(\n html`\n <ox-board-selector\n .creatable=${true}\n .value=${this._boardId}\n @board-selected=${async e => {\n var board = e.detail.board\n\n await this.saveSettings({\n name,\n value: board.id,\n category: 'board',\n description\n })\n\n var settings = await fetchBoardSettings([FALLBACK_HOME_BOARD, DASHBOARD, DASHBOARD_MOBILE])\n store.dispatch({\n type: UPDATE_BOARD_SETTINGS,\n settings: settings.reduce((settings, setting) => {\n settings[setting.name] = setting\n return settings\n }, {})\n })\n\n popup.close()\n this.requestUpdate()\n }}\n ></ox-board-selector>\n `,\n {\n backdrop: true,\n size: 'large',\n title: i18next.t('title.dashboard setting')\n }\n )\n }\n\n async saveSettings({ name, value, category, description }) {\n if (!(name && value)) {\n return\n }\n\n await client.query({\n query: gql`\n mutation UpdateSetting($patch: SettingPatch!, $name: String!) {\n updateSetting(patch: $patch, name: $name) {\n name\n value\n }\n }\n `,\n variables: {\n name,\n patch: {\n name,\n description,\n category,\n value\n }\n }\n })\n }\n}\n"]}
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../client/pages/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/board/ox-board-selector.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/icon/icon.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, TemplateResult } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { store } from '@operato/shell'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { isMobileDevice } from '@operato/utils'\nimport { privileged } from '@things-factory/auth-base/dist-client/directive/privileged.js'\nimport { BoardViewerPage } from '@things-factory/board-ui/dist-client/pages/board-viewer-page.js'\n\nimport { fetchBoardSettings, UPDATE_BOARD_SETTINGS } from '../actions/board-settings'\nimport { isBoardSettingsLoaded } from '../reducers/board-settings'\nimport { DASHBOARD, DASHBOARD_MOBILE, FALLBACK_HOME_BOARD } from '../const'\n\nconst HOME_DESCRIPTION = 'home dashboard'\n\nconst FALLBACK_ORDER = isMobileDevice()\n ? [DASHBOARD_MOBILE, DASHBOARD, FALLBACK_HOME_BOARD]\n : [DASHBOARD, FALLBACK_HOME_BOARD]\nconst HOME_BOARD = isMobileDevice() ? DASHBOARD_MOBILE : DASHBOARD\n\n/**\n * 홈 대시보드를 바꿀 권한 — 서버 지시자와 글자까지 같게 둔다.\n *\n * 서버: `@privilege(category: \"setting\", privilege: \"mutation\")` (setting-mutation.ts 의 updateSetting)\n *\n * 소유자·슈퍼유저 통과가 **선언되어 있지 않다.** 즉 도메인 소유자도 `setting/mutation` 을 따로\n * 부여받아야 한다. 화면에서 임의로 넓히면 버튼은 보이는데 누르면 거절당한다.\n *\n * 화면 게이팅은 UX 이지 보안이 아니다 — 집행은 서버가 한다. 여기서 감추는 것은 할 수 없는 일을\n * 권하지 않기 위한 것이다.\n */\nconst SETTING_MUTATION = { category: 'setting', privilege: 'mutation' }\n\n/**\n * things-factory 의 랜딩 화면.\n *\n * 앱이 따로 정하지 않으면 모든 things-factory 앱이 `/dashboard` 로 들어온다. 관리자가 홈 대시보드로\n * 지정한 보드를 그대로 보여주는 자리다.\n *\n * ── 이 화면이 갈라야 하는 상태 넷 ───────────────────────────────────────────\n *\n * ```\n * 아직 모른다 설정을 받아오는 중 → 기다리는 표시\n * 지정 없음 받아왔는데 지정된 보드 없음 → 무엇을 하면 되는지, 누가 할 수 있는지\n * 열지 못함 지정은 있는데 보드가 안 열림 → 서버가 준 이유를 그대로\n * 정상 보드 표시\n * ```\n *\n * 넷 중 앞의 셋을 하나로 뭉치면 화면이 거짓말을 한다. 실제로 그랬다 — 설정을 받아오는 동안\n * 「지정된 대시보드가 없습니다」가 떴다가 보드로 바뀌었고, 지정할 권한이 없는 사용자에게도\n * 「눌러서 지정하십시오」라고 안내했다(게다가 그 문구가 가리키는 누를 곳이 없었다).\n */\n@customElement('dashboard-home')\nexport class DashboardHome extends localize(i18next)(BoardViewerPage) {\n static styles = [\n ...(Array.isArray(BoardViewerPage.styles) ? BoardViewerPage.styles : [BoardViewerPage.styles]),\n css`\n .empty {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n\n max-width: 34rem;\n padding: var(--spacing-huge, 24px);\n text-align: center;\n }\n\n .empty md-icon {\n font: normal var(--icon-size-huge, 48px) var(--md-icon-font, 'Material Symbols Outlined');\n width: var(--icon-size-huge, 48px);\n height: var(--icon-size-huge, 48px);\n color: var(--md-sys-color-outline);\n }\n\n .empty h2 {\n margin: 0;\n font: var(--subtitle-font, bold 18px var(--theme-font));\n color: var(--md-sys-color-on-surface);\n }\n\n .empty p {\n margin: 0;\n font: normal var(--fontsize-default, 14px) / 1.6 var(--theme-font);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 서버가 준 문장. 길 수 있고 줄바꿈이 들어 있을 수 있어 그대로 흘린다. */\n .empty .reason {\n margin-top: var(--spacing-small, 4px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border: var(--border-dim-color, 1px solid var(--md-sys-color-outline-variant));\n border-radius: var(--border-radius, 4px);\n\n max-width: 100%;\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n\n font: normal var(--fontsize-small, 13px) / 1.5 var(--theme-font);\n color: var(--md-sys-color-on-surface-variant);\n text-align: left;\n }\n\n .empty md-filled-button {\n margin-top: var(--spacing-medium, 8px);\n }\n `\n ]\n\n /** 설정을 한 번이라도 받아왔는지. 받아오기 전에는 「지정된 것이 없다」고 말하지 않는다. */\n @state() private settingsLoaded = false\n\n private _unsubscribe?: () => void\n\n connectedCallback() {\n super.connectedCallback()\n\n const apply = () => {\n const state = store.getState() as any\n const dashboard = state?.boardSetting\n\n this.settingsLoaded = isBoardSettingsLoaded(dashboard)\n\n const homeBoard = dashboard && dashboard[FALLBACK_ORDER.find(name => dashboard[name]) || 'maybe notfound']\n this._boardId = homeBoard?.value\n }\n\n apply()\n this._unsubscribe = store.subscribe(apply)\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this._unsubscribe?.()\n }\n\n /**\n * 지정된 보드를 열지 못했을 때. 「지정이 없다」와는 다른 상태다.\n *\n * 이유는 `_loadError` 에 서버 문장이 그대로 들어 있다. 비어 있으면 이유를 모르는 것이고,\n * 그때는 모른다고 둔다 — 여기서 원인을 지어내지 않는다.\n */\n get oopsNote() {\n return {\n icon: 'error_outline',\n title: i18next.t('title.home dashboard unavailable'),\n description: i18next.t('text.home dashboard unavailable')\n }\n }\n\n get context() {\n return {\n title: i18next.t('title.home dashboard')\n } as any\n }\n\n render(): TemplateResult<1> {\n if (!this.settingsLoaded) {\n return html`<ox-oops-spinner show></ox-oops-spinner>`\n }\n\n /* 지정은 되어 있는데 열지 못했다 — 「지정이 없다」와 다른 말을 해야 한다. */\n if (this._boardId && this._loadError) {\n return html`\n <div class=\"empty\">\n <md-icon>error_outline</md-icon>\n <h2>${i18next.t('title.home dashboard unavailable')}</h2>\n <p>${i18next.t('text.home dashboard unavailable')}</p>\n <div class=\"reason\" role=\"status\">${this._loadError}</div>\n ${this.renderSettingAction()}\n </div>\n `\n }\n\n if (this._boardId) {\n return super.render() as TemplateResult<1>\n }\n\n return html`\n <div class=\"empty\">\n <md-icon>dashboard_customize</md-icon>\n <h2>${i18next.t('title.no home dashboard')}</h2>\n <p>${i18next.t('text.no home dashboard')}</p>\n ${this.renderSettingAction()}\n </div>\n `\n }\n\n /** 지정 버튼 — 권한이 없으면 버튼 대신 누가 지정할 수 있는지 알린다. */\n private renderSettingAction() {\n return privileged(\n SETTING_MUTATION,\n html`\n <md-filled-button @click=${() => this.onClickDashboardSetting(HOME_BOARD, HOME_DESCRIPTION)}>\n ${i18next.t('button.set home dashboard')}\n </md-filled-button>\n `,\n html`<p>${i18next.t('text.home dashboard ask administrator')}</p>`\n )\n }\n\n onClickDashboardSetting(name, description) {\n var popup = openPopup(\n html`\n <ox-board-selector\n .creatable=${true}\n .value=${this._boardId}\n @board-selected=${async e => {\n var board = e.detail.board\n\n await this.saveSettings({\n name,\n value: board.id,\n category: 'board',\n description\n })\n\n var settings = await fetchBoardSettings([FALLBACK_HOME_BOARD, DASHBOARD, DASHBOARD_MOBILE])\n store.dispatch({\n type: UPDATE_BOARD_SETTINGS,\n settings: settings.reduce((settings, setting) => {\n settings[setting.name] = setting\n return settings\n }, {})\n })\n\n popup.close()\n this.requestUpdate()\n }}\n ></ox-board-selector>\n `,\n {\n backdrop: true,\n size: 'large',\n title: i18next.t('title.dashboard setting')\n }\n )\n }\n\n async saveSettings({ name, value, category, description }) {\n if (!(name && value)) {\n return\n }\n\n await client.mutate({\n mutation: gql`\n mutation UpdateSetting($patch: SettingPatch!, $name: String!) {\n updateSetting(patch: $patch, name: $name) {\n name\n value\n }\n }\n `,\n variables: {\n name,\n patch: {\n name,\n description,\n category,\n value\n }\n }\n })\n }\n}\n"]}