mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-26 03:25:01 +02:00
Merge branch 'develop' of https://github.com/hcengineering/platform into staging-new
Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
@@ -310,7 +310,7 @@ services:
|
||||
- PAYMENT_URL=http://huly.local:3040
|
||||
- COMMUNICATION_API_ENABLED=true
|
||||
- BACKUP_URL=http://huly.local:4039/api/backup,
|
||||
- EXCLUDED_APPLICATIONS_FOR_ANONYMOUS=["chunter", "notification"]
|
||||
- EXCLUDED_APPLICATIONS_FOR_ANONYMOUS=[]
|
||||
# - DISABLE_SIGNUP=true
|
||||
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
|
||||
- PULSE_URL=ws://huly.local:8099/ws
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"BACKUP_URL": "http://huly.local:4039/api/backup",
|
||||
"PULSE_URL": "ws://huly.local:8099/ws",
|
||||
"HULYLAKE_URL": "http://huly.local:8096",
|
||||
"EXCLUDED_APPLICATIONS_FOR_ANONYMOUS": "[\"chunter\", \"notification\"]",
|
||||
"EXCLUDED_APPLICATIONS_FOR_ANONYMOUS": "[]",
|
||||
"ANALYTICS_COLLECTOR_URL": "http://huly.local:4017",
|
||||
"LINK_PREVIEW_URL": "http://huly.local:4041"
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ export interface Association extends Doc {
|
||||
nameA: string
|
||||
nameB: string
|
||||
type: '1:1' | '1:N' | 'N:N'
|
||||
automationOnly?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -947,6 +947,20 @@ export function createModel (builder: Builder): void {
|
||||
card.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: card.app.Card,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: card.class.CardSpace,
|
||||
enabled: false,
|
||||
order: 20
|
||||
},
|
||||
card.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
|
||||
builder.mixin(card.class.Card, core.class.Class, view.mixin.ClassFilters, {
|
||||
filters: ['space'],
|
||||
ignoreKeys: ['parent']
|
||||
|
||||
@@ -83,6 +83,20 @@ export function createModel (builder: Builder): void {
|
||||
chunter.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: chunter.app.Chunter,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: chunter.class.ChunterSpace,
|
||||
enabled: true,
|
||||
order: 15
|
||||
},
|
||||
chunter.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
workbench.class.Widget,
|
||||
core.space.Model,
|
||||
|
||||
@@ -95,7 +95,8 @@ export default mergeIds(chunterId, chunter, {
|
||||
},
|
||||
ids: {
|
||||
ChunterNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
},
|
||||
space: {
|
||||
General: '' as Ref<Channel>,
|
||||
|
||||
@@ -1124,6 +1124,20 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
documents.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: documents.app.Documents,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: documents.class.OrgSpace,
|
||||
enabled: false,
|
||||
order: 42
|
||||
},
|
||||
documents.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
definePermissions(builder)
|
||||
defineNotifications(builder)
|
||||
defineSearch(builder)
|
||||
|
||||
@@ -93,6 +93,7 @@ export default mergeIds(documentsId, documents, {
|
||||
StateNotification: '' as Ref<NotificationType>
|
||||
},
|
||||
ids: {
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -150,6 +150,8 @@ export class TAssociation extends TDoc implements Association {
|
||||
nameB!: string
|
||||
|
||||
type!: '1:1' | '1:N' | 'N:N'
|
||||
|
||||
automationOnly?: boolean
|
||||
}
|
||||
|
||||
@Model(core.class.Relation, core.class.Doc, DOMAIN_RELATION)
|
||||
|
||||
@@ -553,6 +553,20 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
document.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: document.app.Documents,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: document.class.Teamspace,
|
||||
enabled: false,
|
||||
order: 40
|
||||
},
|
||||
document.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
definePermissions(builder)
|
||||
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
|
||||
@@ -62,7 +62,8 @@ export default mergeIds(documentId, document, {
|
||||
Other: '' as Ref<TagCategory>
|
||||
},
|
||||
ids: {
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
},
|
||||
string: {
|
||||
ConfigDescription: '' as IntlString,
|
||||
|
||||
@@ -855,5 +855,19 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
drive.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: drive.app.Drive,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: drive.class.Drive,
|
||||
enabled: false,
|
||||
order: 60
|
||||
},
|
||||
drive.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
definePermissions(builder)
|
||||
}
|
||||
|
||||
@@ -102,7 +102,8 @@ export default mergeIds(driveId, drive, {
|
||||
RestoreFileVersion: '' as ViewAction
|
||||
},
|
||||
ids: {
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
},
|
||||
string: {
|
||||
Grid: '' as IntlString,
|
||||
|
||||
@@ -280,6 +280,20 @@ export function createModel (builder: Builder): void {
|
||||
love.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: love.app.Love,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: love.class.Office,
|
||||
enabled: false,
|
||||
order: 50
|
||||
},
|
||||
love.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
workbench.class.Widget,
|
||||
core.space.Model,
|
||||
|
||||
@@ -51,7 +51,8 @@ export default mergeIds(loveId, love, {
|
||||
Settings: '' as Ref<Doc>,
|
||||
LoveNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
MeetingMinutesChatNotification: '' as Ref<NotificationType>,
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
},
|
||||
function: {
|
||||
MeetingMinutesTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
|
||||
|
||||
@@ -318,7 +318,7 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
name: 'guestPermissions',
|
||||
label: setting.string.GuestPermissionsSettings,
|
||||
icon: setting.icon.Members,
|
||||
icon: setting.icon.GuestPermissions,
|
||||
component: setting.component.GuestPermissionsSettings,
|
||||
role: AccountRole.Owner,
|
||||
order: 1050
|
||||
|
||||
@@ -216,6 +216,20 @@ export function createModel (builder: Builder): void {
|
||||
testManagement.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: testManagement.app.TestManagement,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: testManagement.class.TestProject,
|
||||
enabled: false,
|
||||
order: 70
|
||||
},
|
||||
testManagement.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
|
||||
builder.mixin(testManagement.class.TestCase, core.class.Class, view.mixin.ObjectIcon, {
|
||||
component: testManagement.component.TestCaseStatusPresenter
|
||||
})
|
||||
|
||||
@@ -50,6 +50,7 @@ export default mergeIds(testManagementId, testManganement, {
|
||||
RunTestPlanButton: '' as AnyComponent
|
||||
},
|
||||
ids: {
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -682,6 +682,20 @@ export function createModel (builder: Builder): void {
|
||||
tracker.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: tracker.app.Tracker,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: tracker.class.Project,
|
||||
enabled: true,
|
||||
order: 10
|
||||
},
|
||||
tracker.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
chunter.class.ChatMessageViewlet,
|
||||
core.space.Model,
|
||||
|
||||
@@ -82,6 +82,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
BaseProjectType: '' as Ref<ProjectType>,
|
||||
GuestIssueClassPermission: '' as Ref<Doc>,
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>,
|
||||
IssueUpdatedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
IssueCreatedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
IssueRemovedActivityViewlet: '' as Ref<DocUpdateMessageViewlet>,
|
||||
|
||||
@@ -923,6 +923,20 @@ function defineSettings (builder: Builder): void {
|
||||
},
|
||||
training.ids.ModulePermissionGroup
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.ModulePermissionGroup,
|
||||
core.space.Model,
|
||||
{
|
||||
application: training.app.Training,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
permissions: [],
|
||||
spaceClass: core.class.TypedSpace,
|
||||
enabled: false,
|
||||
order: 25
|
||||
},
|
||||
training.ids.ModulePermissionGroupReadOnlyGuest
|
||||
)
|
||||
}
|
||||
|
||||
const columns = {
|
||||
|
||||
@@ -35,7 +35,8 @@ export default mergeIds(trainingId, training, {
|
||||
},
|
||||
ids: {
|
||||
GuestTrainingAttemptClassPermission: '' as Ref<Doc>,
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
},
|
||||
|
||||
// TODO: Move function resources declarations to plugins/*-resources
|
||||
|
||||
@@ -113,10 +113,10 @@
|
||||
--secondary-button-disabled: rgba(255, 255, 255, .12);
|
||||
--secondary-button-disabled-color: rgba(255, 255, 255, .4);
|
||||
|
||||
--theme-button-default: rgba(255, 255, 255, .02);
|
||||
--theme-button-hovered: rgba(255, 255, 255, .04);
|
||||
--theme-button-pressed: rgba(255, 255, 255, .08);
|
||||
--theme-button-focused: rgba(255, 255, 255, .04);
|
||||
--theme-button-default: rgba(255, 255, 255, .03);
|
||||
--theme-button-hovered: rgba(255, 255, 255, .07);
|
||||
--theme-button-pressed: rgba(255, 255, 255, .11);
|
||||
--theme-button-focused: rgba(255, 255, 255, .07);
|
||||
--theme-button-focused-border: rgba(255, 255, 255, .09);
|
||||
--theme-button-disabled: transparent;
|
||||
--theme-button-border: rgba(255, 255, 255, .09);
|
||||
@@ -125,8 +125,8 @@
|
||||
--theme-breadcrumb-hovered: rgba(255, 255, 255, .08);
|
||||
--theme-breadcrumb-pressed: rgba(255, 255, 255, .1);
|
||||
--theme-button-icon-default: rgba(255, 255, 255, 0);
|
||||
--theme-button-icon-hovered: rgba(255, 255, 255, .06);
|
||||
--theme-button-icon-pressed: rgba(255, 255, 255, .1);
|
||||
--theme-button-icon-hovered: rgba(255, 255, 255, .08);
|
||||
--theme-button-icon-pressed: rgba(255, 255, 255, .12);
|
||||
|
||||
--theme-button-contrast-color: #000;
|
||||
--theme-button-contrast-enabled: rgba(255, 255, 255, .8);
|
||||
@@ -136,7 +136,7 @@
|
||||
--theme-button-contrast-disabled-color: rgba(0, 0, 0, .5);
|
||||
--theme-button-contrast-border: rgba(255, 255, 255, .2);
|
||||
|
||||
--theme-button-container-color: #25262A;
|
||||
--theme-button-container-color: #22262B;
|
||||
|
||||
--theme-button-attention-gradient: linear-gradient(135deg, #7C3AED, #A855F7);
|
||||
--theme-button-attention-border: #A855F7;
|
||||
@@ -151,13 +151,13 @@
|
||||
--theme-refinput-border: rgba(255, 255, 255, .1);
|
||||
|
||||
// Be aware to update defineAlpha() function in colors.ts
|
||||
--theme-bg-color: #1A1A28;
|
||||
--theme-bg-color: #161719;
|
||||
--theme-bg-accent-color: rgba(0, 0, 0, .08);
|
||||
--theme-bg-dark-color: rgba(0, 0, 0, .2);
|
||||
--theme-back-color: #0f0f18;
|
||||
--theme-back-color: #0E0F10;
|
||||
--theme-overlay-color: rgba(0, 0, 0, .3);
|
||||
--theme-statusbar-color: #1A1928;
|
||||
--theme-navpanel-color: #14141F;
|
||||
--theme-statusbar-color: #0E0F10;
|
||||
--theme-navpanel-color: #0E0F10;
|
||||
--theme-navpanel-hovered: rgba(255, 255, 255, .04);
|
||||
--theme-navpanel-selected: rgba(255, 255, 255, .08);
|
||||
--theme-navpanel-divider: rgba(255, 255, 255, .1);
|
||||
@@ -165,9 +165,9 @@
|
||||
--theme-navpanel-icons-color: #7F7F7F;
|
||||
--theme-navpanel-icons-divider: rgba(255, 255, 255, .11);
|
||||
--theme-navpanel-shadow-mobile: drop-shadow(0 0 3px rgba(0, 0, 0, .5));
|
||||
--theme-comp-header-color: #1F1F2C;
|
||||
--theme-comp-header-color: #1E2024;
|
||||
--theme-divider-color: rgba(255, 255, 255, .06);
|
||||
--theme-bg-divider-color: #282834;
|
||||
--theme-bg-divider-color: #22242A;
|
||||
--theme-mention-bg-color: rgba(55, 122, 230, 0.1);
|
||||
--theme-mention-focused-bg-color: rgba(55, 122, 230, 0.2);
|
||||
--theme-broken-mention-color: rgba(255, 255, 255, .4);
|
||||
@@ -184,22 +184,22 @@
|
||||
|
||||
--theme-list-border-color: rgba(255, 255, 255, .05);
|
||||
--theme-list-header-color: #C88C65;
|
||||
--theme-list-subheader-color: #262634;
|
||||
--theme-list-row-color: #21212F;
|
||||
--theme-list-subheader-color: #22242A;
|
||||
--theme-list-row-color: #1D1F23;
|
||||
--theme-list-divider-color: rgba(255, 255, 255, .09);
|
||||
--theme-list-subheader-divider: rgba(255, 255, 255, .06);
|
||||
|
||||
--theme-list-button-color: #262633;
|
||||
--theme-list-button-hover: #2F2F3A;
|
||||
--theme-link-button-color: #262634;
|
||||
--theme-link-button-hover: #2F2F3B;
|
||||
--theme-list-button-color: #22242A;
|
||||
--theme-list-button-hover: #2A2C32;
|
||||
--theme-link-button-color: #22242A;
|
||||
--theme-link-button-hover: #2A2C33;
|
||||
|
||||
--theme-table-border-color: rgba(255, 255, 255, .1);
|
||||
--theme-table-header-color: #1C1C29;
|
||||
--theme-table-row-color: #21212F;
|
||||
--theme-table-header-color: #1A1C20;
|
||||
--theme-table-row-color: #1D1F23;
|
||||
|
||||
--theme-kanban-card-bg-color: rgba(222, 222, 240, .04);
|
||||
--theme-kanban-card-border: transparent;
|
||||
--theme-kanban-card-bg-color: rgba(222, 222, 240, .055);
|
||||
--theme-kanban-card-border: rgba(255, 255, 255, .06);
|
||||
--theme-kanban-card-footer: rgba(217, 217, 217, .07);
|
||||
|
||||
--theme-editbox-focus-color: rgba(255, 255, 255, .04);
|
||||
@@ -212,16 +212,16 @@
|
||||
--theme-checkbox-border: rgba(0, 0, 0, .12);
|
||||
--theme-checkbox-disabled: #999;
|
||||
--theme-progress-color: #FFFFFF;
|
||||
--theme-popup-color: #2A2939;
|
||||
--theme-popup-trans-color: #2A2939ee;
|
||||
--theme-popup-trans-gradient: linear-gradient(to bottom, #2A2939ff, #2A293900);
|
||||
--theme-popup-hover: #333240;
|
||||
--theme-popup-divider: rgba(255, 255, 255, .09);
|
||||
--theme-popup-header: #3A3A47;
|
||||
--theme-popup-color: #1F2328;
|
||||
--theme-popup-trans-color: #1F2328ee;
|
||||
--theme-popup-trans-gradient: linear-gradient(to bottom, #1F2328ff, #1F232800);
|
||||
--theme-popup-hover: #2C3139;
|
||||
--theme-popup-divider: rgba(255, 255, 255, .08);
|
||||
--theme-popup-header: #323842;
|
||||
--theme-popup-shadow: 0 0 .5rem rgba(0, 0, 0, .2);
|
||||
--theme-popup-checkicon: #FFFFFF99;
|
||||
--theme-popup-deactivated: #1A1A28;
|
||||
--theme-panel-color: #1A1A28;
|
||||
--theme-popup-deactivated: #161719;
|
||||
--theme-panel-color: #161719;
|
||||
--theme-calendar-today-color: #fff;
|
||||
--theme-calendar-holiday-color: #eb5757;
|
||||
--theme-calendar-weekend-color: rgba(242, 153, 74, 1);
|
||||
@@ -245,17 +245,17 @@
|
||||
--theme-diffview-delete-color: rgb(248, 81, 73);
|
||||
|
||||
--theme-tooltip-color: rgba(255, 255, 255, .8);
|
||||
--theme-tooltip-bg: #353347;
|
||||
--theme-tooltip-bg: #2C3139;
|
||||
--theme-tooltip-key-bg: rgba(255, 255, 255, .08);
|
||||
|
||||
--theme-inbox-notify: #F47758;
|
||||
--theme-inbox-people-notify: #2B5190;
|
||||
--theme-inbox-activity-bgcolor: #1A1A28;
|
||||
--theme-inbox-activity-bgcolor: #0E0F10;
|
||||
--theme-inbox-activitymsg-bgcolor: rgba(255, 255, 255, .03);
|
||||
--theme-inbox-activitymsg-divider: rgba(255, 255, 255, .1);
|
||||
--theme-inbox-activitymsg-border: rgba(255, 255, 255, .03);
|
||||
--theme-inbox-counter-bgcolor: rgba(255, 255, 255, .06);
|
||||
--theme-inbox-people-counter-bgcolor: rgba(43, 81, 144, .1);
|
||||
--theme-inbox-people-counter-bgcolor: rgba(32, 93, 194, .14);
|
||||
|
||||
--theme-toggle-sw-color: #fff;
|
||||
--theme-toggle-on-sw-color: #fff;
|
||||
@@ -309,13 +309,13 @@
|
||||
--theme-text-editor-palette-bg-pink: rgba(78, 44, 60, 1);
|
||||
--theme-text-editor-palette-bg-red:rgba(82, 46, 42, 1);
|
||||
|
||||
--accent-bg-color: #27282b;
|
||||
--accent-bg-color: #22262B;
|
||||
--accent-shadow: rgb(0 0 0 / 10%) 0px 2px 4px;
|
||||
|
||||
--highlight-hover: #282834;
|
||||
--highlight-select: #252b3a;
|
||||
--highlight-select-border: #44506b;
|
||||
--highlight-select-hover: #2c3346;
|
||||
--highlight-hover: #2A2E34;
|
||||
--highlight-select: #2C3139;
|
||||
--highlight-select-border: #3D4450;
|
||||
--highlight-select-hover: #323842;
|
||||
|
||||
--scrollbar-bar-color: #35354a;
|
||||
--scrollbar-bar-hover: #8a8aa5;
|
||||
@@ -330,13 +330,13 @@
|
||||
|
||||
--divider-color: #303236;
|
||||
--divider-trans-color: rgba(255, 255, 255, .12);
|
||||
--menu-bg-select: #2d2f36;
|
||||
--menu-bg-select-trans: #2d2f3665;
|
||||
--menu-bg-select: #2C3139;
|
||||
--menu-bg-select-trans: #2C313965;
|
||||
--menu-icon-hover: #f3f3f8;
|
||||
--header-bg-color: linear-gradient(0deg, var(--accent-bg-color), #2d2e31);
|
||||
--popup-bg-color: linear-gradient(136.61deg, var(--accent-bg-color) 13.72%, #2d2e31 74.3%);
|
||||
--popup-bg-hover: #37373c;
|
||||
--popup-divider: #313236;
|
||||
--header-bg-color: linear-gradient(0deg, var(--accent-bg-color), #2A2E34);
|
||||
--popup-bg-color: linear-gradient(136.61deg, var(--accent-bg-color) 13.72%, #2C3139 74.3%);
|
||||
--popup-bg-hover: #323842;
|
||||
--popup-divider: #2E333C;
|
||||
--popup-shadow: rgb(0 0 0 / 50%) 0px 4px 24px;
|
||||
--popup-panel-shadow: rgb(0 0 0 / 55%) 0px 7px 24px;
|
||||
--popup-aside-shadow: rgb(0 0 0 / 25%) 0px 8px 16px;
|
||||
@@ -381,28 +381,28 @@
|
||||
--theme-clockface-arrows-holder: radial-gradient(at top center, #2F2F3A, #555555);
|
||||
--theme-clockface-arrows-shadow: 0 0 1px white;
|
||||
|
||||
--theme-link-preview-bg-color: #2B2D41;
|
||||
--theme-link-preview-bg-color: #252A32;
|
||||
--theme-link-preview-description-color: #C1C9D6;
|
||||
--theme-link-preview-text-color:#FFFFFF;
|
||||
|
||||
--theme-dialog-border-color: rgba(255, 255, 255, 0.1);
|
||||
--theme-dialog-background-color: #2a2938;
|
||||
--theme-dialog-background-color: #1F2328;
|
||||
--theme-dialog-back-color: #848484;
|
||||
--theme-icon-stroke: #e8e9e9;
|
||||
--theme-state-ghost-color: rgba(123, 123, 123, 0.6);
|
||||
--theme-state-ghost-background-color: rgba(123, 123, 123, 0.1);
|
||||
--theme-state-ghost-border-color: transparent;
|
||||
--theme-state-primary-color: #3070dc;
|
||||
--theme-state-primary-background-color: rgba(48, 112, 220, 0.1);
|
||||
--theme-state-primary-border-color: rgba(48, 112, 220, 0.15);
|
||||
--theme-state-primary-color: #377AE6;
|
||||
--theme-state-primary-background-color: rgba(55, 122, 230, 0.14);
|
||||
--theme-state-primary-border-color: rgba(55, 122, 230, 0.26);
|
||||
--theme-state-regular-color: #7b7b7b;
|
||||
--theme-state-regular-background-color: rgba(123, 123, 123, 0.1);
|
||||
--theme-state-regular-border-color: rgba(123, 123, 123, 0.15);
|
||||
--theme-wizard-not-visited-color: #34343c;
|
||||
|
||||
--calendar-event-back-color: #2B2D41;
|
||||
--calendar-event-border-color: rgba(43, 81, 144, 0.2);
|
||||
--calendar-event-handle-color: #2b5190;
|
||||
--calendar-event-back-color: #252A32;
|
||||
--calendar-event-border-color: rgba(55, 122, 230, 0.28);
|
||||
--calendar-event-handle-color: #205DC2;
|
||||
|
||||
--theme-label-green-color: rgba(34, 197, 94, .8);
|
||||
--theme-label-green-bg-color: rgba(34, 197, 94, .08);
|
||||
|
||||
@@ -49,17 +49,17 @@
|
||||
--global-ui-active-BackgroundColor: #A5BDFF26;
|
||||
--global-ui-highlight-BackgroundColor: #A5BDFF0D;
|
||||
--global-ui-hover-highlight-BackgroundColor: #A5BDFF26;
|
||||
--global-surface-01-BackgroundColor: #131925;
|
||||
--global-surface-01-BackgroundColor: #0E0F10;
|
||||
--global-surface-01-BorderColor: #1F2737;
|
||||
--global-surface-01-hover-BackgroundColor: #19202E;
|
||||
--global-surface-02-BackgroundColor: #19202E;
|
||||
--global-surface-01-hover-BackgroundColor: #121314;
|
||||
--global-surface-02-BackgroundColor: #161719;
|
||||
--global-surface-02-BorderColor: #262F40;
|
||||
--global-surface-03-hover-BackgroundColor: #19202E;
|
||||
--global-surface-03-hover-BackgroundColor: #1A1C1F;
|
||||
--global-subtle-ui-BorderColor: #A5BDFF0D;
|
||||
--global-subtle-BackgroundColor: #072790;
|
||||
--global-popover-BackgroundColor: #262F40;
|
||||
--global-popover-hover-BackgroundColor: #1F2737;
|
||||
--global-popover-BorderColor: #A5BDFF1A;
|
||||
--global-popover-BackgroundColor: #1F2328;
|
||||
--global-popover-hover-BackgroundColor: #2C3139;
|
||||
--global-popover-BorderColor: #FFFFFF14;
|
||||
--global-primary-LinkColor: #4D7FF5;
|
||||
--global-primary-IconColor: #ffffff;
|
||||
--global-primary-TextColor: #FFFFFF;
|
||||
@@ -73,7 +73,7 @@
|
||||
--global-popover-ShadowColor: #0E131E59;
|
||||
--global-modal-ShadowColor: #0E131E73;
|
||||
--global-accent-SkyText: #B9D1F5;
|
||||
--global-accent-BackgroundColor: #204DC8;
|
||||
--global-accent-BackgroundColor: #205DC2;
|
||||
--global-on-nuance-TextColor: #041d7d;
|
||||
|
||||
--global-no-priority-PriorityColor: #8E99AF;
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
--icon-disabled-IconColor: #394358;
|
||||
|
||||
--border-talk-indication-secondary: #25262A;
|
||||
--border-talk-indication-secondary: #22262B;
|
||||
|
||||
/** Buttons **/
|
||||
--button-subtle-LabelColor: #fff;
|
||||
|
||||
@@ -212,7 +212,7 @@ export const avatarDarkColors = Object.freeze<ColorDefinition[]>([
|
||||
])
|
||||
|
||||
export function defaultBackground (dark: boolean): string {
|
||||
return dark ? 'linear-gradient(90deg, #262634, #1A1A28)' : '#FFFFFF'
|
||||
return dark ? 'linear-gradient(90deg, #22242A, #161719)' : '#FFFFFF'
|
||||
}
|
||||
|
||||
export const FeijoaColor = '#A5D179'
|
||||
@@ -418,7 +418,7 @@ export function defineAlpha (dark: boolean, color: string, percent = 100): strin
|
||||
|
||||
const p1 = percent / 100
|
||||
|
||||
const bg = dark ? hexToRgb('#1A1A28') : hexToRgb('#F1F1F4')
|
||||
const bg = dark ? hexToRgb('#161719') : hexToRgb('#F1F1F4')
|
||||
|
||||
return rgbToHex({
|
||||
r: r * p1 + bg.r * (1 - p1),
|
||||
|
||||
@@ -80,10 +80,11 @@
|
||||
background-color: var(--global-accent-BackgroundColor);
|
||||
|
||||
.icon {
|
||||
color: var(--global-on-nuance-TextColor);
|
||||
color: var(--global-on-accent-TextColor);
|
||||
}
|
||||
span {
|
||||
color: var(--global-on-nuance-TextColor);
|
||||
color: var(--global-on-accent-TextColor);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
&:checked + .switcher-element.subtle {
|
||||
|
||||
@@ -226,7 +226,8 @@ const cardPlugin = plugin(cardId, {
|
||||
ids: {
|
||||
CardWidget: '' as Ref<Doc>,
|
||||
GuestCardClassPermission: '' as Ref<Doc>,
|
||||
ModulePermissionGroup: '' as Ref<Doc>
|
||||
ModulePermissionGroup: '' as Ref<Doc>,
|
||||
ModulePermissionGroupReadOnlyGuest: '' as Ref<Doc>
|
||||
},
|
||||
component: {
|
||||
LabelsPresenter: '' as AnyComponent,
|
||||
|
||||
@@ -863,9 +863,9 @@ export async function subProcessMatchCheck (
|
||||
params: Record<string, any>,
|
||||
context: Record<string, any>
|
||||
): Promise<boolean> {
|
||||
const { process: _process, ...otherCritera } = params
|
||||
const { process: _process, currentState } = params
|
||||
if (_process === undefined) return false
|
||||
if (Object.keys(otherCritera).length === 0) return true
|
||||
if (currentState === undefined) return true
|
||||
|
||||
const subProcesses = await client.findAll(process.class.Execution, {
|
||||
parentId: execution._id,
|
||||
@@ -874,18 +874,18 @@ export async function subProcessMatchCheck (
|
||||
|
||||
if (subProcesses.length === 0) return false
|
||||
|
||||
const [predicate, value] = Object.entries(otherCritera)[0]
|
||||
const [predicate, value] = Object.entries(currentState)[0]
|
||||
|
||||
const res = matchQuery(
|
||||
subProcesses,
|
||||
{ currentState: { $in: value } },
|
||||
{ currentState: { $in: value as any } },
|
||||
process.class.Execution,
|
||||
client.getHierarchy(),
|
||||
true
|
||||
)
|
||||
if (predicate === '$all') {
|
||||
return res.length === subProcesses.length
|
||||
} else if (predicate === '$any') {
|
||||
} else if (predicate === '$in') {
|
||||
return res.length > 0
|
||||
} else if (predicate === '$nin') {
|
||||
return res.length === 0
|
||||
|
||||
@@ -15,6 +15,17 @@
|
||||
<path d="M9.6,16.2c-2.1,0-3.9-1.9-4-4.3c-0.1-1.2,0.3-2.3,1-3.1c0.8-0.8,1.8-1.2,3-1.2c1.2,0,2.2,0.4,3,1.3c0.8,0.8,1.1,1.9,1.1,3.1C13.5,14.3,11.7,16.2,9.6,16.2z M9.6,9.5C9,9.5,8.4,9.7,8,10.1c-0.4,0.4-0.6,1-0.5,1.7c0.1,1.4,1.1,2.5,2.2,2.5s2.1-1.2,2.2-2.5c0-0.7-0.2-1.3-0.6-1.7C10.9,9.7,10.3,9.5,9.6,9.5z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2,22.5c0.9-3.5,4.3-5.2,7.6-5.2c1.3,0,2.6,0.2,3.8,0.8c0.5,0.2,0.7,0.8,0.5,1.2c-0.2,0.5-0.8,0.7-1.2,0.5c-0.9-0.4-1.9-0.6-3.1-0.6c-2.6,0-4.9,1.2-5.7,3.4H10c0.5,0,0.9,0.4,0.9,0.9s-0.4,0.9-0.9,0.9H3.5C2.4,24.4,1.8,23.3,2,22.5L2,22.5z" />
|
||||
</symbol>
|
||||
<symbol id="guestPermissions" viewBox="2 0 30 32">
|
||||
<!-- accountSettings paths, scaled from (8, 8) -->
|
||||
<g transform="translate(11, 15.5) scale(1.7) translate(-8, -8)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 8C9.65685 8 11 6.65685 11 5C11 3.34315 9.65685 2 8 2C6.34315 2 5 3.34315 5 5C5 6.65685 6.34315 8 8 8ZM8 9C10.2091 9 12 7.20914 12 5C12 2.79086 10.2091 1 8 1C5.79086 1 4 2.79086 4 5C4 7.20914 5.79086 9 8 9Z" />
|
||||
<path d="M6.5 10C4.567 10 3 11.567 3 13.5V14.5C3 14.7761 3.22386 15 3.5 15C3.77614 15 4 14.7761 4 14.5V13.5C4 12.1193 5.11929 11 6.5 11H9.5C10.8807 11 12 12.1193 12 13.5V14.5C12 14.7761 12.2239 15 12.5 15C12.7761 15 13 14.7761 13 14.5V13.5C13 11.567 11.433 10 9.5 10H6.5Z" />
|
||||
</g>
|
||||
<!-- Small lock badge, lower-right near shoulders -->
|
||||
<g transform="translate(19, 13)">
|
||||
<path d="M12,7.1h-0.7V5.4c0-1.8-1.5-3.3-3.3-3.3c-1.8,0-3.3,1.5-3.3,3.3v1.7H4c-0.8,0-1.5,0.7-1.5,1.5v4.1c0,0.8,0.7,1.5,1.5,1.5h8 c0.8,0,1.5-0.7,1.5-1.5V8.6C13.5,7.8,12.8,7.1,12,7.1z M5.7,5.4c0-1.2,1-2.3,2.3-2.3s2.3,1,2.3,2.3v1.7H5.7V5.4z M12.5,12.7 c0,0.3-0.2,0.5-0.5,0.5H4c-0.3,0-0.5-0.2-0.5-0.5V8.6c0-0.3,0.2-0.5,0.5-0.5h8c0.3,0,0.5,0.2,0.5,0.5V12.7z" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="password" viewBox="0 0 16 16">
|
||||
<path d="M11 6C11.5523 6 12 5.55228 12 5C12 4.44772 11.5523 4 11 4C10.4477 4 10 4.44772 10 5C10 5.55228 10.4477 6 11 6Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 1C8.01472 1 6 3.01472 6 5.5C6 5.89536 6.05111 6.27942 6.14726 6.64563L1.29289 11.5C1.10536 11.6875 1 11.9419 1 12.2071V14.5C1 14.7761 1.22386 15 1.5 15H3.79289C4.05811 15 4.31246 14.8946 4.5 14.7071L9.35437 9.85274C9.72058 9.94889 10.1046 10 10.5 10C12.9853 10 15 7.98528 15 5.5C15 3.01472 12.9853 1 10.5 1ZM7 5.5C7 3.567 8.567 2 10.5 2C12.433 2 14 3.567 14 5.5C14 7.433 12.433 9 10.5 9C10.1048 9 9.72588 8.93469 9.37284 8.81468C9.19252 8.75339 8.99304 8.79986 8.85837 8.93453L8 9.79289L7.85355 9.64645C7.65829 9.45118 7.34171 9.45118 7.14645 9.64645C6.95118 9.84171 6.95118 10.1583 7.14645 10.3536L7.29289 10.5L6.5 11.2929L5.85355 10.6464C5.65829 10.4512 5.34171 10.4512 5.14645 10.6464C4.95118 10.8417 4.95118 11.1583 5.14645 11.3536L5.79289 12L5 12.7929L4.35355 12.1464C4.15829 11.9512 3.84171 11.9512 3.64645 12.1464C3.45118 12.3417 3.45118 12.6583 3.64645 12.8536L4.29289 13.5L3.79289 14H2V12.2071L7.06547 7.14163C7.20014 7.00696 7.24661 6.80748 7.18532 6.62716C7.06531 6.27412 7 5.8952 7 5.5Z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
@@ -212,9 +212,12 @@
|
||||
"OfficeDefaultSettings": "Výchozí nastavení pro jednací místnosti",
|
||||
"DefaultStartWithTranscription": "Povolit přepis v nových kancelářích",
|
||||
"DefaultStartWithRecording": "Povolit záznam v nových kancelářích",
|
||||
"GuestPermissionsSettings": "Oprávnění hostů",
|
||||
"GuestPermissionsModulePermissions": "Oprávnění modulů",
|
||||
"GuestPermissionsModulePermissionsHint": "Vyberte, které moduly mohou hosté používat, a níže upravte oprávnění pro jednotlivé aplikace.",
|
||||
"GuestPermissionsSettings": "Hosté",
|
||||
"GuestPermissionsApplicationPermissions": "Oprávnění aplikací",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Vyberte, které aplikace mohou hosté používat, a níže upravte oprávnění pro jednotlivé aplikace.",
|
||||
"GuestPermissionsTabGuest": "Host",
|
||||
"GuestPermissionsTabAnonymousGuest": "Anonymní host",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Přístup k aplikacím pro anonymní (pouze pro čtení) hosty. Které aplikace uvidí, může záviset i na konfiguraci nasazení.",
|
||||
"ImportDocumentPermission": "Importovat dokumenty",
|
||||
"ImportDocumentDescription": "Umožňuje uživatelům importovat dokumenty do pracovního prostoru",
|
||||
"SelectUsers": "Vybrat uživatele",
|
||||
|
||||
@@ -214,9 +214,12 @@
|
||||
"OfficeDefaultSettings": "Standardeinstellungen für Besprechungsräume",
|
||||
"DefaultStartWithTranscription": "Transkription in neuen Büroräumen aktivieren",
|
||||
"DefaultStartWithRecording": "Aufnahme in neuen Büroräumen aktivieren",
|
||||
"GuestPermissionsSettings": "Gastberechtigungen",
|
||||
"GuestPermissionsModulePermissions": "Modulberechtigungen",
|
||||
"GuestPermissionsModulePermissionsHint": "Wählen Sie, welche Module Gäste nutzen dürfen, und passen Sie unten die Berechtigungen für jede App an.",
|
||||
"GuestPermissionsSettings": "Gäste",
|
||||
"GuestPermissionsApplicationPermissions": "Anwendungsberechtigungen",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Wählen Sie, welche Anwendungen Gäste nutzen dürfen, und passen Sie unten die Berechtigungen für jede Anwendung an.",
|
||||
"GuestPermissionsTabGuest": "Gast",
|
||||
"GuestPermissionsTabAnonymousGuest": "Anonymer Gast",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Anwendungszugriff für anonyme (nur lesende) Gäste. Welche Anwendungen sie sehen, kann auch von der Bereitstellungskonfiguration abhängen.",
|
||||
"ImportDocumentPermission": "Dokumente importieren",
|
||||
"ImportDocumentDescription": "Gewährt Benutzern die Möglichkeit, Dokumente in den Arbeitsbereich zu importieren",
|
||||
"SelectUsers": "Benutzer auswählen",
|
||||
|
||||
@@ -215,9 +215,12 @@
|
||||
"OfficeDefaultSettings": "Default settings for meeting rooms",
|
||||
"DefaultStartWithTranscription": "Enable transcription in new office rooms",
|
||||
"DefaultStartWithRecording": "Enable recording in new office rooms",
|
||||
"GuestPermissionsSettings": "Guest permissions",
|
||||
"GuestPermissionsModulePermissions": "Module permissions",
|
||||
"GuestPermissionsModulePermissionsHint": "Choose which modules guests can use, then adjust permissions for each app below.",
|
||||
"GuestPermissionsSettings": "Guests",
|
||||
"GuestPermissionsApplicationPermissions": "Application permissions",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Choose which applications guests can use, then adjust permissions for each application below.",
|
||||
"GuestPermissionsTabGuest": "Guest",
|
||||
"GuestPermissionsTabAnonymousGuest": "Anonymous guest",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Application access for anonymous (read-only) guests. Which applications they see can also depend on deployment configuration.",
|
||||
"ImportDocumentPermission": "Import documents",
|
||||
"ImportDocumentDescription": "Grants users ability to import documents into the workspace",
|
||||
"SelectUsers": "Select users",
|
||||
|
||||
@@ -205,9 +205,12 @@
|
||||
"OfficeDefaultSettings": "Configuración predeterminada para salas de reuniones",
|
||||
"DefaultStartWithTranscription": "Habilitar transcripción en nuevas oficinas",
|
||||
"DefaultStartWithRecording": "Habilitar grabación en nuevas oficinas",
|
||||
"GuestPermissionsSettings": "Permisos de invitados",
|
||||
"GuestPermissionsModulePermissions": "Permisos de módulos",
|
||||
"GuestPermissionsModulePermissionsHint": "Elija qué módulos pueden usar los invitados y luego ajuste los permisos de cada aplicación a continuación.",
|
||||
"GuestPermissionsSettings": "Invitados",
|
||||
"GuestPermissionsApplicationPermissions": "Permisos de aplicaciones",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Elija qué aplicaciones pueden usar los invitados y luego ajuste los permisos de cada aplicación a continuación.",
|
||||
"GuestPermissionsTabGuest": "Invitado",
|
||||
"GuestPermissionsTabAnonymousGuest": "Invitado anónimo",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Acceso a aplicaciones para invitados anónimos (solo lectura). Las aplicaciones que ven también pueden depender de la configuración de despliegue.",
|
||||
"ImportDocumentPermission": "Importar documentos",
|
||||
"ImportDocumentDescription": "Otorga a los usuarios la capacidad de importar documentos al espacio de trabajo",
|
||||
"SelectUsers": "Seleccionar usuarios",
|
||||
|
||||
@@ -214,9 +214,12 @@
|
||||
"OfficeDefaultSettings": "Paramètres par défaut pour les salles de réunion",
|
||||
"DefaultStartWithTranscription": "Activer la transcription dans les nouveaux bureaux",
|
||||
"DefaultStartWithRecording": "Activer l'enregistrement dans les nouveaux bureaux",
|
||||
"GuestPermissionsSettings": "Permissions des invités",
|
||||
"GuestPermissionsModulePermissions": "Permissions des modules",
|
||||
"GuestPermissionsModulePermissionsHint": "Choisissez les modules accessibles aux invités, puis ajustez les permissions pour chaque application ci-dessous.",
|
||||
"GuestPermissionsSettings": "Invités",
|
||||
"GuestPermissionsApplicationPermissions": "Permissions des applications",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Choisissez les applications accessibles aux invités, puis ajustez les permissions pour chaque application ci-dessous.",
|
||||
"GuestPermissionsTabGuest": "Invité",
|
||||
"GuestPermissionsTabAnonymousGuest": "Invité anonyme",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Accès aux applications pour les invités anonymes (lecture seule). Les applications visibles peuvent aussi dépendre de la configuration de déploiement.",
|
||||
"ImportDocumentPermission": "Importer des documents",
|
||||
"ImportDocumentDescription": "Accorde aux utilisateurs la possibilité d'importer des documents dans l'espace de travail",
|
||||
"SelectUsers": "Sélectionner des utilisateurs",
|
||||
|
||||
@@ -214,9 +214,12 @@
|
||||
"OfficeDefaultSettings": "Impostazioni predefinite per le sale riunioni",
|
||||
"DefaultStartWithTranscription": "Abilita trascrizione nelle nuove stanze dell'ufficio",
|
||||
"DefaultStartWithRecording": "Abilita registrazione nelle nuove stanze dell'ufficio",
|
||||
"GuestPermissionsSettings": "Permessi ospite",
|
||||
"GuestPermissionsModulePermissions": "Permessi dei moduli",
|
||||
"GuestPermissionsModulePermissionsHint": "Scegli quali moduli possono usare gli ospiti, poi regola i permessi per ogni app qui sotto.",
|
||||
"GuestPermissionsSettings": "Ospiti",
|
||||
"GuestPermissionsApplicationPermissions": "Permessi delle applicazioni",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Scegli quali applicazioni possono usare gli ospiti, poi regola i permessi per ogni applicazione qui sotto.",
|
||||
"GuestPermissionsTabGuest": "Ospite",
|
||||
"GuestPermissionsTabAnonymousGuest": "Ospite anonimo",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Accesso alle applicazioni per ospiti anonimi (sola lettura). Le applicazioni visibili possono dipendere anche dalla configurazione di distribuzione.",
|
||||
"ImportDocumentPermission": "Importa documenti",
|
||||
"ImportDocumentDescription": "Concede agli utenti la possibilità di importare documenti nell'area di lavoro",
|
||||
"SelectUsers": "Seleziona utenti",
|
||||
|
||||
@@ -214,9 +214,12 @@
|
||||
"OfficeDefaultSettings": "会議室のデフォルト設定",
|
||||
"DefaultStartWithTranscription": "新しいオフィスルームで文字起こしを有効にする",
|
||||
"DefaultStartWithRecording": "新しいオフィスルームで録画を有効にする",
|
||||
"GuestPermissionsSettings": "ゲストの権限",
|
||||
"GuestPermissionsModulePermissions": "モジュールの権限",
|
||||
"GuestPermissionsModulePermissionsHint": "ゲストが利用できるモジュールを選び、その下で各アプリの権限を調整します。",
|
||||
"GuestPermissionsSettings": "ゲスト",
|
||||
"GuestPermissionsApplicationPermissions": "アプリケーションの権限",
|
||||
"GuestPermissionsApplicationPermissionsHint": "ゲストが利用できるアプリケーションを選び、その下で各アプリケーションの権限を調整します。",
|
||||
"GuestPermissionsTabGuest": "ゲスト",
|
||||
"GuestPermissionsTabAnonymousGuest": "匿名ゲスト",
|
||||
"GuestPermissionsAnonymousApplicationHint": "匿名(読み取り専用)ゲストのアプリケーションアクセス。表示されるアプリケーションはデプロイ設定にも依存する場合があります。",
|
||||
"ImportDocumentPermission": "ドキュメントをインポート",
|
||||
"ImportDocumentDescription": "ユーザーにワークスペースにドキュメントをインポートする機能を付与します",
|
||||
"SelectUsers": "ユーザーを選択",
|
||||
|
||||
@@ -205,9 +205,12 @@
|
||||
"OfficeDefaultSettings": "Configurações padrão para salas de reunião",
|
||||
"DefaultStartWithTranscription": "Habilitar transcrição em novos escritórios",
|
||||
"DefaultStartWithRecording": "Habilitar gravação em novos escritórios",
|
||||
"GuestPermissionsSettings": "Permissões de convidados",
|
||||
"GuestPermissionsModulePermissions": "Permissões de módulos",
|
||||
"GuestPermissionsModulePermissionsHint": "Escolha quais módulos os convidados podem usar e, em seguida, ajuste as permissões de cada aplicativo abaixo.",
|
||||
"GuestPermissionsSettings": "Convidados",
|
||||
"GuestPermissionsApplicationPermissions": "Permissões de aplicativos",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Escolha quais aplicativos os convidados podem usar e, em seguida, ajuste as permissões de cada aplicativo abaixo.",
|
||||
"GuestPermissionsTabGuest": "Convidado",
|
||||
"GuestPermissionsTabAnonymousGuest": "Convidado anônimo",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Acesso a aplicativos para convidados anônimos (somente leitura). Os aplicativos visíveis também podem depender da configuração de implantação.",
|
||||
"ImportDocumentPermission": "Importar documentos",
|
||||
"ImportDocumentDescription": "Concede aos usuários a capacidade de importar documentos para o espaço de trabalho",
|
||||
"SelectUsers": "Selecionar usuários",
|
||||
|
||||
@@ -205,9 +205,12 @@
|
||||
"OfficeDefaultSettings": "Configurações padrão para salas de reunião",
|
||||
"DefaultStartWithTranscription": "Habilitar transcrição em novos escritórios",
|
||||
"DefaultStartWithRecording": "Habilitar gravação em novos escritórios",
|
||||
"GuestPermissionsSettings": "Permissões de convidados",
|
||||
"GuestPermissionsModulePermissions": "Permissões de módulos",
|
||||
"GuestPermissionsModulePermissionsHint": "Escolha quais módulos os convidados podem usar e, em seguida, ajuste as permissões de cada aplicação abaixo.",
|
||||
"GuestPermissionsSettings": "Convidados",
|
||||
"GuestPermissionsApplicationPermissions": "Permissões de aplicações",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Escolha quais aplicações os convidados podem usar e, em seguida, ajuste as permissões de cada aplicação abaixo.",
|
||||
"GuestPermissionsTabGuest": "Convidado",
|
||||
"GuestPermissionsTabAnonymousGuest": "Convidado anónimo",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Acesso a aplicações para convidados anónimos (só leitura). As aplicações visíveis também podem depender da configuração de implementação.",
|
||||
"ImportDocumentPermission": "Importar documentos",
|
||||
"ImportDocumentDescription": "Concede aos usuários a capacidade de importar documentos para o espaço de trabalho",
|
||||
"SelectUsers": "Selecionar usuários",
|
||||
|
||||
@@ -215,9 +215,12 @@
|
||||
"OfficeDefaultSettings": "Настройки по умолчанию для переговорных",
|
||||
"DefaultStartWithTranscription": "Включить транскрипцию в новых комнатах",
|
||||
"DefaultStartWithRecording": "Включить запись в новых комнатах",
|
||||
"GuestPermissionsSettings": "Права гостей",
|
||||
"GuestPermissionsModulePermissions": "Права модулей",
|
||||
"GuestPermissionsModulePermissionsHint": "Выберите, какие модули доступны гостям, затем настройте права для каждого приложения ниже.",
|
||||
"GuestPermissionsSettings": "Гости",
|
||||
"GuestPermissionsApplicationPermissions": "Права приложений",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Выберите, какие приложения доступны гостям, затем настройте права для каждого приложения ниже.",
|
||||
"GuestPermissionsTabGuest": "Гость",
|
||||
"GuestPermissionsTabAnonymousGuest": "Анонимный гость",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Доступ к приложениям для анонимных гостей (только чтение). Видимые приложения также могут зависеть от конфигурации развёртывания.",
|
||||
"ImportDocumentPermission": "Импорт документов",
|
||||
"ImportDocumentDescription": "Предоставляет пользователям возможность импортировать документы в рабочее пространство",
|
||||
"SelectUsers": "Выбрать пользователей",
|
||||
|
||||
@@ -214,9 +214,12 @@
|
||||
"OfficeDefaultSettings": "Toplantı odaları için varsayılan ayarlar",
|
||||
"DefaultStartWithTranscription": "Yeni ofis odalarında transkripsiyonu etkinleştir",
|
||||
"DefaultStartWithRecording": "Yeni ofis odalarında kaydı etkinleştir",
|
||||
"GuestPermissionsSettings": "Misafir izinleri",
|
||||
"GuestPermissionsModulePermissions": "Modül izinleri",
|
||||
"GuestPermissionsModulePermissionsHint": "Misafirlerin hangi modülleri kullanabileceğini seçin, ardından her uygulama için izinleri aşağıdan ayarlayın.",
|
||||
"GuestPermissionsSettings": "Misafirler",
|
||||
"GuestPermissionsApplicationPermissions": "Uygulama izinleri",
|
||||
"GuestPermissionsApplicationPermissionsHint": "Misafirlerin hangi uygulamaları kullanabileceğini seçin, ardından her uygulama için izinleri aşağıdan ayarlayın.",
|
||||
"GuestPermissionsTabGuest": "Misafir",
|
||||
"GuestPermissionsTabAnonymousGuest": "Anonim misafir",
|
||||
"GuestPermissionsAnonymousApplicationHint": "Anonim (salt okunur) misafirler için uygulama erişimi. Gördükleri uygulamalar da dağıtım yapılandırmasına bağlı olabilir.",
|
||||
"ImportDocumentPermission": "Belgeleri içe aktar",
|
||||
"ImportDocumentDescription": "Kullanıcılara çalışma alanına belge içe aktarma yeteneği verir",
|
||||
"SelectUsers": "Kullanıcıları seç",
|
||||
|
||||
@@ -214,9 +214,12 @@
|
||||
"OfficeDefaultSettings": "会议室的默认设置",
|
||||
"DefaultStartWithTranscription": "在新办公室启用转录",
|
||||
"DefaultStartWithRecording": "在新办公室启用录制",
|
||||
"GuestPermissionsSettings": "访客权限",
|
||||
"GuestPermissionsModulePermissions": "模块权限",
|
||||
"GuestPermissionsModulePermissionsHint": "选择访客可以使用哪些模块,然后在下方调整每个应用的权限。",
|
||||
"GuestPermissionsSettings": "访客",
|
||||
"GuestPermissionsApplicationPermissions": "应用权限",
|
||||
"GuestPermissionsApplicationPermissionsHint": "选择访客可以使用哪些应用,然后在下方调整每个应用的权限。",
|
||||
"GuestPermissionsTabGuest": "访客",
|
||||
"GuestPermissionsTabAnonymousGuest": "匿名访客",
|
||||
"GuestPermissionsAnonymousApplicationHint": "匿名(只读)访客的应用访问。他们可见的应用也可能取决于部署配置。",
|
||||
"ImportDocumentPermission": "导入文档",
|
||||
"ImportDocumentDescription": "授予用户将文档导入工作区的权限",
|
||||
"SelectUsers": "选择用户",
|
||||
|
||||
@@ -21,6 +21,7 @@ loadMetadata(setting.icon, {
|
||||
AccountSettings: `${icons}#accountSettings`,
|
||||
Owners: `${icons}#owners`,
|
||||
Members: `${icons}#members`,
|
||||
GuestPermissions: `${icons}#guestPermissions`,
|
||||
Password: `${icons}#password`,
|
||||
Setting: `${icons}#settings`,
|
||||
Integrations: `${icons}#integration`,
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
DropdownLabelsIntl,
|
||||
EditBox,
|
||||
Label,
|
||||
NestedDropdown
|
||||
NestedDropdown,
|
||||
Toggle
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import card from '@hcengineering/card'
|
||||
@@ -96,6 +97,7 @@
|
||||
let classBRef: Ref<Class<Doc>> | undefined = undefined
|
||||
let nameA = association.nameA
|
||||
let nameB = association.nameB
|
||||
let automationOnly = association.automationOnly ?? false
|
||||
|
||||
$: classA = isEmptyClass(classARef) ? undefined : hierarchy.getClass(classARef as Ref<Class<Doc>>)
|
||||
$: classB = isEmptyClass(classBRef) ? undefined : hierarchy.getClass(classBRef as Ref<Class<Doc>>)
|
||||
@@ -113,6 +115,7 @@
|
||||
classARef = !isEmptyClass(association.classA) ? association.classA : undefined
|
||||
nameA = association.nameA
|
||||
nameB = association.nameB
|
||||
automationOnly = association.automationOnly ?? false
|
||||
}
|
||||
|
||||
function isAssociation (data: Data<Association> | Association): data is Association {
|
||||
@@ -128,7 +131,8 @@
|
||||
if (association !== undefined && isAssociation(association)) {
|
||||
await client.diffUpdate(association, {
|
||||
nameA,
|
||||
nameB
|
||||
nameB,
|
||||
automationOnly
|
||||
})
|
||||
} else {
|
||||
await client.createDoc(core.class.Association, core.space.Model, {
|
||||
@@ -136,7 +140,8 @@
|
||||
classB: classBRef,
|
||||
type: mode,
|
||||
nameA,
|
||||
nameB
|
||||
nameB,
|
||||
automationOnly
|
||||
})
|
||||
dispatch('create')
|
||||
dispatch('close')
|
||||
@@ -224,6 +229,13 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-col p-4 flex-gap-2">
|
||||
<span class="label">
|
||||
<Label label={view.string.AutomationOnly} />
|
||||
</span>
|
||||
<Toggle bind:on={automationOnly} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-col-reverse">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import core, { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import presentation, { Card, getClient } from '@hcengineering/presentation'
|
||||
import { DropdownIntlItem, DropdownLabelsIntl, EditBox, Label, NestedDropdown } from '@hcengineering/ui'
|
||||
import { DropdownIntlItem, DropdownLabelsIntl, EditBox, Label, NestedDropdown, Toggle } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import setting from '../plugin'
|
||||
@@ -96,6 +96,7 @@
|
||||
let classBRef: Ref<Class<Doc>> | undefined = undefined
|
||||
let nameA: string = ''
|
||||
let nameB: string = ''
|
||||
let automationOnly = false
|
||||
|
||||
async function save (): Promise<void> {
|
||||
if (classBRef === undefined || classARef === undefined) {
|
||||
@@ -106,7 +107,8 @@
|
||||
classB: classBRef,
|
||||
type: mode,
|
||||
nameA,
|
||||
nameB
|
||||
nameB,
|
||||
automationOnly
|
||||
})
|
||||
dispatch('close', _id)
|
||||
}
|
||||
@@ -200,5 +202,12 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex p-4 flex-gap-2">
|
||||
<span class="label">
|
||||
<Label label={view.string.AutomationOnly} />
|
||||
</span>
|
||||
<Toggle bind:on={automationOnly} />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import core, { Association } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel, IntlString } from '@hcengineering/platform'
|
||||
import presentation, { getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import { Button, DropdownIntlItem, EditBox, IconDelete, Label, Modal, showPopup } from '@hcengineering/ui'
|
||||
import { Button, DropdownIntlItem, EditBox, IconDelete, Label, Modal, showPopup, Toggle } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import setting from '../plugin'
|
||||
import { clearSettingsStore } from '../store'
|
||||
@@ -30,11 +30,13 @@
|
||||
const classB = hierarchy.getClass(association.classB)
|
||||
let nameA = association.nameA
|
||||
let nameB = association.nameB
|
||||
let automationOnly = association.automationOnly ?? false
|
||||
|
||||
async function save (): Promise<void> {
|
||||
await client.diffUpdate(association, {
|
||||
nameA,
|
||||
nameB
|
||||
nameB,
|
||||
automationOnly
|
||||
})
|
||||
clearSettingsStore()
|
||||
}
|
||||
@@ -108,5 +110,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-between p-4 flex-gap-2">
|
||||
<span class="label">
|
||||
<Label label={view.string.AutomationOnly} />
|
||||
</span>
|
||||
<Toggle bind:on={automationOnly} />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -13,30 +13,16 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import card, { Card } from '@hcengineering/card'
|
||||
import chat from '@hcengineering/chat'
|
||||
import communication, { GuestCommunicationSettings } from '@hcengineering/communication'
|
||||
import contact, { AvatarType, ensureEmployeeForPerson } from '@hcengineering/contact'
|
||||
import { AvatarType } from '@hcengineering/contact'
|
||||
import { EditableAvatar, getAccountClient } from '@hcengineering/contact-resources'
|
||||
import core, {
|
||||
type Account,
|
||||
AccountRole,
|
||||
AccountUuid,
|
||||
Configuration,
|
||||
getCurrentAccount,
|
||||
pickPrimarySocialId,
|
||||
readOnlyGuestAccountUuid,
|
||||
Ref,
|
||||
WorkspaceAccountPermission
|
||||
} from '@hcengineering/core'
|
||||
import core, { Configuration, WorkspaceAccountPermission } from '@hcengineering/core'
|
||||
import { loginId } from '@hcengineering/login'
|
||||
import { translateCB } from '@hcengineering/platform'
|
||||
import { createQuery, getClient, MessageBox, uiContext } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import { WorkspaceSetting } from '@hcengineering/setting'
|
||||
import {
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Component,
|
||||
deviceOptionsStore as deviceInfo,
|
||||
DropdownLabels,
|
||||
type DropdownTextItem,
|
||||
@@ -65,8 +51,6 @@
|
||||
let oldName: string
|
||||
let name: string = ''
|
||||
let workspaceUrl = ''
|
||||
let allowReadOnlyGuests: boolean
|
||||
let allowGuestSignUp: boolean
|
||||
let passwordAgingRule: number | undefined = undefined
|
||||
|
||||
const accountClient = getAccountClient()
|
||||
@@ -87,8 +71,6 @@
|
||||
workspaceUrl = res.url
|
||||
oldName = res.name
|
||||
name = oldName
|
||||
allowReadOnlyGuests = res.allowReadOnlyGuest ?? false
|
||||
allowGuestSignUp = res.allowGuestSignUp ?? false
|
||||
passwordAgingRule = res.passwordAgingRule ?? undefined
|
||||
loading = false
|
||||
}
|
||||
@@ -166,40 +148,6 @@
|
||||
}
|
||||
)
|
||||
|
||||
async function handleToggleReadonlyAccess (e: CustomEvent<boolean>): Promise<void> {
|
||||
const enabled = e.detail
|
||||
const guestUserInfo = await accountClient.updateAllowReadOnlyGuests(enabled)
|
||||
allowReadOnlyGuests = enabled
|
||||
if (guestUserInfo !== undefined) {
|
||||
const guestAccount: Account = {
|
||||
uuid: guestUserInfo.guestPerson.uuid as AccountUuid,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
primarySocialId: pickPrimarySocialId(guestUserInfo.guestSocialIds)._id,
|
||||
socialIds: guestUserInfo.guestSocialIds.map((si) => si._id),
|
||||
fullSocialIds: guestUserInfo.guestSocialIds
|
||||
}
|
||||
const myAccount = getCurrentAccount()
|
||||
const ctx = uiContext.newChild('connect', {})
|
||||
await ensureEmployeeForPerson(
|
||||
ctx,
|
||||
myAccount,
|
||||
guestAccount,
|
||||
client,
|
||||
guestUserInfo.guestSocialIds,
|
||||
guestUserInfo.guestPerson
|
||||
)
|
||||
} else {
|
||||
const readonlyEmployee = await client.findOne(contact.mixin.Employee, { personUuid: readOnlyGuestAccountUuid })
|
||||
if (readonlyEmployee !== undefined) {
|
||||
await client.update(readonlyEmployee, { active: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleToggleGuestSignUp (e: CustomEvent<boolean>): Promise<void> {
|
||||
await accountClient.updateAllowGuestSignUp(e.detail)
|
||||
}
|
||||
|
||||
async function changePasswordAgingRules (val: number | undefined): Promise<void> {
|
||||
passwordAgingRule = Math.max(val ?? 1, 1)
|
||||
await accountClient.updatePasswordAgingRule(passwordAgingRule)
|
||||
@@ -253,29 +201,6 @@
|
||||
}
|
||||
)
|
||||
|
||||
let existingGuestChatSettings: GuestCommunicationSettings | undefined = undefined
|
||||
const query = createQuery()
|
||||
|
||||
$: query.query(communication.class.GuestCommunicationSettings, {}, (settings) => {
|
||||
existingGuestChatSettings = settings[0]
|
||||
})
|
||||
|
||||
async function onAllowedCardsChange (value: Ref<Card>[]): Promise<void> {
|
||||
if (existingGuestChatSettings === undefined) {
|
||||
await client.createDoc(communication.class.GuestCommunicationSettings, core.space.Workspace, {
|
||||
allowedCards: value,
|
||||
enabled: true
|
||||
})
|
||||
} else {
|
||||
await client.updateDoc(
|
||||
communication.class.GuestCommunicationSettings,
|
||||
core.space.Workspace,
|
||||
existingGuestChatSettings._id,
|
||||
{ allowedCards: value, enabled: true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const onSelected = (e: CustomEvent<string>): void => {
|
||||
selected = e.detail
|
||||
localStorage.setItem('firstDayOfWeek', `${e.detail}`)
|
||||
@@ -373,43 +298,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-col flex-gap-4 mt-6">
|
||||
<div class="title"><Label label={settingsRes.string.GuestAccess} /></div>
|
||||
<div class="flex-row-center flex-gap-4">
|
||||
<Label label={settingsRes.string.GuestAccessDescription} />
|
||||
<Toggle
|
||||
on={allowReadOnlyGuests}
|
||||
on:change={(e) => {
|
||||
void handleToggleReadonlyAccess(e)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-row-center flex-gap-4">
|
||||
<Label label={settingsRes.string.GuestSignUpDescription} />
|
||||
<Toggle
|
||||
disabled={!allowReadOnlyGuests}
|
||||
on={allowGuestSignUp}
|
||||
on:change={(e) => {
|
||||
void handleToggleGuestSignUp(e)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-row-center flex-gap-4">
|
||||
<Label label={settingsRes.string.GuestChannelsDescription} />
|
||||
<Component
|
||||
is={card.component.CardArrayEditor}
|
||||
props={{
|
||||
_class: chat.masterTag.Thread,
|
||||
value: existingGuestChatSettings !== undefined ? existingGuestChatSettings.allowedCards : [],
|
||||
label: settingsRes.string.GuestChannelsArrayLabel,
|
||||
onChange: onAllowedCardsChange
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-col flex-gap-4 mt-6">
|
||||
<div class="title"><Label label={settingsRes.string.AccessControl} /></div>
|
||||
<div class="w-32">
|
||||
|
||||
@@ -12,27 +12,89 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { ModulePermissionGroup, type Doc, type Permission, type Ref } from '@hcengineering/core'
|
||||
import card, { Card } from '@hcengineering/card'
|
||||
import chat from '@hcengineering/chat'
|
||||
import communication, { GuestCommunicationSettings } from '@hcengineering/communication'
|
||||
import contact, { ensureEmployeeForPerson } from '@hcengineering/contact'
|
||||
import { getAccountClient } from '@hcengineering/contact-resources'
|
||||
import core, {
|
||||
type Account,
|
||||
AccountRole,
|
||||
AccountUuid,
|
||||
ModulePermissionGroup,
|
||||
getCurrentAccount,
|
||||
pickPrimarySocialId,
|
||||
readOnlyGuestAccountUuid,
|
||||
type Doc,
|
||||
type Permission,
|
||||
type Ref
|
||||
} from '@hcengineering/core'
|
||||
import { getEmbeddedLabel, getMetadata, type IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient, uiContext } from '@hcengineering/presentation'
|
||||
import workbench, { type Application } from '@hcengineering/workbench'
|
||||
import { Breadcrumb, Header, Icon, Label, Loading, Scroller, Toggle } from '@hcengineering/ui'
|
||||
import {
|
||||
Breadcrumb,
|
||||
Component,
|
||||
defineSeparators,
|
||||
Header,
|
||||
Icon,
|
||||
Label,
|
||||
Loading,
|
||||
NavItem,
|
||||
Scroller,
|
||||
Separator,
|
||||
Toggle,
|
||||
twoPanelsSeparators
|
||||
} from '@hcengineering/ui'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { onMount } from 'svelte'
|
||||
import settingsRes from '../plugin'
|
||||
|
||||
let loadingSettings = true
|
||||
let loadingPermissions = true
|
||||
let workspaceAppsReady = false
|
||||
let loadingWorkspaceGuest = true
|
||||
|
||||
let allowReadOnlyGuests = false
|
||||
let allowGuestSignUp = false
|
||||
let existingGuestChatSettings: GuestCommunicationSettings | undefined = undefined
|
||||
|
||||
const accountClient = getAccountClient()
|
||||
|
||||
let moduleGroups: ModulePermissionGroup[] = []
|
||||
let permissionsMap: Map<Ref<Permission>, Permission> = new Map<Ref<Permission>, Permission>()
|
||||
let hiddenApplicationIds: Array<Ref<Application>> = []
|
||||
|
||||
const excludedApplicationIds = getMetadata(workbench.metadata.ExcludedApplications) ?? []
|
||||
const communicationApiEnabled = getMetadata(communication.metadata.Enabled) === true
|
||||
|
||||
let guestPermissionsTab: 'guest' | 'anonymous' = 'guest'
|
||||
|
||||
const client = getClient()
|
||||
const moduleGroupsQuery = createQuery()
|
||||
const permissionsQuery = createQuery()
|
||||
const hiddenAppsQuery = createQuery()
|
||||
const guestCommunicationQuery = createQuery()
|
||||
|
||||
onMount(() => {
|
||||
void (async (): Promise<void> => {
|
||||
try {
|
||||
const res = await accountClient.getWorkspaceInfo()
|
||||
allowReadOnlyGuests = res.allowReadOnlyGuest ?? false
|
||||
allowGuestSignUp = res.allowGuestSignUp ?? false
|
||||
} finally {
|
||||
loadingWorkspaceGuest = false
|
||||
}
|
||||
})()
|
||||
})
|
||||
|
||||
$: {
|
||||
if (communicationApiEnabled) {
|
||||
guestCommunicationQuery.query(communication.class.GuestCommunicationSettings, {}, (settings) => {
|
||||
existingGuestChatSettings = settings[0]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$: moduleGroupsQuery.query(core.class.ModulePermissionGroup, {}, (res) => {
|
||||
moduleGroups = res as unknown as ModulePermissionGroup[]
|
||||
@@ -62,13 +124,20 @@
|
||||
workspaceApplications.map((application) => [application._id as Ref<Doc>, application])
|
||||
)
|
||||
|
||||
$: loading = loadingSettings || loadingPermissions || !workspaceAppsReady
|
||||
$: loading = loadingSettings || loadingPermissions || !workspaceAppsReady || loadingWorkspaceGuest
|
||||
|
||||
/** Ignore permission groups for applications not enabled in this workspace. */
|
||||
$: visibleModuleGroups = moduleGroups.filter((group) => applicationsMap.has(group.application))
|
||||
$: modulePermissionsRole = guestPermissionsTab === 'guest' ? AccountRole.Guest : AccountRole.ReadOnlyGuest
|
||||
|
||||
/** Module permission groups for the selected tab (Guest vs anonymous read-only). */
|
||||
$: visibleModuleGroups = moduleGroups.filter(
|
||||
(group) => applicationsMap.has(group.application) && group.role === modulePermissionsRole
|
||||
)
|
||||
|
||||
$: sortedVisibleModuleGroups = [...visibleModuleGroups].sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity))
|
||||
|
||||
/** Anonymous (read-only guest) module rows are view-only until workspace allows anonymous guests. */
|
||||
$: anonymousModulePermissionsReadOnly = guestPermissionsTab === 'anonymous' && !allowReadOnlyGuests
|
||||
|
||||
function getApplicationLabel (applicationId: Ref<Doc>): IntlString {
|
||||
return applicationsMap.get(applicationId)?.label ?? getEmbeddedLabel(applicationId)
|
||||
}
|
||||
@@ -90,6 +159,7 @@
|
||||
permissionId: Ref<Permission>,
|
||||
enabled: boolean
|
||||
): Promise<void> {
|
||||
if (anonymousModulePermissionsReadOnly) return
|
||||
if (!isModuleEnabled(group)) return
|
||||
const disabled = getDisabledPermissions(group)
|
||||
if (enabled) {
|
||||
@@ -103,6 +173,7 @@
|
||||
}
|
||||
|
||||
async function toggleModule (group: ModulePermissionGroup, enabled: boolean): Promise<void> {
|
||||
if (anonymousModulePermissionsReadOnly) return
|
||||
await client.updateDoc(core.class.ModulePermissionGroup, core.space.Model, group._id, {
|
||||
enabled
|
||||
} as any)
|
||||
@@ -137,91 +208,245 @@
|
||||
onPermissionToggle(group, permissionId, ev)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleToggleReadonlyAccess (e: CustomEvent<boolean>): Promise<void> {
|
||||
const enabled = e.detail
|
||||
const guestUserInfo = await accountClient.updateAllowReadOnlyGuests(enabled)
|
||||
allowReadOnlyGuests = enabled
|
||||
if (guestUserInfo !== undefined) {
|
||||
const guestAccount: Account = {
|
||||
uuid: guestUserInfo.guestPerson.uuid as AccountUuid,
|
||||
role: AccountRole.ReadOnlyGuest,
|
||||
primarySocialId: pickPrimarySocialId(guestUserInfo.guestSocialIds)._id,
|
||||
socialIds: guestUserInfo.guestSocialIds.map((si) => si._id),
|
||||
fullSocialIds: guestUserInfo.guestSocialIds
|
||||
}
|
||||
const myAccount = getCurrentAccount()
|
||||
const ctx = uiContext.newChild('connect', {})
|
||||
await ensureEmployeeForPerson(
|
||||
ctx,
|
||||
myAccount,
|
||||
guestAccount,
|
||||
client,
|
||||
guestUserInfo.guestSocialIds,
|
||||
guestUserInfo.guestPerson
|
||||
)
|
||||
} else {
|
||||
const readonlyEmployee = await client.findOne(contact.mixin.Employee, { personUuid: readOnlyGuestAccountUuid })
|
||||
if (readonlyEmployee !== undefined) {
|
||||
await client.update(readonlyEmployee, { active: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleToggleGuestSignUp (e: CustomEvent<boolean>): Promise<void> {
|
||||
await accountClient.updateAllowGuestSignUp(e.detail)
|
||||
allowGuestSignUp = e.detail
|
||||
}
|
||||
|
||||
async function onAllowedCardsChange (value: Ref<Card>[]): Promise<void> {
|
||||
if (existingGuestChatSettings === undefined) {
|
||||
await client.createDoc(communication.class.GuestCommunicationSettings, core.space.Workspace, {
|
||||
allowedCards: value,
|
||||
enabled: true
|
||||
})
|
||||
} else {
|
||||
await client.updateDoc(
|
||||
communication.class.GuestCommunicationSettings,
|
||||
core.space.Workspace,
|
||||
existingGuestChatSettings._id,
|
||||
{ allowedCards: value, enabled: true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
defineSeparators('guestPermissionsSettings', twoPanelsSeparators)
|
||||
</script>
|
||||
|
||||
<div class="hulyComponent">
|
||||
<Header adaptive={'disabled'}>
|
||||
<Breadcrumb label={setting.string.GuestPermissionsSettings} size={'large'} isCurrent />
|
||||
<Breadcrumb
|
||||
icon={setting.icon.GuestPermissions}
|
||||
label={setting.string.GuestPermissionsSettings}
|
||||
size={'large'}
|
||||
isCurrent
|
||||
/>
|
||||
</Header>
|
||||
<div class="hulyComponent-content__column content">
|
||||
{#if loading}
|
||||
<div class="w-full h-full flex-col-center justify-center">
|
||||
<Loading />
|
||||
</div>
|
||||
{:else}
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content guestPermissionsRoot flex-col">
|
||||
<section class="section">
|
||||
<div class="sectionHeader">
|
||||
<div class="sectionTitle">
|
||||
<Label label={setting.string.GuestPermissionsModulePermissions} />
|
||||
</div>
|
||||
<div class="sectionHint">
|
||||
<Label label={setting.string.GuestPermissionsModulePermissionsHint} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="hulyComponent-content__container columns">
|
||||
<div class="hulyComponent-content__column navigation py-2">
|
||||
<Scroller shrink>
|
||||
<NavItem
|
||||
icon={contact.icon.Person}
|
||||
label={setting.string.GuestPermissionsTabGuest}
|
||||
selected={guestPermissionsTab === 'guest'}
|
||||
on:click={() => {
|
||||
guestPermissionsTab = 'guest'
|
||||
}}
|
||||
/>
|
||||
<NavItem
|
||||
icon={contact.icon.Persona}
|
||||
label={setting.string.GuestPermissionsTabAnonymousGuest}
|
||||
selected={guestPermissionsTab === 'anonymous'}
|
||||
on:click={() => {
|
||||
guestPermissionsTab = 'anonymous'
|
||||
}}
|
||||
/>
|
||||
</Scroller>
|
||||
</div>
|
||||
|
||||
<div class="cardStack">
|
||||
{#each sortedVisibleModuleGroups as group}
|
||||
{@const app = getApplication(group.application)}
|
||||
{@const moduleOn = isModuleEnabled(group)}
|
||||
{@const permissionCount = (group.permissions ?? []).length}
|
||||
<div class="permissionModuleCard" class:permissionModuleCard-off={!moduleOn}>
|
||||
<div
|
||||
class="permissionModuleCard-header"
|
||||
class:permissionModuleCard-headerOnly={permissionCount === 0}
|
||||
>
|
||||
<div class="permissionModuleCard-headerMain">
|
||||
{#if app}
|
||||
<div class="appIcon appIcon-sm">
|
||||
<Icon icon={app.icon} size={'small'} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="appIcon appIcon-sm appIcon-placeholder" />
|
||||
{/if}
|
||||
<div class="permissionModuleCard-titles">
|
||||
<div class="permissionModuleCard-name">
|
||||
<Label label={getApplicationLabel(group.application)} />
|
||||
<Separator name={'guestPermissionsSettings'} index={0} color={'var(--theme-divider-color)'} />
|
||||
|
||||
<div class="hulyComponent-content__column content">
|
||||
{#if loading}
|
||||
<div class="w-full h-full flex-col-center justify-center">
|
||||
<Loading />
|
||||
</div>
|
||||
{:else}
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content guestPermissionsRoot flex-col">
|
||||
{#if guestPermissionsTab === 'anonymous'}
|
||||
<section class="section">
|
||||
<div class="sectionHeader">
|
||||
<div class="sectionTitle">
|
||||
<Label label={settingsRes.string.GuestAccess} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="guestAccessBlock">
|
||||
<div class="guestAccessRow">
|
||||
<div class="guestAccessRow-label">
|
||||
<Label label={settingsRes.string.GuestAccessDescription} />
|
||||
</div>
|
||||
<div class="guestAccessRow-toggleCell">
|
||||
<Toggle
|
||||
on={allowReadOnlyGuests}
|
||||
on:change={(e) => {
|
||||
void handleToggleReadonlyAccess(e)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="guestAccessRow">
|
||||
<div class="guestAccessRow-label">
|
||||
<Label label={settingsRes.string.GuestSignUpDescription} />
|
||||
</div>
|
||||
<div class="guestAccessRow-toggleCell">
|
||||
<Toggle
|
||||
disabled={!allowReadOnlyGuests}
|
||||
on={allowGuestSignUp}
|
||||
on:change={(e) => {
|
||||
void handleToggleGuestSignUp(e)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if communicationApiEnabled}
|
||||
<div class="guestAccessRow guestAccessRow--editor">
|
||||
<div class="guestAccessRow-label">
|
||||
<Label label={settingsRes.string.GuestChannelsDescription} />
|
||||
</div>
|
||||
<div class="guestAccessRow-editorCell">
|
||||
<div class="guestAccessRow-editorInner">
|
||||
<Component
|
||||
is={card.component.CardArrayEditor}
|
||||
props={{
|
||||
_class: chat.masterTag.Thread,
|
||||
value:
|
||||
existingGuestChatSettings !== undefined ? existingGuestChatSettings.allowedCards : [],
|
||||
label: settingsRes.string.GuestChannelsArrayLabel,
|
||||
onChange: onAllowedCardsChange
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="permissionModuleCard-toggleCell">
|
||||
<Toggle on={moduleOn} on:change={handleAccessToggle(group)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if permissionCount > 0}
|
||||
<div class="permissionRows">
|
||||
{#each group.permissions ?? [] as permissionId}
|
||||
<div class="permissionRow">
|
||||
<div class="permissionRow-label">
|
||||
<Label label={getPermissionLabel(permissionId)} />
|
||||
</div>
|
||||
<div class="permissionRow-toggleCell">
|
||||
<Toggle
|
||||
disabled={!moduleOn}
|
||||
on={isPermissionActive(group, permissionId)}
|
||||
on:change={handlePermissionToggle(group, permissionId)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{#if visibleModuleGroups.length === 0}
|
||||
<div class="emptyState emptyState-block">—</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</Scroller>
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<section class="section">
|
||||
<div class="sectionHeader">
|
||||
<div class="sectionTitle">
|
||||
<Label label={setting.string.GuestPermissionsApplicationPermissions} />
|
||||
</div>
|
||||
<div class="sectionHint">
|
||||
{#if guestPermissionsTab === 'anonymous'}
|
||||
<Label label={setting.string.GuestPermissionsAnonymousApplicationHint} />
|
||||
{:else}
|
||||
<Label label={setting.string.GuestPermissionsApplicationPermissionsHint} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cardStack" class:cardStack-readonly={anonymousModulePermissionsReadOnly}>
|
||||
{#each sortedVisibleModuleGroups as group}
|
||||
{@const app = getApplication(group.application)}
|
||||
{@const moduleOn = isModuleEnabled(group)}
|
||||
{@const permissionCount = (group.permissions ?? []).length}
|
||||
<div class="permissionModuleCard" class:permissionModuleCard-off={!moduleOn}>
|
||||
<div
|
||||
class="permissionModuleCard-header"
|
||||
class:permissionModuleCard-headerOnly={permissionCount === 0}
|
||||
>
|
||||
<div class="permissionModuleCard-headerMain">
|
||||
{#if app}
|
||||
<div class="appIcon appIcon-sm">
|
||||
<Icon icon={app.icon} size={'small'} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="appIcon appIcon-sm appIcon-placeholder" />
|
||||
{/if}
|
||||
<div class="permissionModuleCard-titles">
|
||||
<div class="permissionModuleCard-name">
|
||||
<Label label={getApplicationLabel(group.application)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="permissionModuleCard-toggleCell">
|
||||
<Toggle
|
||||
disabled={anonymousModulePermissionsReadOnly}
|
||||
on={moduleOn}
|
||||
on:change={handleAccessToggle(group)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if permissionCount > 0}
|
||||
<div class="permissionRows">
|
||||
{#each group.permissions ?? [] as permissionId}
|
||||
<div class="permissionRow">
|
||||
<div class="permissionRow-label">
|
||||
<Label label={getPermissionLabel(permissionId)} />
|
||||
</div>
|
||||
<div class="permissionRow-toggleCell">
|
||||
<Toggle
|
||||
disabled={!moduleOn || anonymousModulePermissionsReadOnly}
|
||||
on={isPermissionActive(group, permissionId)}
|
||||
on:change={handlePermissionToggle(group, permissionId)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{#if visibleModuleGroups.length === 0}
|
||||
<div class="emptyState emptyState-block">—</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</Scroller>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
/* Matches packages/ui Toggle width; shared with permission rows and guest access */
|
||||
$toggleTrackWidth: 2.25rem;
|
||||
|
||||
.guestPermissionsRoot {
|
||||
max-width: 40rem;
|
||||
width: 100%;
|
||||
@@ -229,6 +454,65 @@
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.guestAccessBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
padding-right: 1rem;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.guestAccessRow {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) #{$toggleTrackWidth};
|
||||
align-items: center;
|
||||
column-gap: 0.75rem;
|
||||
min-height: 2.5rem;
|
||||
padding: 0.625rem 0 0.625rem 0.25rem;
|
||||
}
|
||||
|
||||
.guestAccessRow:not(:first-child) {
|
||||
border-top: 1px solid var(--theme-navpanel-divider);
|
||||
}
|
||||
|
||||
.guestAccessRow--editor {
|
||||
align-items: start;
|
||||
min-height: auto;
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Same two columns as toggle rows; editor sits in col 2 and grows left so its right edge matches toggles */
|
||||
.guestAccessRow--editor .guestAccessRow-editorCell {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
width: max-content;
|
||||
max-width: min(22rem, calc(100vw - 3rem));
|
||||
min-width: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.guestAccessRow-editorInner {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.guestAccessRow-label {
|
||||
min-width: 0;
|
||||
color: var(--theme-content-color);
|
||||
}
|
||||
|
||||
.guestAccessRow-toggleCell {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: $toggleTrackWidth;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -252,9 +536,6 @@
|
||||
color: var(--theme-halfcontent-color);
|
||||
}
|
||||
|
||||
/* Matches packages/ui Toggle width for a single aligned column */
|
||||
$toggleTrackWidth: 2.25rem;
|
||||
|
||||
.cardStack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -262,6 +543,10 @@
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.cardStack-readonly {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.appIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -334,11 +619,6 @@
|
||||
color: var(--theme-content-color);
|
||||
}
|
||||
|
||||
.permissionModuleCard-meta {
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-halfcontent-color);
|
||||
}
|
||||
|
||||
.permissionRows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -312,8 +312,11 @@ export default plugin(settingId, {
|
||||
DefaultStartWithTranscription: '' as IntlString,
|
||||
DefaultStartWithRecording: '' as IntlString,
|
||||
GuestPermissionsSettings: '' as IntlString,
|
||||
GuestPermissionsModulePermissions: '' as IntlString,
|
||||
GuestPermissionsModulePermissionsHint: '' as IntlString,
|
||||
GuestPermissionsApplicationPermissions: '' as IntlString,
|
||||
GuestPermissionsApplicationPermissionsHint: '' as IntlString,
|
||||
GuestPermissionsTabGuest: '' as IntlString,
|
||||
GuestPermissionsTabAnonymousGuest: '' as IntlString,
|
||||
GuestPermissionsAnonymousApplicationHint: '' as IntlString,
|
||||
MailboxErrorInvalidName: '' as IntlString,
|
||||
MailboxErrorDomainNotFound: '' as IntlString,
|
||||
MailboxErrorNameRulesViolated: '' as IntlString,
|
||||
@@ -356,6 +359,7 @@ export default plugin(settingId, {
|
||||
AccountSettings: '' as Asset,
|
||||
Owners: '' as Asset,
|
||||
Members: '' as Asset,
|
||||
GuestPermissions: '' as Asset,
|
||||
Password: '' as Asset,
|
||||
Setting: '' as Asset,
|
||||
Integrations: '' as Asset,
|
||||
|
||||
@@ -82,17 +82,23 @@
|
||||
: { docA: doc._id, docB: object._id, association: association._id }
|
||||
const relation = await client.findOne(core.class.Relation, q)
|
||||
const overrides = new Map()
|
||||
const excludedActions: string[] = []
|
||||
if (relation !== undefined) {
|
||||
overrides.set(view.action.Delete, async (obj: Doc | Doc[], ev?: Event) => {
|
||||
if (relation !== undefined) {
|
||||
await client.remove(relation)
|
||||
}
|
||||
})
|
||||
if (association.automationOnly) {
|
||||
excludedActions.push(view.action.Delete)
|
||||
} else {
|
||||
overrides.set(view.action.Delete, async (obj: Doc | Doc[], ev?: Event) => {
|
||||
if (relation !== undefined) {
|
||||
await client.remove(relation)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
showMenu(ev, { object: doc, overrides })
|
||||
showMenu(ev, { object: doc, overrides, excludedActions })
|
||||
}
|
||||
|
||||
function isAllowedToCreate (association: Association, docs: Doc[], direction: 'A' | 'B'): boolean {
|
||||
if (association.automationOnly) return false
|
||||
if (docs.length === 0 || association.type === 'N:N') return true
|
||||
if (association.type === '1:1') return false
|
||||
return direction === 'B'
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
if (g.enabled ?? true) return false
|
||||
const role = getCurrentAccount().role
|
||||
if (role === g.role) return true
|
||||
// DocGuest / ReadOnlyGuest should also respect Guest module disables.
|
||||
return (role === AccountRole.DocGuest || role === AccountRole.ReadOnlyGuest) && g.role === AccountRole.Guest
|
||||
// DocGuest should also respect Guest module disables.
|
||||
return role === AccountRole.DocGuest && g.role === AccountRole.Guest
|
||||
})
|
||||
.map((g) => g.application as Ref<Application>)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user