Initial UI work
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<section class="py-8">
|
||||
<SettingsSectionHeader title="New workspace creation" subheading />
|
||||
<p class="text-body-xs text-foreground-2 mt-2 mb-6">
|
||||
Control whether workspace members can create new workspaces.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col space-y-6">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 flex-col pr-6 gap-y-1">
|
||||
<p class="text-body-xs font-medium text-foreground">
|
||||
Restrict member workspace creation
|
||||
</p>
|
||||
<p class="text-body-2xs text-foreground-2 leading-5 max-w-md mt-1">
|
||||
Prevent workspace members from creating new workspaces. Admins and guests
|
||||
can still create workspaces.
|
||||
</p>
|
||||
</div>
|
||||
<div v-tippy="!isWorkspaceAdmin ? 'You must be a workspace admin' : undefined">
|
||||
<FormSwitch
|
||||
v-model="isExclusive"
|
||||
name="workspace-exclusive"
|
||||
:disabled="!isWorkspaceAdmin"
|
||||
:show-label="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { graphql } from '~/lib/common/generated/gql'
|
||||
import type { SettingsWorkspacesSecurityWorkspaceCreation_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
|
||||
import { useMixpanel } from '~/lib/core/composables/mp'
|
||||
import { workspaceUpdateExclusiveMutation } from '~/lib/workspaces/graphql/mutations'
|
||||
|
||||
graphql(`
|
||||
fragment SettingsWorkspacesSecurityWorkspaceCreation_Workspace on Workspace {
|
||||
id
|
||||
slug
|
||||
role
|
||||
isExclusive
|
||||
}
|
||||
`)
|
||||
|
||||
const props = defineProps<{
|
||||
workspace: SettingsWorkspacesSecurityWorkspaceCreation_WorkspaceFragment
|
||||
}>()
|
||||
|
||||
const mixpanel = useMixpanel()
|
||||
const { mutate: updateExclusive } = useMutation(workspaceUpdateExclusiveMutation)
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
|
||||
const isWorkspaceAdmin = computed(() => props.workspace.role === Roles.Workspace.Admin)
|
||||
|
||||
const isExclusive = computed({
|
||||
get: () => props.workspace?.isExclusive || false,
|
||||
set: async (newVal) => {
|
||||
if (!props.workspace?.id) return
|
||||
|
||||
const result = await updateExclusive({
|
||||
input: {
|
||||
id: props.workspace.id,
|
||||
isExclusive: newVal
|
||||
}
|
||||
}).catch(convertThrowIntoFetchResult)
|
||||
|
||||
if (result?.data) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Success,
|
||||
title: 'Workspace creation restriction updated',
|
||||
description: `Member workspace creation has been ${
|
||||
newVal ? 'restricted' : 'allowed'
|
||||
}`
|
||||
})
|
||||
mixpanel.track('Workspace Creation Restriction Toggled', {
|
||||
value: newVal,
|
||||
// eslint-disable-next-line camelcase
|
||||
workspace_id: props.workspace?.id
|
||||
})
|
||||
} else {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Failed to update workspace creation restriction',
|
||||
description: 'Please try again later'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -142,6 +142,7 @@ type Documents = {
|
||||
"\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n slug\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n": typeof types.SettingsWorkspacesSecurityDomainProtection_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurityWorkspaceCreation_Workspace on Workspace {\n id\n slug\n role\n isExclusive\n }\n": typeof types.SettingsWorkspacesSecurityWorkspaceCreation_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": typeof types.SettingsWorkspacesSecuritySsoWrapper_WorkspaceFragmentDoc,
|
||||
"\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n visibility\n workspace {\n id\n slug\n name\n role\n }\n embedOptions {\n hideSpeckleBranding\n }\n hasAccessToFeature(featureName: hideSpeckleBranding)\n ...ViewerLimitsDialog_Project\n }\n": typeof types.ModelPageProjectFragmentDoc,
|
||||
"\n fragment ViewerCommentThreadData on Comment {\n id\n permissions {\n canArchive {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.ViewerCommentThreadDataFragmentDoc,
|
||||
@@ -418,6 +419,7 @@ type Documents = {
|
||||
"\n mutation DismissDiscoverableWorkspace($input: WorkspaceDismissInput!) {\n workspaceMutations {\n dismiss(input: $input)\n }\n }\n": typeof types.DismissDiscoverableWorkspaceDocument,
|
||||
"\n mutation WorkspaceUpdateAutoJoinMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n discoverabilityAutoJoinEnabled\n }\n }\n }\n": typeof types.WorkspaceUpdateAutoJoinMutationDocument,
|
||||
"\n mutation WorkspaceUpdateDefaultSeatTypeMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n defaultSeatType\n }\n }\n }\n": typeof types.WorkspaceUpdateDefaultSeatTypeMutationDocument,
|
||||
"\n mutation WorkspaceUpdateExclusiveMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n isExclusive\n }\n }\n }\n": typeof types.WorkspaceUpdateExclusiveMutationDocument,
|
||||
"\n query WorkspaceAccessCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n }\n }\n": typeof types.WorkspaceAccessCheckDocument,
|
||||
"\n query WorkspaceSidebar(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceSidebar_Workspace\n }\n }\n": typeof types.WorkspaceSidebarDocument,
|
||||
"\n query WorkspaceDashboard(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceDashboard_Workspace\n }\n }\n": typeof types.WorkspaceDashboardDocument,
|
||||
@@ -461,7 +463,7 @@ type Documents = {
|
||||
"\n fragment SettingsWorkspacesProjects_Workspace on Workspace {\n id\n name\n slug\n plan {\n name\n }\n role\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.SettingsWorkspacesProjects_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesRegions_Workspace on Workspace {\n id\n role\n defaultRegion {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n hasAccessToMultiRegion: hasAccessToFeature(\n featureName: workspaceDataRegionSpecificity\n )\n hasProjects: projects(limit: 0) {\n totalCount\n }\n }\n": typeof types.SettingsWorkspacesRegions_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesRegions_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": typeof types.SettingsWorkspacesRegions_ServerInfoFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n id\n slug\n }\n": typeof types.SettingsWorkspacesSecurity_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n ...SettingsWorkspacesSecurityWorkspaceCreation_Workspace\n id\n slug\n }\n": typeof types.SettingsWorkspacesSecurity_WorkspaceFragmentDoc,
|
||||
};
|
||||
const documents: Documents = {
|
||||
"\n fragment AuthLoginWithEmailBlock_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n email\n user {\n id\n }\n }\n": types.AuthLoginWithEmailBlock_PendingWorkspaceCollaboratorFragmentDoc,
|
||||
@@ -592,6 +594,7 @@ const documents: Documents = {
|
||||
"\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n slug\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n": types.SettingsWorkspacesSecurityDomainProtection_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n": types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurityWorkspaceCreation_Workspace on Workspace {\n id\n slug\n role\n isExclusive\n }\n": types.SettingsWorkspacesSecurityWorkspaceCreation_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": types.SettingsWorkspacesSecuritySsoWrapper_WorkspaceFragmentDoc,
|
||||
"\n fragment ModelPageProject on Project {\n id\n createdAt\n name\n visibility\n workspace {\n id\n slug\n name\n role\n }\n embedOptions {\n hideSpeckleBranding\n }\n hasAccessToFeature(featureName: hideSpeckleBranding)\n ...ViewerLimitsDialog_Project\n }\n": types.ModelPageProjectFragmentDoc,
|
||||
"\n fragment ViewerCommentThreadData on Comment {\n id\n permissions {\n canArchive {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.ViewerCommentThreadDataFragmentDoc,
|
||||
@@ -868,6 +871,7 @@ const documents: Documents = {
|
||||
"\n mutation DismissDiscoverableWorkspace($input: WorkspaceDismissInput!) {\n workspaceMutations {\n dismiss(input: $input)\n }\n }\n": types.DismissDiscoverableWorkspaceDocument,
|
||||
"\n mutation WorkspaceUpdateAutoJoinMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n discoverabilityAutoJoinEnabled\n }\n }\n }\n": types.WorkspaceUpdateAutoJoinMutationDocument,
|
||||
"\n mutation WorkspaceUpdateDefaultSeatTypeMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n defaultSeatType\n }\n }\n }\n": types.WorkspaceUpdateDefaultSeatTypeMutationDocument,
|
||||
"\n mutation WorkspaceUpdateExclusiveMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n isExclusive\n }\n }\n }\n": types.WorkspaceUpdateExclusiveMutationDocument,
|
||||
"\n query WorkspaceAccessCheck($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n }\n }\n": types.WorkspaceAccessCheckDocument,
|
||||
"\n query WorkspaceSidebar(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceSidebar_Workspace\n }\n }\n": types.WorkspaceSidebarDocument,
|
||||
"\n query WorkspaceDashboard(\n $workspaceSlug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n ...WorkspaceDashboard_Workspace\n }\n }\n": types.WorkspaceDashboardDocument,
|
||||
@@ -911,7 +915,7 @@ const documents: Documents = {
|
||||
"\n fragment SettingsWorkspacesProjects_Workspace on Workspace {\n id\n name\n slug\n plan {\n name\n }\n role\n permissions {\n canCreateProject {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.SettingsWorkspacesProjects_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesRegions_Workspace on Workspace {\n id\n role\n defaultRegion {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n hasAccessToMultiRegion: hasAccessToFeature(\n featureName: workspaceDataRegionSpecificity\n )\n hasProjects: projects(limit: 0) {\n totalCount\n }\n }\n": types.SettingsWorkspacesRegions_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesRegions_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": types.SettingsWorkspacesRegions_ServerInfoFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n id\n slug\n }\n": types.SettingsWorkspacesSecurity_WorkspaceFragmentDoc,
|
||||
"\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n ...SettingsWorkspacesSecurityWorkspaceCreation_Workspace\n id\n slug\n }\n": types.SettingsWorkspacesSecurity_WorkspaceFragmentDoc,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1440,6 +1444,10 @@ export function graphql(source: "\n fragment SettingsWorkspacesSecurityDomainRe
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment SettingsWorkspacesSecurityWorkspaceCreation_Workspace on Workspace {\n id\n slug\n role\n isExclusive\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurityWorkspaceCreation_Workspace on Workspace {\n id\n slug\n role\n isExclusive\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -2544,6 +2552,10 @@ export function graphql(source: "\n mutation WorkspaceUpdateAutoJoinMutation($i
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation WorkspaceUpdateDefaultSeatTypeMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n defaultSeatType\n }\n }\n }\n"): (typeof documents)["\n mutation WorkspaceUpdateDefaultSeatTypeMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n defaultSeatType\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation WorkspaceUpdateExclusiveMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n isExclusive\n }\n }\n }\n"): (typeof documents)["\n mutation WorkspaceUpdateExclusiveMutation($input: WorkspaceUpdateInput!) {\n workspaceMutations {\n update(input: $input) {\n id\n isExclusive\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -2719,7 +2731,7 @@ export function graphql(source: "\n fragment SettingsWorkspacesRegions_ServerIn
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n id\n slug\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n id\n slug\n }\n"];
|
||||
export function graphql(source: "\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n ...SettingsWorkspacesSecurityWorkspaceCreation_Workspace\n id\n slug\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurity_Workspace on Workspace {\n ...SettingsWorkspacesSecurityDefaultSeat_Workspace\n ...SettingsWorkspacesSecurityDomainManagement_Workspace\n ...SettingsWorkspacesSecurityDiscoverability_Workspace\n ...SettingsWorkspacesSecuritySsoWrapper_Workspace\n ...SettingsWorkspacesSecurityDomainProtection_Workspace\n ...SettingsWorkspacesSecurityWorkspaceCreation_Workspace\n id\n slug\n }\n"];
|
||||
|
||||
export function graphql(source: string) {
|
||||
return (documents as any)[source] ?? {};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -174,3 +174,14 @@ export const workspaceUpdateDefaultSeatTypeMutation = graphql(`
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
export const workspaceUpdateExclusiveMutation = graphql(`
|
||||
mutation WorkspaceUpdateExclusiveMutation($input: WorkspaceUpdateInput!) {
|
||||
workspaceMutations {
|
||||
update(input: $input) {
|
||||
id
|
||||
isExclusive
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<template v-if="isSsoEnabled">
|
||||
<SettingsWorkspacesSecuritySsoWrapper :workspace="workspace" />
|
||||
</template>
|
||||
<SettingsWorkspacesSecurityWorkspaceCreation :workspace="workspace" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -31,6 +32,7 @@ graphql(`
|
||||
...SettingsWorkspacesSecurityDiscoverability_Workspace
|
||||
...SettingsWorkspacesSecuritySsoWrapper_Workspace
|
||||
...SettingsWorkspacesSecurityDomainProtection_Workspace
|
||||
...SettingsWorkspacesSecurityWorkspaceCreation_Workspace
|
||||
id
|
||||
slug
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user