diff --git a/packages/frontend-2/components/feedback/Dialog.vue b/packages/frontend-2/components/feedback/Dialog.vue
index dc9f3792c..2fb608e49 100644
--- a/packages/frontend-2/components/feedback/Dialog.vue
+++ b/packages/frontend-2/components/feedback/Dialog.vue
@@ -6,7 +6,7 @@
:on-submit="onSubmit"
max-width="md"
>
-
+
How can we improve Speckle? If you have a feature request, please also share how
you would use it and why it's important to you
@@ -18,6 +18,13 @@
label="Feedback"
color="foundation"
/>
+
+ Need help? For support, head over to our
+
+ community forum
+
+ where we can chat and solve problems together.
+
diff --git a/packages/frontend-2/components/header/NavLink.vue b/packages/frontend-2/components/header/NavLink.vue
index 55cced73b..007b41af7 100644
--- a/packages/frontend-2/components/header/NavLink.vue
+++ b/packages/frontend-2/components/header/NavLink.vue
@@ -9,16 +9,16 @@
viewBox="0 0 8 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
- class="text-outline-2"
+ class="text-outline-5"
>
{{ name || to }}
diff --git a/packages/frontend-2/components/invite/Banner.vue b/packages/frontend-2/components/invite/Banner.vue
index 0d605747a..74a3b1d65 100644
--- a/packages/frontend-2/components/invite/Banner.vue
+++ b/packages/frontend-2/components/invite/Banner.vue
@@ -98,7 +98,7 @@ const token = computed(
)
const mainClasses = computed(() => {
const classParts = [
- 'flex flex-col space-y-4 px-4 py-5 transition border-x border-b border-outline-2 first:border-t first:rounded-t-lg last:rounded-b-lg'
+ 'flex flex-col space-y-4 px-4 py-5 transition bg-foundation border-x border-b border-outline-2 first:border-t first:rounded-t-lg last:rounded-b-lg'
]
if (props.block) {
diff --git a/packages/frontend-2/components/projects/DashboardHeader.vue b/packages/frontend-2/components/projects/DashboardHeader.vue
index 39037d8ca..4ab541986 100644
--- a/packages/frontend-2/components/projects/DashboardHeader.vue
+++ b/packages/frontend-2/components/projects/DashboardHeader.vue
@@ -1,9 +1,6 @@
-
+
>({})
const actionItems: LayoutMenuItem[][] = [
[
{
- title: 'Change role',
+ title: 'Change role...',
id: ActionTypes.ChangeRole
},
{
- title: 'Remove user',
+ title: 'Remove user...',
id: ActionTypes.RemoveUser
}
]
diff --git a/packages/frontend-2/components/settings/server/PendingInvitations.vue b/packages/frontend-2/components/settings/server/PendingInvitations.vue
index 8b0331c75..2644c0156 100644
--- a/packages/frontend-2/components/settings/server/PendingInvitations.vue
+++ b/packages/frontend-2/components/settings/server/PendingInvitations.vue
@@ -129,7 +129,7 @@ const invites = computed(() => result.value?.admin.inviteList.items || [])
const actionItems: LayoutMenuItem[][] = [
[
{ title: 'Resend invitation', id: 'resend-invite' },
- { title: 'Delete invitation', id: 'delete-invite' }
+ { title: 'Delete invitation...', id: 'delete-invite' }
]
]
diff --git a/packages/frontend-2/components/settings/shared/projects/index.vue b/packages/frontend-2/components/settings/shared/projects/index.vue
index 10c6846bc..17d1dd237 100644
--- a/packages/frontend-2/components/settings/shared/projects/index.vue
+++ b/packages/frontend-2/components/settings/shared/projects/index.vue
@@ -177,8 +177,8 @@ const showActionsMenu = ref>({})
const actionItems: LayoutMenuItem[][] = [
[
- { title: 'View project...', id: ActionTypes.ViewProject },
- { title: 'Edit members...', id: ActionTypes.EditMembers },
+ { title: 'View project', id: ActionTypes.ViewProject },
+ { title: 'Edit members', id: ActionTypes.EditMembers },
{ title: 'Remove project...', id: ActionTypes.RemoveProject }
]
]
diff --git a/packages/frontend-2/components/settings/workspaces/General.vue b/packages/frontend-2/components/settings/workspaces/General.vue
index 9b233d67d..0c1ab0304 100644
--- a/packages/frontend-2/components/settings/workspaces/General.vue
+++ b/packages/frontend-2/components/settings/workspaces/General.vue
@@ -290,6 +290,8 @@ const updateWorkspaceSlug = async (newSlug: string) => {
title: 'Workspace short ID updated'
})
+ showEditSlugDialog.value = false
+
slug.value = newSlug
if (route.params.slug === oldSlug) {
diff --git a/packages/frontend-2/components/settings/workspaces/General/DeleteDialog.vue b/packages/frontend-2/components/settings/workspaces/General/DeleteDialog.vue
index 6de17bd4f..e69523428 100644
--- a/packages/frontend-2/components/settings/workspaces/General/DeleteDialog.vue
+++ b/packages/frontend-2/components/settings/workspaces/General/DeleteDialog.vue
@@ -5,23 +5,36 @@
max-width="sm"
:buttons="dialogButtons"
>
-
- Are you sure you want to
- permanently delete
- the selected workspace?
-
-
- {{ workspace.name }}
-
-
- This action
- cannot
- be undone.
+
+ Are you sure you want to permanently delete
+ {{ workspace.name }}?
+ This action cannot be undone.
+
+
+
diff --git a/packages/frontend-2/components/settings/workspaces/General/EditSlugDialog.vue b/packages/frontend-2/components/settings/workspaces/General/EditSlugDialog.vue
index 85f9f3483..18a482134 100644
--- a/packages/frontend-2/components/settings/workspaces/General/EditSlugDialog.vue
+++ b/packages/frontend-2/components/settings/workspaces/General/EditSlugDialog.vue
@@ -19,11 +19,16 @@
@@ -33,11 +38,11 @@ import { useForm } from 'vee-validate'
import { graphql } from '~~/lib/common/generated/gql'
import type { LayoutDialogButton } from '@speckle/ui-components'
import type { SettingsWorkspacesGeneralEditSlugDialog_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
-import {
- isStringOfLength,
- isValidWorkspaceSlug
-} from '~~/lib/common/helpers/validation'
+import { isStringOfLength } from '~~/lib/common/helpers/validation'
import { workspaceRoute } from '~/lib/common/helpers/route'
+import { useQuery } from '@vue/apollo-composable'
+import { validateWorkspaceSlugQuery } from '~/lib/workspaces/graphql/queries'
+import { debounce } from 'lodash'
graphql(`
fragment SettingsWorkspacesGeneralEditSlugDialog_Workspace on Workspace {
@@ -57,13 +62,27 @@ const emit = defineEmits<{
(e: 'update:slug', newSlug: string): void
}>()
-const { handleSubmit } = useForm<{ slug: string }>()
-
+// Main ref that holds the current value of the slug input.
const workspaceShortId = ref(props.workspace.slug)
+// Used to debounce API calls for slug validation.
+const debouncedWorkspaceShortId = ref(props.workspace.slug)
+// Keeps track of the initially generated slug to prevent unnecessary validations.
+const originalSlug = ref(props.workspace.slug)
+
+const { error, loading } = useQuery(
+ validateWorkspaceSlugQuery,
+ () => ({
+ slug: debouncedWorkspaceShortId.value
+ }),
+ () => ({
+ enabled: debouncedWorkspaceShortId.value !== props.workspace.slug
+ })
+)
+
+const { handleSubmit, resetForm } = useForm<{ slug: string }>()
const updateSlug = handleSubmit(() => {
emit('update:slug', workspaceShortId.value)
- isOpen.value = false
})
const dialogButtons = computed((): LayoutDialogButton[] => [
@@ -78,12 +97,16 @@ const dialogButtons = computed((): LayoutDialogButton[] => [
text: 'Update',
props: {
color: 'primary',
- disabled: workspaceShortId.value === props.workspace.slug
+ disabled: workspaceShortId.value === props.workspace.slug || error.value !== null
},
submit: true
}
])
+const updateDebouncedShortId = debounce((value: string) => {
+ debouncedWorkspaceShortId.value = value
+}, 300)
+
watch(
() => props.workspace.slug,
(newValue) => {
@@ -91,4 +114,14 @@ watch(
},
{ immediate: true }
)
+
+watch(
+ () => isOpen.value,
+ (newValue) => {
+ if (!newValue) {
+ resetForm()
+ error.value = null
+ }
+ }
+)
diff --git a/packages/frontend-2/components/workspace/CreateDialog.vue b/packages/frontend-2/components/workspace/CreateDialog.vue
index fb59bb43b..06b96ec8e 100644
--- a/packages/frontend-2/components/workspace/CreateDialog.vue
+++ b/packages/frontend-2/components/workspace/CreateDialog.vue
@@ -23,12 +23,11 @@
label="Short ID"
:help="getShortIdHelp"
color="foundation"
- :rules="[
- isStringOfLength({ maxLength: 50, minLength: 3 }),
- isValidWorkspaceSlug
- ]"
+ :loading="loading"
+ :rules="isStringOfLength({ maxLength: 50, minLength: 3 })"
+ :custom-error-message="error?.graphQLErrors[0]?.message"
show-label
- @update:model-value="shortIdManuallyEdited = true"
+ @update:model-value="onSlugChange"
/>
void>()
@@ -69,15 +66,25 @@ const isOpen = defineModel('open', { required: true })
const createWorkspace = useCreateWorkspace()
const { generateDefaultLogoIndex, getDefaultAvatar } = useWorkspacesAvatar()
-const { handleSubmit } = useForm<{ name: string; slug: string }>()
+const { handleSubmit, resetForm } = useForm<{ name: string; slug: string }>()
const workspaceName = ref('')
const workspaceShortId = ref('')
+const debouncedWorkspaceShortId = ref('')
const editAvatarMode = ref(false)
const workspaceLogo = ref>()
const defaultLogoIndex = ref(0)
const shortIdManuallyEdited = ref(false)
-const customShortIdError = ref('')
+
+const { error, loading } = useQuery(
+ validateWorkspaceSlugQuery,
+ () => ({
+ slug: debouncedWorkspaceShortId.value
+ }),
+ () => ({
+ enabled: !!debouncedWorkspaceShortId.value
+ })
+)
const baseUrl = useRuntimeConfig().public.baseUrl
@@ -105,7 +112,7 @@ const dialogButtons = computed((): LayoutDialogButton[] => [
disabled:
!workspaceName.value.trim() ||
!workspaceShortId.value.trim() ||
- !!customShortIdError.value
+ error.value !== null
}
}
])
@@ -122,7 +129,7 @@ const handleCreateWorkspace = handleSubmit(async () => {
{ source: props.eventSource }
)
- if (newWorkspace) {
+ if (newWorkspace && !newWorkspace?.errors) {
emit('created')
isOpen.value = false
}
@@ -135,21 +142,36 @@ const onLogoSave = (newVal: MaybeNullOrUndefined) => {
const reset = () => {
defaultLogoIndex.value = generateDefaultLogoIndex()
- workspaceName.value = ''
- workspaceShortId.value = ''
+ debouncedWorkspaceShortId.value = ''
workspaceLogo.value = null
editAvatarMode.value = false
shortIdManuallyEdited.value = false
- customShortIdError.value = ''
+ error.value = null
}
const updateShortId = debounce((newName: string) => {
if (!shortIdManuallyEdited.value) {
- workspaceShortId.value = generateSlugFromName({ name: newName })
+ const newSlug = generateSlugFromName({ name: newName })
+ workspaceShortId.value = newSlug
+ updateDebouncedShortId(newSlug)
}
}, 600)
+const updateDebouncedShortId = debounce((newSlug: string) => {
+ debouncedWorkspaceShortId.value = newSlug
+}, 300)
+
+const onSlugChange = (newSlug: string) => {
+ workspaceShortId.value = newSlug
+ shortIdManuallyEdited.value = true
+ updateDebouncedShortId(newSlug)
+}
+
+// Seperate resets to avoid a temporary invalid state on submission
watch(isOpen, (newVal) => {
- if (newVal) reset()
+ if (!newVal) {
+ reset()
+ resetForm()
+ }
})
diff --git a/packages/frontend-2/components/workspace/MoveProjectsDialog.vue b/packages/frontend-2/components/workspace/MoveProjectsDialog.vue
index 67e9d55ff..5a9e6b9e0 100644
--- a/packages/frontend-2/components/workspace/MoveProjectsDialog.vue
+++ b/packages/frontend-2/components/workspace/MoveProjectsDialog.vue
@@ -29,7 +29,7 @@
- You don't have any projects that are moveable to this workspace
+ You don't have any projects that can be moved into this workspace
-
+
;
updatedAt: Scalars['DateTime']['output'];
/** Retrieve a specific project version by its ID */
- version?: Maybe;
+ version: Version;
/** Returns a flat list of all project versions */
versions: VersionCollection;
/** Return metadata about resources being requested in the viewer */
@@ -4749,7 +4749,7 @@ export type GendoAiRenderQueryVariables = Exact<{
}>;
-export type GendoAiRenderQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, version?: { __typename?: 'Version', id: string, gendoAIRender: { __typename?: 'GendoAIRender', id: string, projectId: string, modelId: string, versionId: string, createdAt: string, updatedAt: string, gendoGenerationId?: string | null, status: string, prompt: string, camera?: {} | null, responseImage?: string | null, user?: { __typename?: 'AvatarUser', name: string, avatar?: string | null, id: string } | null } } | null } };
+export type GendoAiRenderQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, version: { __typename?: 'Version', id: string, gendoAIRender: { __typename?: 'GendoAIRender', id: string, projectId: string, modelId: string, versionId: string, createdAt: string, updatedAt: string, gendoGenerationId?: string | null, status: string, prompt: string, camera?: {} | null, responseImage?: string | null, user?: { __typename?: 'AvatarUser', name: string, avatar?: string | null, id: string } | null } } } };
export type GendoAiRendersQueryVariables = Exact<{
versionId: Scalars['String']['input'];
@@ -4757,7 +4757,7 @@ export type GendoAiRendersQueryVariables = Exact<{
}>;
-export type GendoAiRendersQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, version?: { __typename?: 'Version', id: string, gendoAIRenders: { __typename?: 'GendoAIRenderCollection', totalCount: number, items: Array<{ __typename?: 'GendoAIRender', id: string, createdAt: string, updatedAt: string, status: string, gendoGenerationId?: string | null, prompt: string, camera?: {} | null } | null> } } | null } };
+export type GendoAiRendersQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, version: { __typename?: 'Version', id: string, gendoAIRenders: { __typename?: 'GendoAIRenderCollection', totalCount: number, items: Array<{ __typename?: 'GendoAIRender', id: string, createdAt: string, updatedAt: string, status: string, gendoGenerationId?: string | null, prompt: string, camera?: {} | null } | null> } } } };
export type ProjectVersionGendoAiRenderCreatedSubscriptionVariables = Exact<{
id: Scalars['String']['input'];
@@ -5702,6 +5702,13 @@ export type MoveProjectsDialogQueryVariables = Exact<{ [key: string]: never; }>;
export type MoveProjectsDialogQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', projects: { __typename?: 'ProjectCollection', items: Array<{ __typename?: 'Project', role?: string | null, id: string, name: string, workspace?: { __typename?: 'Workspace', id: string } | null, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number } }> } } | null };
+export type ValidateWorkspaceSlugQueryVariables = Exact<{
+ slug: Scalars['String']['input'];
+}>;
+
+
+export type ValidateWorkspaceSlugQuery = { __typename?: 'Query', validateWorkspaceSlug: boolean };
+
export type LegacyBranchRedirectMetadataQueryVariables = Exact<{
streamId: Scalars['String']['input'];
branchName: Scalars['String']['input'];
@@ -5716,7 +5723,7 @@ export type LegacyViewerCommitRedirectMetadataQueryVariables = Exact<{
}>;
-export type LegacyViewerCommitRedirectMetadataQuery = { __typename?: 'Query', project: { __typename?: 'Project', version?: { __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string } } | null } };
+export type LegacyViewerCommitRedirectMetadataQuery = { __typename?: 'Query', project: { __typename?: 'Project', version: { __typename?: 'Version', id: string, model: { __typename?: 'Model', id: string } } } };
export type LegacyViewerStreamRedirectMetadataQueryVariables = Exact<{
streamId: Scalars['String']['input'];
@@ -6086,6 +6093,7 @@ export const WorkspacePageQueryDocument = {"kind":"Document","definitions":[{"ki
export const WorkspaceProjectsQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceProjectsQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceProjectsFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceAvatar_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"defaultLogoIndex"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceAvatar_Workspace"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}}]} as unknown as DocumentNode;
export const WorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"options"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteLookupOptions"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"options"},"value":{"kind":"Variable","name":{"kind":"Name","value":"options"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceSlug"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}}]} as unknown as DocumentNode;
export const MoveProjectsDialogDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MoveProjectsDialog"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MoveProjectsDialog_User"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_User"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Project"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode;
+export const ValidateWorkspaceSlugDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ValidateWorkspaceSlug"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validateWorkspaceSlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}]}]}}]} as unknown as DocumentNode;
export const LegacyBranchRedirectMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LegacyBranchRedirectMetadata"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelByName"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode;
export const LegacyViewerCommitRedirectMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LegacyViewerCommitRedirectMetadata"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"commitId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"commitId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode;
export const LegacyViewerStreamRedirectMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LegacyViewerStreamRedirectMetadata"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode;
diff --git a/packages/frontend-2/lib/workspaces/graphql/queries.ts b/packages/frontend-2/lib/workspaces/graphql/queries.ts
index 0faecce7d..72e9998a8 100644
--- a/packages/frontend-2/lib/workspaces/graphql/queries.ts
+++ b/packages/frontend-2/lib/workspaces/graphql/queries.ts
@@ -72,3 +72,9 @@ export const moveProjectsDialogQuery = graphql(`
}
}
`)
+
+export const validateWorkspaceSlugQuery = graphql(`
+ query ValidateWorkspaceSlug($slug: String!) {
+ validateWorkspaceSlug(slug: $slug)
+ }
+`)
diff --git a/packages/frontend-2/pages/projects/[id]/index.vue b/packages/frontend-2/pages/projects/[id]/index.vue
index eaf19dc67..639ae269d 100644
--- a/packages/frontend-2/pages/projects/[id]/index.vue
+++ b/packages/frontend-2/pages/projects/[id]/index.vue
@@ -171,10 +171,10 @@ const actionsItems = computed(() => {
if (isWorkspacesEnabled.value && !project.value?.workspace?.id) {
items.push([
{
- title: 'Move...',
+ title: 'Move project...',
id: ActionTypes.Move,
disabled: !isOwner.value,
- disabledTooltip: 'Only project owners can move projects into workspaces'
+ disabledTooltip: 'Only the project owner can move this project into a workspace'
}
])
}
diff --git a/packages/server/assets/core/typedefs/modelsAndVersions.graphql b/packages/server/assets/core/typedefs/modelsAndVersions.graphql
index a10b78598..f03b38464 100644
--- a/packages/server/assets/core/typedefs/modelsAndVersions.graphql
+++ b/packages/server/assets/core/typedefs/modelsAndVersions.graphql
@@ -39,7 +39,7 @@ extend type Project {
"""
Retrieve a specific project version by its ID
"""
- version(id: String!): Version
+ version(id: String!): Version!
"""
Retrieve a specific project model by its ID
diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts
index 5db603fcc..f482f2d4f 100644
--- a/packages/server/modules/core/graph/generated/graphql.ts
+++ b/packages/server/modules/core/graph/generated/graphql.ts
@@ -1836,7 +1836,7 @@ export type Project = {
team: Array;
updatedAt: Scalars['DateTime']['output'];
/** Retrieve a specific project version by its ID */
- version?: Maybe;
+ version: Version;
/** Returns a flat list of all project versions */
versions: VersionCollection;
/** Return metadata about resources being requested in the viewer */
@@ -5508,7 +5508,7 @@ export type ProjectResolvers, ParentType, ContextType>;
team?: Resolver, ParentType, ContextType>;
updatedAt?: Resolver;
- version?: Resolver, ParentType, ContextType, RequireFields>;
+ version?: Resolver>;
versions?: Resolver>;
viewerResources?: Resolver, ParentType, ContextType, RequireFields>;
visibility?: Resolver;
diff --git a/packages/server/modules/core/graph/resolvers/versions.ts b/packages/server/modules/core/graph/resolvers/versions.ts
index 484380246..294304023 100644
--- a/packages/server/modules/core/graph/resolvers/versions.ts
+++ b/packages/server/modules/core/graph/resolvers/versions.ts
@@ -11,7 +11,7 @@ import {
batchDeleteCommits,
batchMoveCommitsFactory
} from '@/modules/core/services/commit/batchCommitActions'
-import { CommitUpdateError } from '@/modules/core/errors/commit'
+import { CommitNotFoundError, CommitUpdateError } from '@/modules/core/errors/commit'
import {
createCommitByBranchIdFactory,
markCommitReceivedAndNotify,
@@ -106,9 +106,14 @@ const batchMoveCommits = batchMoveCommitsFactory({
export = {
Project: {
async version(parent, args, ctx) {
- return await ctx.loaders.streams.getStreamCommit
+ const version = await ctx.loaders.streams.getStreamCommit
.forStream(parent.id)
.load(args.id)
+ if (!version) {
+ throw new CommitNotFoundError('Version not found')
+ }
+
+ return version
}
},
Version: {
diff --git a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts
index a2decff57..b1a487086 100644
--- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts
+++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts
@@ -1820,7 +1820,7 @@ export type Project = {
team: Array;
updatedAt: Scalars['DateTime']['output'];
/** Retrieve a specific project version by its ID */
- version?: Maybe;
+ version: Version;
/** Returns a flat list of all project versions */
versions: VersionCollection;
/** Return metadata about resources being requested in the viewer */
diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts
index 9087d829b..aa31005ee 100644
--- a/packages/server/test/graphql/generated/graphql.ts
+++ b/packages/server/test/graphql/generated/graphql.ts
@@ -1821,7 +1821,7 @@ export type Project = {
team: Array;
updatedAt: Scalars['DateTime']['output'];
/** Retrieve a specific project version by its ID */
- version?: Maybe;
+ version: Version;
/** Returns a flat list of all project versions */
versions: VersionCollection;
/** Return metadata about resources being requested in the viewer */
diff --git a/packages/ui-components/src/components/common/loading/Icon.vue b/packages/ui-components/src/components/common/loading/Icon.vue
index 2dd25d431..911c16ada 100644
--- a/packages/ui-components/src/components/common/loading/Icon.vue
+++ b/packages/ui-components/src/components/common/loading/Icon.vue
@@ -1,8 +1,25 @@
-
+
+
+
+
+
diff --git a/packages/ui-components/src/components/form/Button.vue b/packages/ui-components/src/components/form/Button.vue
index bef07387b..8d2c4043d 100644
--- a/packages/ui-components/src/components/form/Button.vue
+++ b/packages/ui-components/src/components/form/Button.vue
@@ -25,8 +25,8 @@ import { isObjectLike } from 'lodash'
import type { PropAnyComponent } from '~~/src/helpers/common/components'
import { computed, resolveDynamicComponent } from 'vue'
import type { Nullable } from '@speckle/shared'
-import { ArrowPathIcon } from '@heroicons/vue/24/solid'
import type { FormButtonStyle, FormButtonSize } from '~~/src/helpers/form/button'
+import { CommonLoadingIcon } from '~~/src/lib'
const emit = defineEmits<{
/**
@@ -122,7 +122,9 @@ const buttonType = computed(() => {
})
const isDisabled = computed(() => props.disabled || props.loading)
-const finalLeftIcon = computed(() => (props.loading ? ArrowPathIcon : props.iconLeft))
+const finalLeftIcon = computed(() =>
+ props.loading ? CommonLoadingIcon : props.iconLeft
+)
const bgAndBorderClasses = computed(() => {
const classParts: string[] = []
@@ -266,10 +268,6 @@ const buttonClasses = computed(() => {
const iconClasses = computed(() => {
const classParts: string[] = ['shrink-0']
- if (props.loading) {
- classParts.push('animate-spin')
- }
-
switch (props.size) {
case 'sm':
classParts.push('h-5 w-5 p-0.5')
diff --git a/packages/ui-components/src/components/form/TextInput.stories.ts b/packages/ui-components/src/components/form/TextInput.stories.ts
index 22337f00f..005b8c7a6 100644
--- a/packages/ui-components/src/components/form/TextInput.stories.ts
+++ b/packages/ui-components/src/components/form/TextInput.stories.ts
@@ -154,6 +154,14 @@ export const LabelLeft = mergeStories(Default, {
}
})
+export const Loading = mergeStories(Default, {
+ args: {
+ name: generateRandomName('loading'),
+ label: 'With loading spinner',
+ loading: true
+ }
+})
+
export const WithCustomRightSlot = mergeStories(Default, {
render: (args) => ({
components: { FormTextInput, FormButton },
diff --git a/packages/ui-components/src/components/form/TextInput.vue b/packages/ui-components/src/components/form/TextInput.vue
index 27c5c1599..2bcb8ca7a 100644
--- a/packages/ui-components/src/components/form/TextInput.vue
+++ b/packages/ui-components/src/components/form/TextInput.vue
@@ -37,6 +37,13 @@
aria-hidden="true"
/>
+
+
+
+
''
diff --git a/packages/ui-components/src/composables/form/textInput.ts b/packages/ui-components/src/composables/form/textInput.ts
index 0e191e0b6..a9e60bee1 100644
--- a/packages/ui-components/src/composables/form/textInput.ts
+++ b/packages/ui-components/src/composables/form/textInput.ts
@@ -26,6 +26,7 @@ export function useTextInputCore
(params: {
autoFocus?: boolean
showClear?: boolean
useLabelInErrors?: boolean
+ customErrorMessage?: string
hideErrorMessage?: boolean
color?: InputColor
labelPosition?: LabelPosition
@@ -41,11 +42,15 @@ export function useTextInputCore(params: {
}) {
const { props, inputEl, emit, options } = params
- const { value, errorMessage: error } = useField(props.name, props.rules, {
- validateOnMount: unref(props.validateOnMount),
- validateOnValueUpdate: unref(props.validateOnValueUpdate),
- initialValue: unref(props.modelValue) || undefined
- })
+ const { value, errorMessage: veeErrorMessage } = useField(
+ props.name,
+ props.rules,
+ {
+ validateOnMount: unref(props.validateOnMount),
+ validateOnValueUpdate: unref(props.validateOnValueUpdate),
+ initialValue: unref(props.modelValue) || undefined
+ }
+ )
const labelClasses = computed(() => {
const classParts = [
@@ -76,7 +81,7 @@ export function useTextInputCore(params: {
coreInputClasses.value
]
- if (error.value) {
+ if (hasError.value) {
classParts.push('!border-danger')
} else {
classParts.push('border-0 focus:ring-2 focus:ring-outline-2')
@@ -99,12 +104,19 @@ export function useTextInputCore(params: {
const internalHelpTipId = ref(nanoid())
const title = computed(() => unref(props.label) || unref(props.name))
+
const errorMessage = computed(() => {
- const base = error.value
+ if (unref(props.customErrorMessage)) {
+ return unref(props.customErrorMessage)
+ }
+
+ const base = veeErrorMessage.value
if (!base || !unref(props.useLabelInErrors)) return base
return base.replace('Value', title.value)
})
+ const hasError = computed(() => !!errorMessage.value)
+
const hideHelpTip = computed(
() => errorMessage.value && unref(props.hideErrorMessage)
)
@@ -115,7 +127,7 @@ export function useTextInputCore(params: {
)
const helpTipClasses = computed((): string => {
const classParts = ['text-body-2xs break-words']
- classParts.push(error.value ? 'text-danger' : 'text-foreground-2')
+ classParts.push(hasError.value ? 'text-danger' : 'text-foreground-2')
return classParts.join(' ')
})
const shouldShowClear = computed(() => {
@@ -154,7 +166,8 @@ export function useTextInputCore(params: {
clear,
focus,
labelClasses,
- shouldShowClear
+ shouldShowClear,
+ hasError
}
}
diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts
index 20ae1b9f7..1f4e6da35 100644
--- a/packages/viewer-sandbox/src/Sandbox.ts
+++ b/packages/viewer-sandbox/src/Sandbox.ts
@@ -1357,11 +1357,11 @@ export default class Sandbox {
url.includes('latest') ? 'AuthTokenLatest' : 'AuthToken'
) as string
const objUrls = await UrlHelper.getResourceUrls(url, authToken)
- for (const url of objUrls) {
+ for (const objUrl of objUrls) {
console.log(`Loading ${url}`)
const loader = new SpeckleLoader(
this.viewer.getWorldTree(),
- url,
+ objUrl,
authToken,
true,
undefined
@@ -1377,7 +1377,7 @@ export default class Sandbox {
console.error(`Loader warning: ${arg.message}`)
})
- await this.viewer.loadObject(loader, true)
+ void this.viewer.loadObject(loader, true)
}
localStorage.setItem('last-load-url', url)
}
diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts
index 6859ac6b6..d172c4cd7 100644
--- a/packages/viewer-sandbox/src/main.ts
+++ b/packages/viewer-sandbox/src/main.ts
@@ -105,7 +105,7 @@ const getStream = () => {
// prettier-ignore
// 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D'
// Revit sample house (good for bim-like stuff with many display meshes)
- // 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8'
+ 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8'
// 'https://latest.speckle.systems/streams/c1faab5c62/commits/ab1a1ab2b6'
// 'https://app.speckle.systems/streams/da9e320dad/commits/5388ef24b8'
// 'https://latest.speckle.systems/streams/58b5648c4d/commits/60371ecb2d'
@@ -424,7 +424,8 @@ const getStream = () => {
// 'https://app.speckle.systems/projects/00a5c443d6/models/de56edf901'
- 'https://app.speckle.systems/projects/6cf358a40e/models/e01ffbc891@f1ddc19011'
+ // 'https://app.speckle.systems/projects/6cf358a40e/models/e01ffbc891@f1ddc19011'
+ // 'https://latest.speckle.systems/projects/2c5c4cd493/models/cb20c2e87b02003d4b2ddf4df96912b9,8a2c9a45093fecb42273607d0c936d66'
)
}
diff --git a/packages/viewer/src/modules/batching/Batcher.ts b/packages/viewer/src/modules/batching/Batcher.ts
index 9049fabc0..003c60d43 100644
--- a/packages/viewer/src/modules/batching/Batcher.ts
+++ b/packages/viewer/src/modules/batching/Batcher.ts
@@ -74,7 +74,7 @@ export default class Batcher {
await pause.wait(50)
}
- let instancedNodes = worldTree.findId(g)
+ let instancedNodes = worldTree.findId(g, renderTree.subtreeId)
if (!instancedNodes) {
continue
}
@@ -99,7 +99,7 @@ export default class Batcher {
}
for (const v in instancedBatches) {
for (let k = 0; k < instancedBatches[v].length; k++) {
- const nodes = worldTree.findId(instancedBatches[v][k])
+ const nodes = worldTree.findId(instancedBatches[v][k], renderTree.subtreeId)
if (!nodes) continue
/** Make sure entire instance set is instanced */
let instanced = true
diff --git a/packages/viewer/src/modules/tree/RenderTree.ts b/packages/viewer/src/modules/tree/RenderTree.ts
index 13ea770a9..06beaff53 100644
--- a/packages/viewer/src/modules/tree/RenderTree.ts
+++ b/packages/viewer/src/modules/tree/RenderTree.ts
@@ -15,6 +15,10 @@ export class RenderTree {
return this.root.model.id
}
+ public get subtreeId(): number {
+ return this.root.model.subtreeId
+ }
+
public constructor(tree: WorldTree, subtreeRoot: TreeNode) {
this.tree = tree
this.root = subtreeRoot
@@ -192,8 +196,11 @@ export class RenderTree {
})
}
- public getRenderViewsForNodeId(id: string): NodeRenderView[] | null {
- const nodes = this.tree.findId(id)
+ public getRenderViewsForNodeId(
+ id: string,
+ subtreeId?: number
+ ): NodeRenderView[] | null {
+ const nodes = this.tree.findId(id, subtreeId)
if (!nodes) {
Logger.warn(`Id ${id} does not exist`)
return null