diff --git a/src/app/api/PermissionsAPIService/APIPermissionsGetter.js b/src/app/api/PermissionsAPIService/APIPermissionsGetter.js new file mode 100644 index 00000000..90896020 --- /dev/null +++ b/src/app/api/PermissionsAPIService/APIPermissionsGetter.js @@ -0,0 +1,84 @@ +import { getDefaultGetListResponse, getDefaultGetParams } from '@webitel/ui-sdk/src/api/defaults/index.js'; +import applyTransform, { + camelToSnake, + generateUrl, + merge, + mergeEach, + notify, + sanitize, + snakeToCamel, + starToSearch, +} from '@webitel/ui-sdk/src/api/transformers/index.js'; +import instance from '../instance'; + +export default class APIPermissionsGetter { + nestedUrl = 'acl'; + + constructor(url) { + this.baseUrl = url; + + this.listGetter = async ({ parentId, ...params }) => { + const fieldsToSend = ['page', 'size', 'q', 'sort', 'fields', 'id']; + + const defaultObject = { + user: false, + }; + + const url = applyTransform(params, [ + merge(getDefaultGetParams()), + starToSearch('search'), + (params) => ({ + ...params, + q: params.search, + }), + sanitize(fieldsToSend), + camelToSnake(), + generateUrl(`${this.baseUrl}/${parentId}/${this.nestedUrl}`), + ]); + try { + const response = await instance.get(url); + const { items, next } = applyTransform(response.data, [ + snakeToCamel(), + merge(getDefaultGetListResponse()), + ]); + return { + items: applyTransform(items, [ + mergeEach(defaultObject), + APIPermissionsGetter.handlePermissionsList, + ]), + next, + }; + } catch (err) { + throw applyTransform(err, [notify]); + } + }; + } + + static handlePermissionsList(items) { + return items.map((item) => ({ + ...item, + access: { + x: { + id: (item.granted.match(/x/g) || []).length + 1, + rule: 'x'.repeat((item.granted.match(/x/g) || []).length), + }, + r: { + id: (item.granted.match(/r/g) || []).length + 1, + rule: 'r'.repeat((item.granted.match(/r/g) || []).length), + }, + w: { + id: (item.granted.match(/w/g) || []).length + 1, + rule: 'w'.repeat((item.granted.match(/w/g) || []).length), + }, + d: { + id: (item.granted.match(/d/g) || []).length + 1, + rule: 'd'.repeat((item.granted.match(/d/g) || []).length), + }, + }, + })); + } + + async getList(params) { + return this.listGetter(params); + } +} diff --git a/src/app/api/PermissionsAPIService/APIPermissionsPatcher.js b/src/app/api/PermissionsAPIService/APIPermissionsPatcher.js new file mode 100644 index 00000000..56c49413 --- /dev/null +++ b/src/app/api/PermissionsAPIService/APIPermissionsPatcher.js @@ -0,0 +1,27 @@ +import applyTransform, { + camelToSnake, + notify, + snakeToCamel, +} from '@webitel/ui-sdk/src/api/transformers/index.js'; +import instance from '../instance'; + +export default class APIPermissionsPatcher { + constructor(baseUrl) { + this.baseUrl = baseUrl; + this.patcher = async ({ changes, id }) => { + const afterUrl = 'acl'; + const body = applyTransform(changes, [camelToSnake()]); + const url = `${baseUrl}/${id}/${afterUrl}`; + try { + const response = await instance.patch(url, body); + return applyTransform(response.data, [snakeToCamel()]); + } catch (err) { + throw applyTransform(err, [notify]); + } + }; + } + + async patchItem({ id, changes }) { + return this.patcher({ id, changes }); + } +} diff --git a/src/app/assets/icons/sprite/conf-lookups.svg b/src/app/assets/icons/sprite/conf-lookups.svg new file mode 100644 index 00000000..9d195c17 --- /dev/null +++ b/src/app/assets/icons/sprite/conf-lookups.svg @@ -0,0 +1,6 @@ + + + + diff --git a/src/app/assets/icons/sprite/index.js b/src/app/assets/icons/sprite/index.js new file mode 100644 index 00000000..1bba68f4 --- /dev/null +++ b/src/app/assets/icons/sprite/index.js @@ -0,0 +1,2 @@ +import './conf-lookups.svg'; + diff --git a/src/app/css/main.scss b/src/app/css/main.scss index db782d0a..039c7aa0 100644 --- a/src/app/css/main.scss +++ b/src/app/css/main.scss @@ -1,12 +1,21 @@ @import '@webitel/ui-sdk/src/css/main'; +@import './objects/table-page'; +@import './objects/objects'; //disables scrollbar on 100vh auth on small laptops body { + @extend %wt-scrollbar; @extend %typo-body-1; display: flex; min-width: 100%; min-height: 100%; color: var(--text-main-color); + background: var(--wt-page-wrapper-background-color); +} + +#app { + min-width: 100%; + min-height: 100%; } .wt-page-wrapper { @@ -25,6 +34,7 @@ body { .table-wrapper { flex-grow: 1; display: flex; + padding-top: var(--spacing-xs); flex-direction: column; gap: var(--spacing-xs); max-height: 100%; @@ -70,3 +80,74 @@ a { text-decoration: none; color: #000; } + +.mb-0 { + margin-bottom: 0; +} + +// helper class +.no-padding { + padding: 0; +} + +.box-shadow { + //box-shadow: 0px 5px 6px rgba(0, 0, 0, 0.2), 0px 3px 16px rgba(0, 0, 0, 0.12), 0px 9px 12px rgba(0, 0, 0, 0.14); + box-shadow: rgba(0, 0, 0, 0.08) 0 8px 18px; +} + +.icon-action { + cursor: pointer; + + &:before { + transition: var(--transition); + } + + &:hover:before { + color: #000; + } +} + +// helper underline class +.border-underline { + position: relative; + + &:before { + position: absolute; + right: 0; + bottom: 6px; + left: 0; + height: 1px; + content: ''; + } +} + +$scrollbar-bg-color: #EAEAEA; +$srollbar-thumb-color: var(--primary-color); +$srollbar-border-radius: 4px; + +.scrollbar { + // scrollbar itself + &::-webkit-scrollbar { + width: 4px; + height: 6px; + cursor: pointer; + background-color: $scrollbar-bg-color; + } + + // scrollable element + &::-webkit-scrollbar-thumb { + width: 4px; + height: 6px; + border-radius: $srollbar-border-radius; + background-color: $srollbar-thumb-color; + } +} + +.hidden { + pointer-events: none; + opacity: 0; +} + +.value-pair-wrap { + position: relative; +} diff --git a/src/app/css/objects/objects.scss b/src/app/css/objects/objects.scss new file mode 100644 index 00000000..d106d17b --- /dev/null +++ b/src/app/css/objects/objects.scss @@ -0,0 +1,164 @@ +// ----- new styles + +.tabs-page-wrapper { + display: flex; + flex-direction: column; + width: 100%; +} + +.wt-page-wrapper { + width: 100%; + + .main-container { + display: flex; + flex-direction: column; + width: 100%; + + & > section { + display: flex; + flex-direction: column; + height: 100%; + } + } +} + +// ----- old styles + +// inner content section +.object-content { + display: flex; + flex-direction: column; + box-sizing: border-box; + width: 100%; + min-height: 65vh; + padding: 30px; + border-radius: var(--border-radius); + background: #fff; + + .pagination { + margin: auto 0 0 auto; + padding-top: 28px; + } +} + +.main-section__wrapper { + display: flex; + flex-direction: column; + width: 100%; +} + +.content-wrapper { + display: flex; + flex-direction: column; + flex-grow: 1; +} + +.btn-controls { + .btn { + margin-left: 28px; + } +} + +// SECTION HEADING +.content-header { + position: relative; + display: flex; + align-items: center; + flex-wrap: wrap; + justify-content: space-between; + margin: var(--spacing-sm) 0; + + .content-title { + @extend %typo-heading-3; + + + .hint { + top: -2px; + } + } + + // WRAP FOR SEARCH INPUT + .content-header__actions-wrap { + display: flex; + align-items: center; + gap: var(--table-actions-icon-gap); + } +} + +// GRID TEMPLATE FOR MODULE INPUTS +.object-input-grid { + display: grid; + align-items: flex-start; + grid-template-columns: 1fr 1fr; + grid-column-gap: 20px; + grid-row-gap: 16px; + + &__1-col { + grid-template-columns: 1fr; + } + + &__w50 { + width: 50%; + } +} + +.grid-w100 { + .object-input-grid { + grid-template-columns: 1fr; + } +} + +.grid-w50 { + .object-input-grid { + grid-template-columns: 50%; + } +} + +.value-pair { + display: grid; + align-items: center; + margin-bottom: 20px; + grid-template-columns: 1fr 1fr 24px; + grid-gap: 20px; +} + +.module-new .object-input-grid .select-preview { + margin-bottom: 20px; +} + +.module-new { + .new_w50 { + width: 50%; + + .form-input { + margin: 8px 0; + } + } + + &.object-with-tabs { + padding-top: 28px; + padding-right: 0; + padding-left: 0; + + .tabs-inner-component { + padding: 0 58px 38px 58px; + } + } +} + +.opened-calendar-work-week { + .vuetable tr { + border-bottom: transparent; + + &.day-start { + //border-top: 1px solid rgba(0, 0, 0, 0.1); + } + + &:last-child { + //border-bottom: 1px solid rgba(0, 0, 0, 0.1); + } + } +} + +.dummy-wrapper { + height: 100%; +} diff --git a/src/app/css/objects/table-page.scss b/src/app/css/objects/table-page.scss new file mode 100644 index 00000000..5b9e2246 --- /dev/null +++ b/src/app/css/objects/table-page.scss @@ -0,0 +1,60 @@ + +.actions-panel-wrapper { + display: flex; + + .filter-wrap { + flex: 1; + } + + .wt-table-actions { + height: fit-content; + margin-top: 24px; + } +} + +.table-wrapper { + display: flex; + flex: 1 1 100%; + flex-direction: column; + justify-content: space-between; + box-sizing: border-box; + width: 100%; + + .wt-table { + flex-grow: 1; + } + + .wt-pagination { + margin-top: 20px; + margin-left: auto; + } + + .table-action { + margin-left: 10px; + } + + /* + wrapper class for table content overflow: visible preserving table scroll itself + .table-wrapper>.table-wrapper__visible-scroll-wrapper>.wt-table + */ + .table-wrapper__visible-scroll-wrapper { + overflow: visible; + height: 100%; + + .wt-table { + overflow: visible; + } + } + + .table-wrapper__scroll-wrapper { + @extend %wt-scrollbar; + + overflow: auto; + height: 100%; + min-height: 0; + + .wt-table { + overflow: visible; + } + } +}