Compare commits

..

11 Commits

Author SHA1 Message Date
oguzhankoral 016dbd97da Handle connectors that not deployed by Speckle 2025-06-19 19:36:37 +03:00
Oğuzhan Koral e55c0ca7dd Change the messaging for personal projects (#34) 2025-06-17 12:57:35 +03:00
Dimitrie Stefanescu 8ef79f7a7c Merge pull request #33 from specklesystems/oguzhan/hide-receive-button-on-navbar
Fix: hide receive button according to binding
2025-06-17 10:35:19 +01:00
oguzhankoral 79951f7cf7 Hide receive button according to binding 2025-06-17 12:22:23 +03:00
Dimitrie Stefanescu cf70ddc79b fix: don't throw on zero affected elements on automate results (#32) 2025-06-09 15:58:01 +03:00
Dimitrie Stefanescu 4e16813c75 Merge pull request #31 from specklesystems/dim/correctly-respect-server-roles
feat: correctly respects server roles when adding by url
2025-06-05 14:28:04 +01:00
Dimitrie Stefanescu ee4e7576ad feat: correctly respects server roles 2025-06-05 14:22:52 +01:00
Oğuzhan Koral 3ba11c983b Feat: open workspace and server in web buttons (#30)
* Open workspace and server buttons

* Do not have navigator for personal projects
2025-06-04 19:53:52 +03:00
Dimitrie Stefanescu d92dcf5342 Merge pull request #29 from specklesystems/dimitrie/cnx-1968-update-dui-and-appspsecklesystems-doc-links
feat: points docs to docs.speckle.systems
2025-06-03 13:42:05 +01:00
Dimitrie Stefanescu 55afedab68 feat: points docs to docs.speckle.systems 2025-06-03 12:58:24 +01:00
Dimitrie Stefanescu a8d948ec71 Merge pull request #25 from specklesystems/intercom
Adds intercom
2025-06-03 10:30:38 +01:00
12 changed files with 154 additions and 95 deletions
@@ -49,6 +49,7 @@ const applicationIds = computed(() => {
})
const handleClick = async () => {
if (applicationIds.value.length === 0) return
await app.$baseBinding.highlightObjects(applicationIds.value)
}
+3 -1
View File
@@ -25,6 +25,7 @@
</div>
<div class="relative group flex items-center">
<FormButton
v-if="app.$receiveBinding"
v-tippy="'Load a model from Speckle into this file'"
color="outline"
size="sm"
@@ -52,10 +53,11 @@
{{ hostAppStore.connectorVersion }}
</div>
<HeaderButton
v-if="hostAppStore.isDistributedBySpeckle"
v-tippy="'Documentation and help'"
@click="
app.$openUrl(
`https://www.speckle.systems/connectors/${hostAppStore.hostAppName}?utm=dui`
`https://docs.speckle.systems/connectors/${hostAppStore.hostAppName}?utm=dui`
)
"
>
+14 -65
View File
@@ -12,78 +12,27 @@
Move your projects to a workspace
</h1>
<p class="mb-2">
<span class="text-sm"></span>
<span class="text-xs">
We are making workspaces the default way to work in Speckle.
</span>
</p>
<p class="mb-1">
<span class="text-sm"></span>
<span class="text-xs">
Introducing
<FormButton
text
link
external
size="sm"
class="font-semibold"
@click="$openUrl(`https://www.speckle.systems/pricing`)"
>
new pricing
</FormButton>
including limits to the free plan.
Personal projects are being phased out. Move your projects to a
workspace to create new projects and models, invite new project
collaborators, and view comments and versions older than 7 days. By
January 1st 2026, all projects will be archived if not moved into a
workspace.
</span>
</p>
<FormButton
text
color="primary"
size="sm"
:class="showMore ? `mb-1` : ``"
:icon-right="showMore ? ChevronUpIcon : ChevronDownIcon"
@click="showMore = !showMore"
class="mb-2"
@click="
$openUrl(
`${accountStore.activeAccount.accountInfo.serverInfo.url}/projects`
)
"
>
{{ showMore ? 'Show less' : 'Show timeline' }}
Move projects
</FormButton>
<div v-show="showMore">
<hr />
<h3 class="font-medium text-warning-darker my-1">By June 1st 2025</h3>
<p class="text-xs mb-1">Move your projects to a workspace to:</p>
<ul class="list-disc list-inside pl-2 mb-2">
<li>
<span class="text-xs font-medium">
Create new projects and models
</span>
<span class="text-xs">
(will be disabled for personal projects; existing projects and
models stay editable)
</span>
</li>
<li>
<span class="text-xs font-medium">
Invite new project collaborators
</span>
<span class="text-xs">
(new invites will be unavailable for personal projects)
</span>
</li>
<li>
<span class="text-xs font-medium">
Preserve version and comment history
</span>
<span class="text-xs">
(history is reduced to 7 days for personal projects)
</span>
</li>
</ul>
<h3 class="font-medium text-warning-darker">By Janury 1st 2026</h3>
<span class="text-xs mb-1">
All projects will be archived if not moved into a workspace. Don't
worry, we'll give you plenty of reminders before then.
</span>
</div>
</div>
</div>
</div>
@@ -92,8 +41,8 @@
</template>
<script setup lang="ts">
import { ChevronUpIcon, ChevronDownIcon } from '@heroicons/vue/20/solid'
import { useAccountStore } from '~/store/accounts'
const accountStore = useAccountStore()
const { $openUrl } = useNuxtApp()
const showMore = ref(false)
</script>
+12
View File
@@ -36,6 +36,18 @@
<div class="min-w-0 truncate flex-grow text-left">
<span>{{ selectedWorkspace.name }}</span>
</div>
<button
v-if="selectedWorkspace.slug"
v-tippy="'Open workspace in browser'"
class="transition mr-1 opacity-70 hover:opacity-100"
@click.stop="
$openUrl(
`${accountStore.activeAccount.accountInfo.serverInfo.url}/workspaces/${selectedWorkspace.slug}`
)
"
>
<ArrowTopRightOnSquareIcon class="w-3.5" />
</button>
<ChevronDownIcon class="h-3 w-3 shrink-0" />
</button>
</template>
+16
View File
@@ -21,11 +21,27 @@
<span>{{ workspace.name }}</span>
</div>
</div>
<button
v-if="workspace.slug"
v-tippy="'Open workspace in browser'"
class="hidden transition mr-1 opacity-70 group-hover:block"
@click.stop="
$openUrl(
`${accountStore.activeAccount.accountInfo.serverInfo.url}/workspaces/${workspace.slug}`
)
"
>
<ArrowTopRightOnSquareIcon class="w-3.5" />
</button>
</div>
</button>
</template>
<script setup lang="ts">
import type { WorkspaceListWorkspaceItemFragment } from '~/lib/common/generated/gql/graphql'
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/outline'
import { useAccountStore } from '~/store/accounts'
const accountStore = useAccountStore()
defineProps<{
workspace: WorkspaceListWorkspaceItemFragment
+6
View File
@@ -29,6 +29,7 @@ type Documents = {
"\n fragment AutomationRunItem on AutomateRun {\n id\n status\n automation {\n id\n name\n }\n functionRuns {\n ...AutomateFunctionRunItem\n }\n }\n": typeof types.AutomationRunItemFragmentDoc,
"\n query AutomationStatus($projectId: String!, $modelId: String!) {\n project(id: $projectId) {\n model(id: $modelId) {\n automationsStatus {\n id\n status\n automationRuns {\n ...AutomationRunItem\n }\n }\n }\n }\n }\n": typeof types.AutomationStatusDocument,
"\n query WorkspaceListQuery(\n $limit: Int!\n $filter: UserWorkspacesFilter\n $cursor: String\n ) {\n activeUser {\n id\n workspaces(limit: $limit, filter: $filter, cursor: $cursor) {\n totalCount\n cursor\n items {\n ...WorkspaceListWorkspaceItem\n }\n }\n }\n }\n": typeof types.WorkspaceListQueryDocument,
"\n query ActiveUser {\n activeUser {\n role\n id\n name\n }\n }\n": typeof types.ActiveUserDocument,
"\n query CanCreatePersonalProject {\n activeUser {\n permissions {\n canCreatePersonalProject {\n authorized\n code\n message\n payload\n }\n }\n }\n }\n": typeof types.CanCreatePersonalProjectDocument,
"\n query CanCreateProjectInWorkspace($workspaceId: String!) {\n workspace(id: $workspaceId) {\n permissions {\n canCreateProject {\n authorized\n code\n message\n payload\n }\n }\n }\n }\n": typeof types.CanCreateProjectInWorkspaceDocument,
"\n query CanCreateModelInProject($projectId: String!) {\n project(id: $projectId) {\n permissions {\n canCreateModel {\n authorized\n code\n message\n }\n }\n }\n }\n": typeof types.CanCreateModelInProjectDocument,
@@ -70,6 +71,7 @@ const documents: Documents = {
"\n fragment AutomationRunItem on AutomateRun {\n id\n status\n automation {\n id\n name\n }\n functionRuns {\n ...AutomateFunctionRunItem\n }\n }\n": types.AutomationRunItemFragmentDoc,
"\n query AutomationStatus($projectId: String!, $modelId: String!) {\n project(id: $projectId) {\n model(id: $modelId) {\n automationsStatus {\n id\n status\n automationRuns {\n ...AutomationRunItem\n }\n }\n }\n }\n }\n": types.AutomationStatusDocument,
"\n query WorkspaceListQuery(\n $limit: Int!\n $filter: UserWorkspacesFilter\n $cursor: String\n ) {\n activeUser {\n id\n workspaces(limit: $limit, filter: $filter, cursor: $cursor) {\n totalCount\n cursor\n items {\n ...WorkspaceListWorkspaceItem\n }\n }\n }\n }\n": types.WorkspaceListQueryDocument,
"\n query ActiveUser {\n activeUser {\n role\n id\n name\n }\n }\n": types.ActiveUserDocument,
"\n query CanCreatePersonalProject {\n activeUser {\n permissions {\n canCreatePersonalProject {\n authorized\n code\n message\n payload\n }\n }\n }\n }\n": types.CanCreatePersonalProjectDocument,
"\n query CanCreateProjectInWorkspace($workspaceId: String!) {\n workspace(id: $workspaceId) {\n permissions {\n canCreateProject {\n authorized\n code\n message\n payload\n }\n }\n }\n }\n": types.CanCreateProjectInWorkspaceDocument,
"\n query CanCreateModelInProject($projectId: String!) {\n project(id: $projectId) {\n permissions {\n canCreateModel {\n authorized\n code\n message\n }\n }\n }\n }\n": types.CanCreateModelInProjectDocument,
@@ -170,6 +172,10 @@ export function graphql(source: "\n query AutomationStatus($projectId: String!,
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query WorkspaceListQuery(\n $limit: Int!\n $filter: UserWorkspacesFilter\n $cursor: String\n ) {\n activeUser {\n id\n workspaces(limit: $limit, filter: $filter, cursor: $cursor) {\n totalCount\n cursor\n items {\n ...WorkspaceListWorkspaceItem\n }\n }\n }\n }\n"): (typeof documents)["\n query WorkspaceListQuery(\n $limit: Int!\n $filter: UserWorkspacesFilter\n $cursor: String\n ) {\n activeUser {\n id\n workspaces(limit: $limit, filter: $filter, cursor: $cursor) {\n totalCount\n cursor\n items {\n ...WorkspaceListWorkspaceItem\n }\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 query ActiveUser {\n activeUser {\n role\n id\n name\n }\n }\n"): (typeof documents)["\n query ActiveUser {\n activeUser {\n role\n id\n name\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
+14
View File
@@ -4644,6 +4644,8 @@ export type WorkspaceInviteCreateInput = {
email?: InputMaybe<Scalars['String']['input']>;
/** Defaults to the member role, if not specified */
role?: InputMaybe<WorkspaceRole>;
/** The workspace seat type to assign to the user upon accepting the invite. */
seatType?: InputMaybe<WorkspaceSeatType>;
/** Defaults to User, if not specified */
serverRole?: InputMaybe<ServerRole>;
/** Either this or email must be filled */
@@ -4902,6 +4904,7 @@ export type WorkspacePlanUsage = {
export enum WorkspacePlans {
Academia = 'academia',
Enterprise = 'enterprise',
Free = 'free',
Pro = 'pro',
ProUnlimited = 'proUnlimited',
@@ -4924,6 +4927,11 @@ export type WorkspaceProjectInviteCreateInput = {
email?: InputMaybe<Scalars['String']['input']>;
/** Defaults to the contributor role, if not specified */
role?: InputMaybe<Scalars['String']['input']>;
/**
* The workspace seat type to assign to the user upon accepting the invite
* (if user is a workspace member already, the seat type will be updated)
*/
seatType?: InputMaybe<WorkspaceSeatType>;
/** Can only be specified if guest mode is on or if the user is an admin */
serverRole?: InputMaybe<Scalars['String']['input']>;
/** Either this or email must be filled */
@@ -5216,6 +5224,11 @@ export type WorkspaceListQueryQueryVariables = Exact<{
export type WorkspaceListQueryQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', id: string, workspaces: { __typename?: 'WorkspaceCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Workspace', id: string, slug: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null, role?: string | null, readOnly: boolean, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean } | null, permissions: { __typename?: 'WorkspacePermissionChecks', canCreateProject: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string } } }> } } | null };
export type ActiveUserQueryVariables = Exact<{ [key: string]: never; }>;
export type ActiveUserQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', role?: string | null, id: string, name: string } | null };
export type CanCreatePersonalProjectQueryVariables = Exact<{ [key: string]: never; }>;
@@ -5393,6 +5406,7 @@ export const StreamAccessRequestCreateDocument = {"kind":"Document","definitions
export const CreateAutomationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateAutomation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAutomationCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automationMutations"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CreateAutomationMutation, CreateAutomationMutationVariables>;
export const AutomationStatusDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AutomationStatus"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"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":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"model"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationRunItem"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateFunctionRunItem"},"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":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationRunItem"},"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":"status"}},{"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":"FragmentSpread","name":{"kind":"Name","value":"AutomateFunctionRunItem"}}]}}]}}]} as unknown as DocumentNode<AutomationStatusQuery, AutomationStatusQueryVariables>;
export const WorkspaceListQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceListQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UserWorkspacesFilter"}}},{"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":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceListWorkspaceItem"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceListWorkspaceItem"},"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":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canCreateProject"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authorized"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]} as unknown as DocumentNode<WorkspaceListQueryQuery, WorkspaceListQueryQueryVariables>;
export const ActiveUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ActiveUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<ActiveUserQuery, ActiveUserQueryVariables>;
export const CanCreatePersonalProjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CanCreatePersonalProject"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canCreatePersonalProject"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authorized"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"payload"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CanCreatePersonalProjectQuery, CanCreatePersonalProjectQueryVariables>;
export const CanCreateProjectInWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CanCreateProjectInWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canCreateProject"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authorized"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"payload"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CanCreateProjectInWorkspaceQuery, CanCreateProjectInWorkspaceQueryVariables>;
export const CanCreateModelInProjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CanCreateModelInProject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"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":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canCreateModel"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"authorized"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CanCreateModelInProjectQuery, CanCreateModelInProjectQueryVariables>;
+4 -2
View File
@@ -7,7 +7,8 @@ import type {
} from '~/lib/common/generated/gql/graphql'
import {
projectAddByUrlQueryWithoutVersion,
projectAddByUrlQueryWithVersion
projectAddByUrlQueryWithVersion,
userInfoAndServerRoleQuery
} from '~/lib/graphql/mutationsAndQueries'
import { omit } from 'lodash-es'
import { useDebounceFn } from '@vueuse/core'
@@ -60,6 +61,7 @@ export function useAddByUrl() {
const { projectId, modelId, versionId } = params
const apollo = (acc as DUIAccount).client
const userInfoRes = await apollo.query({ query: userInfoAndServerRoleQuery })
let project: ProjectListProjectItemFragment | undefined = undefined,
model: ModelListModelItemFragment | undefined = undefined,
@@ -113,7 +115,7 @@ export function useAddByUrl() {
? project.permissions.canPublish.authorized
: project.permissions.canLoad.authorized
if (!hasAccess) {
if (!hasAccess && userInfoRes.data.activeUser?.role !== 'server:admin') {
urlParseError.value = errorMessage
return
}
+40 -15
View File
@@ -2,6 +2,16 @@ import type { ToastNotification } from '@speckle/ui-components'
import { ToastNotificationType } from '@speckle/ui-components'
import { useHostAppStore } from '~/store/hostApp'
export class UpdateError extends Error {
constructor(message: string) {
super(message)
this.name = 'FetchError'
// Required when extending Error in TypeScript
Object.setPrototypeOf(this, new.target.prototype)
}
}
type Versions = {
Versions: Version[]
}
@@ -35,24 +45,39 @@ export function useUpdateConnector() {
}
async function getVersions() {
const response = await fetch(
`https://releases.speckle.dev/manager2/feeds/${hostApp.hostAppName?.toLowerCase()}-v3.json`,
{
method: 'GET'
try {
// End point to get list of versions that deployed by Speckle's pipeline
const response = await fetch(
`https://releases.speckle.dev/manager2/feeds/${hostApp.hostAppName?.toLowerCase()}-v3.json`,
{
method: 'GET'
}
)
if (!response.ok) {
// It is the only way to understand the connector is distributed by Speckle or not.
throw new UpdateError('Failed to fetch versions')
}
)
if (!response.ok) {
throw new Error('Failed to fetch versions')
const data = (await response.json()) as unknown as Versions
const sortedVersions = data.Versions.sort(function (a: Version, b: Version) {
return new Date(b.Date).getTime() - new Date(a.Date).getTime()
})
versions.value = sortedVersions
latestAvailableVersion.value = sortedVersions[0]
hostApp.setLatestAvailableVersion(sortedVersions[0])
} catch (err) {
if (err instanceof TypeError && err.message === 'Failed to fetch') {
// When user has network issue in between, actually it is not so likely because regardless user need network to be able to render netlify page
throw new Error('Network error')
} else if (err instanceof UpdateError) {
// We set the flag to use it in relavant places, hide some documentation related buttons etc..
hostApp.setIsDistributedBySpeckle(false)
} else {
// Rest of the possibilites that we trigger toast
throw new Error('Unknown error occurred')
}
}
const data = (await response.json()) as unknown as Versions
const sortedVersions = data.Versions.sort(function (a: Version, b: Version) {
return new Date(b.Date).getTime() - new Date(a.Date).getTime()
})
versions.value = sortedVersions
latestAvailableVersion.value = sortedVersions[0]
hostApp.setLatestAvailableVersion(sortedVersions[0])
}
return { checkUpdate }
+10
View File
@@ -193,6 +193,16 @@ export const workspacesListQuery = graphql(`
}
`)
export const userInfoAndServerRoleQuery = graphql(`
query ActiveUser {
activeUser {
role
id
name
}
}
`)
export const canCreatePersonalProjectQuery = graphql(`
query CanCreatePersonalProject {
activeUser {
+27 -12
View File
@@ -11,11 +11,23 @@
class="fixed h-screen w-screen flex items-center justify-center pr-2 pointer-events-none"
>
<LayoutPanel fancy-glow class="transition pointer-events-auto w-[90%]">
<h1
class="text-heading-lg w-full bg-gradient-to-r from-blue-500 via-blue-400 to-blue-600 inline-block py-1 text-transparent bg-clip-text"
>
Hello!
</h1>
<div class="flex">
<h1
class="text-heading-lg w-full bg-gradient-to-r from-blue-500 via-blue-400 to-blue-600 inline-block py-1 text-transparent bg-clip-text"
>
Hello!
</h1>
<button
v-if="accountStore.activeAccount"
v-tippy="'Open web app'"
class="transition mr-1 opacity-70 hover:opacity-100"
@click.stop="
$openUrl(accountStore.activeAccount.accountInfo.serverInfo.url)
"
>
<ArrowTopRightOnSquareIcon class="w-4" />
</button>
</div>
<!-- Returning null from host app is blocked by CI for now, hence host app send here empty documentInfo, we check it's id whether null or not. -->
<div v-if="!!store.documentInfo?.id">
<div class="text-foreground-2 text-body-sm">
@@ -57,7 +69,10 @@
</div>
</div>
<!-- TEMPORARY MESSAGE TO USER! will be deleted -->
<div class="mt-2 bg-highlight-1 rounded-md p-2">
<div
v-if="store.isDistributedBySpeckle"
class="mt-2 bg-highlight-1 rounded-md p-2"
>
<h1
class="text-heading-sm w-full bg-gradient-to-r from-blue-500 via-blue-400 to-blue-600 inline-block py-1 text-transparent bg-clip-text"
>
@@ -74,15 +89,14 @@
full-width
@click="
app.$openUrl(
`https://speckle.systems/connectors/${store.hostAppName}`
`https://docs.speckle.systems/connectors/${store.hostAppName}?utm=dui`
)
"
>
<span class="capitalize">{{ store.hostAppName }}&nbsp;</span>
documentation
Getting started
</FormButton>
</div>
<!--
<FormButton
text
size="sm"
@@ -98,7 +112,7 @@
<span class="text-foreground-2 text-body-3xs truncate line-clamp-1">
New connectors announcement
</span>
</FormButton>
</FormButton> -->
</div>
</LayoutPanel>
</div>
@@ -153,7 +167,8 @@ import { storeToRefs } from 'pinia'
import {
ArrowDownTrayIcon,
ArrowUpTrayIcon,
ArrowPathIcon
ArrowPathIcon,
ArrowTopRightOnSquareIcon
} from '@heroicons/vue/24/solid'
import { useAccountStore } from '~~/store/accounts'
import { useHostAppStore } from '~~/store/hostApp'
+7
View File
@@ -43,6 +43,7 @@ export const useHostAppStore = defineStore('hostAppStore', () => {
const accountsStore = useAccountStore()
const { checkUpdate } = useUpdateConnector()
const isDistributedBySpeckle = ref<boolean>(true)
const latestAvailableVersion = ref<Version | null>(null)
const currentNotification = ref<Nullable<ToastNotification>>(null)
@@ -85,6 +86,10 @@ export const useHostAppStore = defineStore('hostAppStore', () => {
hostAppError.value = error
}
const setIsDistributedBySpeckle = (val: boolean) => {
isDistributedBySpeckle.value = val
}
/**
* Model Card Operations
*/
@@ -734,6 +739,8 @@ export const useHostAppStore = defineStore('hostAppStore', () => {
availableViews,
navisworksAvailableSavedSets,
availableSelectSendFilters,
isDistributedBySpeckle,
setIsDistributedBySpeckle,
setNotification,
setModelError,
setLatestAvailableVersion,