Merge branch 'main' into andrew/usage-limits-plan-composables
This commit is contained in:
@@ -27,39 +27,8 @@
|
||||
<h3 class="label mb-2">Access permissions</h3>
|
||||
<ProjectVisibilitySelect v-model="visibility" mount-menu-on-body />
|
||||
</div>
|
||||
<template v-if="isWorkspacesEnabled && !workspaceId">
|
||||
<div class="flex gap-y-2 flex-col">
|
||||
<p class="text-body-xs text-foreground font-medium">Workspace</p>
|
||||
<div class="flex gap-x-2 items-center">
|
||||
<ProjectsWorkspaceSelect
|
||||
v-model="selectedWorkspace"
|
||||
:items="workspaces"
|
||||
:disabled-roles="[Roles.Workspace.Guest]"
|
||||
:disabled="!hasWorkspaces"
|
||||
disabled-item-tooltip="You dont have rights to create projects in this workspace"
|
||||
class="flex-1"
|
||||
/>
|
||||
<div v-tippy="'Create workspace'" class="flex">
|
||||
<FormButton
|
||||
:icon-left="PlusIcon"
|
||||
hide-text
|
||||
class="flex"
|
||||
color="outline"
|
||||
@click="navigateToWorkspaceExplainer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-foreground-2 text-body-2xs">
|
||||
Workspaces offer better project management and higher data security.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</form>
|
||||
<CommonConfirmDialog
|
||||
v-model:open="showConfirmDialog"
|
||||
@confirm="handleConfirmAction"
|
||||
/>
|
||||
</LayoutDialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@@ -69,31 +38,7 @@ import { SimpleProjectVisibility } from '~~/lib/common/generated/gql/graphql'
|
||||
import { isRequired, isStringOfLength } from '~~/lib/common/helpers/validation'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useCreateProject } from '~~/lib/projects/composables/projectManagement'
|
||||
import { useIsWorkspacesEnabled } from '~/composables/globals'
|
||||
import { PlusIcon } from '@heroicons/vue/24/outline'
|
||||
import type { ProjectsAddDialog_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import { projectWorkspaceSelectQuery } from '~/lib/projects/graphql/queries'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { workspacesRoute } from '~/lib/common/helpers/route'
|
||||
|
||||
graphql(`
|
||||
fragment ProjectsAddDialog_Workspace on Workspace {
|
||||
id
|
||||
...ProjectsWorkspaceSelect_Workspace
|
||||
}
|
||||
`)
|
||||
|
||||
graphql(`
|
||||
fragment ProjectsAddDialog_User on User {
|
||||
workspaces {
|
||||
items {
|
||||
...ProjectsAddDialog_Workspace
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
type FormValues = {
|
||||
name: string
|
||||
@@ -108,37 +53,29 @@ const emit = defineEmits<{
|
||||
(e: 'created'): void
|
||||
}>()
|
||||
|
||||
const isWorkspacesEnabled = useIsWorkspacesEnabled()
|
||||
const createProject = useCreateProject()
|
||||
const router = useRouter()
|
||||
const logger = useLogger()
|
||||
const { handleSubmit, meta, isSubmitting } = useForm<FormValues>()
|
||||
const { result: workspaceResult } = useQuery(projectWorkspaceSelectQuery, null, () => ({
|
||||
enabled: isWorkspacesEnabled.value
|
||||
}))
|
||||
const { handleSubmit, isSubmitting } = useForm<FormValues>()
|
||||
|
||||
const visibility = ref(SimpleProjectVisibility.Unlisted)
|
||||
const selectedWorkspace = ref<ProjectsAddDialog_WorkspaceFragment>()
|
||||
const showConfirmDialog = ref(false)
|
||||
const confirmActionType = ref<'navigate' | 'close' | null>(null)
|
||||
const isClosing = ref(false)
|
||||
const isLoading = ref(false)
|
||||
|
||||
const open = defineModel<boolean>('open', { required: true })
|
||||
|
||||
const mp = useMixpanel()
|
||||
|
||||
const onSubmit = handleSubmit(async (values) => {
|
||||
if (isClosing.value) return // Prevent submission while closing
|
||||
if (isLoading.value) return // Prevent submission while closing
|
||||
|
||||
try {
|
||||
isClosing.value = true
|
||||
const workspaceId = props.workspaceId || selectedWorkspace.value?.id
|
||||
isLoading.value = true
|
||||
|
||||
await createProject({
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
visibility: visibility.value,
|
||||
...(workspaceId ? { workspaceId } : {})
|
||||
...(props.workspaceId ? { workspaceId: props.workspaceId } : {})
|
||||
})
|
||||
emit('created')
|
||||
mp.track('Stream Action', {
|
||||
@@ -149,18 +86,13 @@ const onSubmit = handleSubmit(async (values) => {
|
||||
})
|
||||
open.value = false
|
||||
} catch (error) {
|
||||
isClosing.value = false
|
||||
isLoading.value = false
|
||||
logger.error('Failed to create project:', error)
|
||||
}
|
||||
})
|
||||
|
||||
const workspaces = computed(
|
||||
() => workspaceResult.value?.activeUser?.workspaces.items ?? []
|
||||
)
|
||||
const hasWorkspaces = computed(() => workspaces.value.length > 0)
|
||||
|
||||
const dialogButtons = computed((): LayoutDialogButton[] => {
|
||||
const isDisabled = isSubmitting.value || isClosing.value
|
||||
const isDisabled = isSubmitting.value || isLoading.value
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -169,7 +101,7 @@ const dialogButtons = computed((): LayoutDialogButton[] => {
|
||||
color: 'outline',
|
||||
disabled: isDisabled
|
||||
},
|
||||
onClick: confirmCancel
|
||||
onClick: () => (open.value = false)
|
||||
},
|
||||
{
|
||||
text: 'Create',
|
||||
@@ -183,41 +115,10 @@ const dialogButtons = computed((): LayoutDialogButton[] => {
|
||||
]
|
||||
})
|
||||
|
||||
const formIsDirty = computed(() => {
|
||||
return meta.value.dirty
|
||||
})
|
||||
|
||||
const navigateToWorkspaceExplainer = () => {
|
||||
if (formIsDirty.value) {
|
||||
confirmActionType.value = 'navigate'
|
||||
showConfirmDialog.value = true
|
||||
} else {
|
||||
router.push(workspacesRoute)
|
||||
}
|
||||
}
|
||||
|
||||
const confirmCancel = () => {
|
||||
if (formIsDirty.value) {
|
||||
confirmActionType.value = 'close'
|
||||
showConfirmDialog.value = true
|
||||
} else {
|
||||
open.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirmAction = () => {
|
||||
if (confirmActionType.value === 'navigate') {
|
||||
router.push(workspacesRoute)
|
||||
} else if (confirmActionType.value === 'close') {
|
||||
open.value = false
|
||||
}
|
||||
confirmActionType.value = null
|
||||
}
|
||||
|
||||
watch(open, (newVal, oldVal) => {
|
||||
if (newVal && !oldVal) {
|
||||
selectedWorkspace.value = undefined
|
||||
isClosing.value = false
|
||||
isLoading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<p class="text-body-xs text-foreground font-medium">
|
||||
You're not a member of any workspaces.
|
||||
</p>
|
||||
<FormButton :to="workspacesRoute">Learn about workspaces</FormButton>
|
||||
<FormButton :to="workspaceCreateRoute">Learn about workspaces</FormButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -75,7 +75,7 @@ import { useMutationLoading, useQuery } from '@vue/apollo-composable'
|
||||
import type { LayoutDialogButton } from '@speckle/ui-components'
|
||||
import { useMoveProjectToWorkspace } from '~/lib/projects/composables/projectManagement'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { workspacesRoute } from '~/lib/common/helpers/route'
|
||||
import { workspaceCreateRoute } from '~/lib/common/helpers/route'
|
||||
import {
|
||||
useWorkspaceCustomDataResidencyDisclaimer,
|
||||
RegionStaticDataDisclaimerVariant
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { workspacesRoute } from '~~/lib/common/helpers/route'
|
||||
import { workspaceCreateRoute } from '~~/lib/common/helpers/route'
|
||||
import { WizardSteps } from '~/lib/workspaces/helpers/types'
|
||||
import { useWorkspacesWizard } from '~/lib/workspaces/composables/wizard'
|
||||
import { useMixpanel } from '~/lib/core/composables/mp'
|
||||
@@ -81,7 +81,7 @@ const requiresWorkspaceCreation = computed(() => {
|
||||
|
||||
const onCancelClick = () => {
|
||||
if (isFirstStep.value) {
|
||||
navigateTo(workspacesRoute)
|
||||
navigateTo(workspaceCreateRoute())
|
||||
resetWizardState()
|
||||
mixpanel.stop_session_recording()
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { LayoutDialogButton } from '@speckle/ui-components'
|
||||
import { useMixpanel } from '~/lib/core/composables/mp'
|
||||
import { workspacesRoute } from '~/lib/common/helpers/route'
|
||||
import { homeRoute } from '~/lib/common/helpers/route'
|
||||
import {
|
||||
convertThrowIntoFetchResult,
|
||||
modifyObjectFields
|
||||
@@ -93,7 +93,7 @@ const onConfirm = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
router.push(workspacesRoute)
|
||||
router.push(homeRoute)
|
||||
isOpen.value = false
|
||||
resetWizardState()
|
||||
mixpanel.track('Workspace Creation Canceled')
|
||||
|
||||
@@ -7,7 +7,6 @@ export const profileRoute = '/profile'
|
||||
export const authBlockedDueToVerificationRoute = '/error-email-verify'
|
||||
export const homeRoute = '/'
|
||||
export const projectsRoute = '/projects'
|
||||
export const workspacesRoute = '/workspaces'
|
||||
export const loginRoute = '/authn/login'
|
||||
export const registerRoute = '/authn/register'
|
||||
export const ssoLoginRoute = '/authn/sso'
|
||||
|
||||
@@ -197,7 +197,11 @@ export = FF_GATEKEEPER_MODULE_ENABLED
|
||||
const projectModelCount =
|
||||
await getPaginatedProjectModelsTotalCountFactory({ db: regionDb })(
|
||||
project.id,
|
||||
{}
|
||||
{
|
||||
filter: {
|
||||
onlyWithVersions: true
|
||||
}
|
||||
}
|
||||
)
|
||||
modelCount = modelCount + projectModelCount
|
||||
}
|
||||
|
||||
@@ -29,7 +29,14 @@ import {
|
||||
TestApolloServer
|
||||
} from '@/test/graphqlHelper'
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { createTestBranches } from '@/test/speckle-helpers/branchHelper'
|
||||
import {
|
||||
BasicTestBranch,
|
||||
createTestBranches
|
||||
} from '@/test/speckle-helpers/branchHelper'
|
||||
import {
|
||||
createTestCommits,
|
||||
createTestObject
|
||||
} from '@/test/speckle-helpers/commitHelper'
|
||||
import { BasicTestStream, createTestStream } from '@/test/speckle-helpers/streamHelper'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { expect } from 'chai'
|
||||
@@ -283,36 +290,49 @@ describe('Workspaces Billing', () => {
|
||||
workspaceId: workspace.id
|
||||
}
|
||||
await createTestStream(project, user)
|
||||
await createTestBranches([
|
||||
|
||||
const models: BasicTestBranch[] = [
|
||||
{
|
||||
owner: user,
|
||||
stream: project,
|
||||
branch: {
|
||||
id: createRandomString(),
|
||||
streamId: project.id,
|
||||
authorId: user.id,
|
||||
name: createRandomString()
|
||||
}
|
||||
id: '',
|
||||
streamId: project.id,
|
||||
authorId: user.id,
|
||||
name: createRandomString()
|
||||
},
|
||||
{
|
||||
owner: user,
|
||||
stream: project,
|
||||
branch: {
|
||||
id: createRandomString(),
|
||||
streamId: project.id,
|
||||
authorId: user.id,
|
||||
name: createRandomString()
|
||||
}
|
||||
id: '',
|
||||
streamId: project.id,
|
||||
authorId: user.id,
|
||||
name: createRandomString()
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
streamId: project.id,
|
||||
authorId: user.id,
|
||||
name: createRandomString()
|
||||
}
|
||||
]
|
||||
await createTestBranches(
|
||||
models.map((branch) => ({
|
||||
owner: user,
|
||||
stream: project,
|
||||
branch: {
|
||||
id: createRandomString(),
|
||||
streamId: project.id,
|
||||
authorId: user.id,
|
||||
name: createRandomString()
|
||||
}
|
||||
branch
|
||||
}))
|
||||
)
|
||||
const objectId = await createTestObject({ projectId: project.id })
|
||||
await createTestCommits([
|
||||
{
|
||||
id: '',
|
||||
authorId: user.id,
|
||||
objectId,
|
||||
streamId: project.id,
|
||||
branchName: models[0].name
|
||||
},
|
||||
{
|
||||
id: '',
|
||||
authorId: user.id,
|
||||
objectId,
|
||||
streamId: project.id,
|
||||
branchName: models[1].name
|
||||
}
|
||||
])
|
||||
|
||||
@@ -324,7 +344,7 @@ describe('Workspaces Billing', () => {
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res?.data?.workspace?.plan?.usage?.projectCount).to.equal(1)
|
||||
expect(res?.data?.workspace?.plan?.usage?.modelCount).to.equal(3)
|
||||
expect(res?.data?.workspace?.plan?.usage?.modelCount).to.equal(2)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -45,12 +45,14 @@ spec:
|
||||
- "process.exit((Date.now() - require('fs').readFileSync('/tmp/last_successful_query', 'utf8') > 25 * 60 * 1000) ? 1 : 0)"
|
||||
|
||||
resources:
|
||||
{{- with .Values.fileimport_service.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.fileimport_service.requests.cpu }}
|
||||
memory: {{ .Values.fileimport_service.requests.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.fileimport_service.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.fileimport_service.limits.cpu }}
|
||||
memory: {{ .Values.fileimport_service.limits.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
@@ -35,12 +35,14 @@ spec:
|
||||
protocol: TCP
|
||||
|
||||
resources:
|
||||
{{- with .Values.frontend_2.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.frontend_2.requests.cpu }}
|
||||
memory: {{ .Values.frontend_2.requests.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.frontend_2.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.frontend_2.limits.cpu }}
|
||||
memory: {{ .Values.frontend_2.limits.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
# Allow for k8s to remove the pod from the service endpoints to stop receive traffic
|
||||
lifecycle:
|
||||
|
||||
@@ -30,12 +30,14 @@ spec:
|
||||
protocol: TCP
|
||||
|
||||
resources:
|
||||
{{- with .Values.monitoring.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.monitoring.requests.cpu }}
|
||||
memory: {{ .Values.monitoring.requests.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.monitoring.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.monitoring.limits.cpu }}
|
||||
memory: {{ .Values.monitoring.limits.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
@@ -34,12 +34,14 @@ spec:
|
||||
protocol: TCP
|
||||
|
||||
resources:
|
||||
{{- with .Values.objects.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.objects.requests.cpu }}
|
||||
memory: {{ .Values.objects.requests.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.objects.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.objects.limits.cpu }}
|
||||
memory: {{ .Values.objects.limits.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
@@ -42,12 +42,14 @@ spec:
|
||||
port: metrics
|
||||
|
||||
resources:
|
||||
{{- with .Values.preview_service.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.preview_service.requests.cpu }}
|
||||
memory: {{ .Values.preview_service.requests.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.preview_service.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.preview_service.limits.cpu }}
|
||||
memory: {{ .Values.preview_service.limits.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
@@ -34,12 +34,14 @@ spec:
|
||||
protocol: TCP
|
||||
|
||||
resources:
|
||||
{{- with .Values.server.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.server.requests.cpu }}
|
||||
memory: {{ .Values.server.requests.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.server.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.server.limits.cpu }}
|
||||
memory: {{ .Values.server.limits.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
@@ -32,12 +32,14 @@ spec:
|
||||
- name: LOG_PRETTY
|
||||
value: {{ .Values.test.logPretty | quote }}
|
||||
resources:
|
||||
{{- with .Values.test.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.test.requests.cpu }}
|
||||
memory: {{ .Values.test.requests.memory }}
|
||||
{{- toYaml . | nindent 14 }}
|
||||
{{- end }}
|
||||
{{- with .Values.test.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.test.limits.cpu }}
|
||||
memory: {{ .Values.test.limits.memory }}
|
||||
{{- toYaml . | nindent 14 }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
|
||||
@@ -39,12 +39,14 @@ spec:
|
||||
- process.exit(Date.now() - require('fs').readFileSync('/tmp/last_successful_query', 'utf8') > 30 * 1000)
|
||||
|
||||
resources:
|
||||
{{- with .Values.webhook_service.requests }}
|
||||
requests:
|
||||
cpu: {{ .Values.webhook_service.requests.cpu }}
|
||||
memory: {{ .Values.webhook_service.requests.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhook_service.limits }}
|
||||
limits:
|
||||
cpu: {{ .Values.webhook_service.limits.cpu }}
|
||||
memory: {{ .Values.webhook_service.limits.memory }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
@@ -1123,6 +1123,7 @@ preview_service:
|
||||
## @param preview_service.dedicatedPreviewsQueue Allows using a dedicated redis url for the preview service job queue
|
||||
##
|
||||
dedicatedPreviewsQueue: false
|
||||
|
||||
## @param preview_service.replicas The number of instances of the Preview Service pod to be deployed within the cluster.
|
||||
##
|
||||
replicas: 1
|
||||
|
||||
Reference in New Issue
Block a user