Compare commits

...

57 Commits

Author SHA1 Message Date
Dimitrie Stefanescu 65a9d3e485 Merge branch 'main' into intercom 2025-06-03 09:30:43 +01:00
Oğuzhan Koral be631746b9 Fix explore plans (#28) 2025-06-02 16:25:00 +03:00
Dimitrie Stefanescu 20c43f2108 feat: adds last guards 2025-05-30 17:59:53 +01:00
Dimitrie Stefanescu af0de85ef7 chore: comment 2025-05-30 17:38:02 +01:00
Dimitrie Stefanescu 7c54845a05 removes runtime config 2025-05-30 17:37:14 +01:00
Oğuzhan Koral cbec244443 Fix: exclude incomplete workspaces (#26)
* Exclude incomplete workspaces

* get rid of from computed value
2025-05-30 19:36:52 +03:00
Dimitrie Stefanescu a2a9ab1f4b another try 2025-05-30 17:36:35 +01:00
Dimitrie Stefanescu 2f87c34272 reverts bad change 2025-05-30 17:34:30 +01:00
Dimitrie Stefanescu 7fde35e639 feat: maybe fix 2025-05-30 17:27:54 +01:00
Dimitrie Stefanescu 97765d84ca fix: maybe prod fix 2025-05-30 16:56:21 +01:00
Dimitrie Stefanescu 9d15be73ad Merge branch 'intercom' of https://github.com/specklesystems/speckle-connectors-dui into intercom 2025-05-30 16:52:00 +01:00
Dimitrie Stefanescu 45f763a2ce feat: makes intercom plugin client side only 2025-05-30 16:48:46 +01:00
Oğuzhan Koral 3c6bda7af9 Merge branch 'main' into intercom 2025-05-30 17:52:00 +03:00
Dimitrie Stefanescu 007794dae2 chore: just a comment 2025-05-30 15:24:41 +01:00
Dimitrie Stefanescu f8912338cb chore: unfies watch logic on active account 2025-05-30 15:11:42 +01:00
Dimitrie Stefanescu f932fa46a3 feat: stylign + account handling changes for intercom, when no accounts are present 2025-05-30 15:07:32 +01:00
Oğuzhan Koral 292d2bf0bb Feat: Handle new automate schema (#24)
* Handle new automate schema

* Get rid of from old schema for automate
2025-05-30 17:04:22 +03:00
Dimitrie Stefanescu 43340d9b52 feat: maybe fix for revit 2022 2025-05-30 14:44:06 +01:00
Dimitrie Stefanescu 4898a9e2e9 chore: console.log etc cleanup 2025-05-30 14:25:52 +01:00
Dimitrie Stefanescu 19b982e2e3 feat: adds intercom. wip, revit 2022 seems to not like it 2025-05-30 14:14:48 +01:00
Dimitrie Stefanescu e3cf896c14 Merge pull request #23 from specklesystems/dim/url-fixes
feat: removes buttons if there's a faulty url parsed
2025-05-27 17:42:35 +01:00
Dimitrie Stefanescu 34d855212f Merge branch 'main' into dim/url-fixes 2025-05-27 17:30:31 +01:00
Dimitrie Stefanescu 8d159547d4 feat: removes buttons if there's a faulty url parsed 2025-05-27 17:28:37 +01:00
Dimitrie Stefanescu 0c3ee8b38f Merge pull request #22 from specklesystems/dimitrie/cnx-1781-in-the-revit-connector-add-the-option-to-select-all-the
feat: adds select/deselect all button in revit categories filter
2025-05-26 14:26:26 +01:00
Dimitrie Stefanescu c51f282644 feat: adds select/deselect all button in revit categories filter 2025-05-26 13:48:31 +01:00
Dimitrie Stefanescu 1faea0aec2 Merge pull request #21 from specklesystems/dimitrie/cnx-598-version-message
Allows to set a version message post send
2025-05-26 11:55:45 +01:00
Dimitrie Stefanescu 5e97acf4c0 chore: ogu should be happy now 2025-05-26 11:45:03 +01:00
Dimitrie Stefanescu 9c7413d630 chore: linting errs 2025-05-26 10:43:36 +01:00
Dimitrie Stefanescu 89acd7ca80 chore: comment on the hack 2025-05-26 10:40:30 +01:00
Dimitrie Stefanescu 2e51a2fb3c feat: adds mp 2025-05-26 10:37:07 +01:00
Dimitrie Stefanescu 644754262c feat: wraps up set version message 2025-05-26 10:36:54 +01:00
Dimitrie Stefanescu b28129c30e feat: sets message. todo: fix hack when needing to query for latest version 2025-05-22 21:39:00 +01:00
Dimitrie Stefanescu 305ad36cac feat: wip 2025-05-22 20:09:33 +01:00
Dimitrie Stefanescu 554d0ee478 Merge pull request #20 from specklesystems/dim/doc-changed-event-removal
feat: removes doc changed event
2025-05-22 10:09:29 +01:00
Dimitrie Stefanescu 33fd9c65e3 feat: removes doc changed event 2025-05-22 10:01:52 +01:00
Dimitrie Stefanescu ebaee49fe8 Merge pull request #17 from specklesystems/dimitrie/cnx-1859-bring-back-search-and-create-over-project-and-model
feat: reorganises project creation
2025-05-22 09:21:04 +01:00
Dimitrie Stefanescu b877c1d321 feat: ogu is now happy 2025-05-21 19:05:08 +01:00
Dimitrie Stefanescu be4dc87b3e Merge branch 'main' into dimitrie/cnx-1859-bring-back-search-and-create-over-project-and-model 2025-05-21 19:02:20 +01:00
Dimitrie Stefanescu 94ddc486aa chore: cleanup 2025-05-21 18:56:49 +01:00
Dimitrie Stefanescu ac5984d184 feat: prefills model name, disables create button if empty name, makes notificaiton buttons primary 2025-05-21 18:56:10 +01:00
Dimitrie Stefanescu 8dae170592 feat: brings back model creation from search & cleanup 2025-05-21 18:38:10 +01:00
Dimitrie Stefanescu d6f371c7d2 chore: removes console.logs 2025-05-21 18:13:04 +01:00
Dimitrie Stefanescu cbf9e8d578 fix: handles correctly servers with no workspaces 2025-05-21 18:04:26 +01:00
Dimitrie Stefanescu 6f10519e6f chore: cleanup 2025-05-21 17:37:35 +01:00
Dimitrie Stefanescu 590b8b8872 feat: upgrade actions 2025-05-21 17:34:51 +01:00
Dimitrie Stefanescu 2514b6c606 chore: removes unused comp 2025-05-21 17:22:52 +01:00
Dimitrie Stefanescu af37112a5f feat: reorganises project creation 2025-05-21 17:20:24 +01:00
Oğuzhan Koral d224b33bc8 Remove toast for update (#11) 2025-05-21 00:16:21 +03:00
Adam Hathcock 377cfc2f65 Feat(preview): get preview image over rest api (#8)
* pass token to the model card to allow it to be used when loading a preview

* fix lint conditons

* composable for preview url and reuse in version and model cards

* remove unused import

* handle no version scenario

* remove console log

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2025-05-20 20:53:52 +03:00
Dimitrie Stefanescu fdd13170f5 Merge pull request #9 from specklesystems/dim/codegen-without-local-server
feat: enables running the gql codegen without having a local server running
2025-05-20 18:20:10 +01:00
Dimitrie Stefanescu e70310654b Merge pull request #10 from specklesystems/dimitrie/cnx-1656-default-model-name-input-doesnt-work
fix: in the model create dialog, always defaults to either a model name
2025-05-20 18:19:50 +01:00
Dimitrie Stefanescu 8bb14d3389 fix: in the model create dialog, always defaults to either a model name 2025-05-20 18:01:01 +01:00
Dimitrie Stefanescu 93e4762b6a feat: enables running the gql codegen without having a local server running 2025-05-20 17:47:10 +01:00
Oğuzhan Koral 21a4bd4076 Feat: personal projects messaging (#7)
* Remember the personal projects choose

* Remove unused workspace selector

* Messaging about personal projects

* Pushy on move personal project into workspace
2025-05-16 18:18:25 +03:00
Oğuzhan Koral 82c95aab58 Remove read access remaining from old logic (#6) 2025-05-15 16:05:05 +03:00
Oğuzhan Koral fe77ede49e feat: introduce CI linting & fix various issues (#5)
* introduce CI checks

* fixx

* add caching

* fixes

* wip

* server bridge linting

* No lint errors

* fix paths on lint:prettier

* make files pretty again

* fix stylelint

* fix lock

---------

Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
2025-05-14 10:05:51 +03:00
Oğuzhan Koral f70915f485 Update README.md (#4) 2025-05-13 16:29:38 +03:00
66 changed files with 2226 additions and 2665 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
HOST=0.0.0.0
HOST=127.0.0.1
PORT=8082
NUXT_PUBLIC_MIXPANEL_TOKEN_ID=acd87c5a50b56df91a795e999812a3a4
-1
View File
File diff suppressed because one or more lines are too long
+44
View File
@@ -0,0 +1,44 @@
name: Linting
on:
pull_request:
branches:
- main
jobs:
lint-and-build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.14.0'
- name: Enable Corepack and Install Correct Yarn Version
run: |
corepack enable
corepack prepare yarn@$(jq -r .packageManager package.json | cut -d'@' -f2) --activate
yarn --version
- name: Cache node_modules
uses: actions/cache@v4
with:
path: |
**/node_modules
.yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install Dependencies
run: yarn install --immutable
- name: Run Linter
run: yarn lint
- name: Run generate
run: yarn generate
+35
View File
@@ -0,0 +1,35 @@
node_modules
build
dist
dist2
dist-*
coverage
.nyc_output
.output
.nuxt
**/nuxt-modules/**/templates/*.js
/lib/common/generated/**/*
package-lock.json
yarn.lock
.yarn
# Profiler output
events.json
# Prettier doesn't understand the syntax inside the Yaml files, because of the brackets
utils/helm/speckle-server/templates
# Optional eslint cache
.eslintcache
.venv
venv
.*.{ts,js,vue,tsx,jsx}
**/generated/**/*
**/generated/graphql.ts
storybook-static
.tshy
.tshy-build
+11
View File
@@ -0,0 +1,11 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": false,
"endOfLine": "auto",
"bracketSpacing": true,
"vueIndentScriptAndStyle": false,
"htmlWhitespaceSensitivity": "ignore",
"printWidth": 88,
"singleQuote": true
}
+16 -16
View File
@@ -1,18 +1,18 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"Vue.volar",
"bradlc.vscode-tailwindcss",
"stylelint.vscode-stylelint",
"cpylua.language-postcss",
"graphql.vscode-graphql",
"graphql.vscode-graphql-syntax"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": ["octref.vetur"]
}
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"Vue.volar",
"bradlc.vscode-tailwindcss",
"stylelint.vscode-stylelint",
"cpylua.language-postcss",
"graphql.vscode-graphql",
"graphql.vscode-graphql-syntax"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": ["octref.vetur"]
}
+1 -1
View File
@@ -1 +1 @@
nodeLinker: node-modules
nodeLinker: node-modules
+6 -12
View File
@@ -1,6 +1,6 @@
# dui3
# Speckle Connectors DUI
DUIv3 is a Speckle interface embedded inside the desktop connectors that allows users to interact with them - sync streams, manage servers etc. It's built in Vue 3 with Nuxt 3 and only supports client side rendering.
DUI v3 is a Speckle interface embedded inside the desktop connectors that allows users to interact with them - sync streams, manage servers etc. It's built in Vue 3 with Nuxt 3 and only supports client side rendering.
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
@@ -15,12 +15,12 @@ yarn install
And create an `.env` file from `.env.example`.
## Development Server
## Development
Start the development server on `http://localhost:3000`
Start the development server on `http://localhost:8082`
```bash
npm run dev
yarn dev
```
## Production
@@ -28,13 +28,7 @@ npm run dev
Build the application for production:
```bash
npm run build
```
Locally preview production build:
```bash
npm run preview
yarn build
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information...
+10 -4
View File
@@ -13,15 +13,19 @@
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { useConfigStore } from '~/store/config'
import { useAccountStore } from '~/store/accounts'
import { useHostAppStore } from '~/store/hostApp'
import { storeToRefs } from 'pinia'
const uiConfigStore = useConfigStore()
const { isDarkTheme } = storeToRefs(uiConfigStore)
const hostAppStore = useHostAppStore()
const { connectorVersion, hostAppName, hostAppVersion } = storeToRefs(hostAppStore)
useHead({
// Title suffix
titleTemplate: (titleChunk) =>
titleChunk ? `${titleChunk as string} - Speckle DUIv3` : 'Speckle DUIv3',
title: computed(
() =>
`CNX: (hostApp: ${hostAppName.value}:v${hostAppVersion.value}),(version: ${connectorVersion.value})`
),
htmlAttrs: {
lang: 'en',
class: computed(() => (isDarkTheme.value ? `dark` : ``))
@@ -50,6 +54,8 @@ onMounted(() => {
uniqueEmails.add(email)
}
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { $intercom } = useNuxtApp() // needed her for initialisation
})
</script>
<style></style>
+2 -1
View File
@@ -1,5 +1,6 @@
/* stylelint-disable selector-id-pattern */
@import '@speckle/ui-components/style.css';
@import url('@speckle/ui-components/style.css');
@tailwind base;
@tailwind components;
@tailwind utilities;
+1 -1
View File
@@ -1,7 +1,7 @@
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'http://127.0.0.1:3000/graphql',
schema: 'https://app.speckle.systems/graphql',
documents: ['{lib,components,layouts,pages,middleware}/**/*.{vue,js,ts}'],
ignoreNoDocuments: true, // for better experience with the watcher
generates: {
+1 -1
View File
@@ -60,7 +60,7 @@
<script setup lang="ts">
import type { DUIAccount } from '~~/store/accounts'
import { TrashIcon } from '@heroicons/vue/24/outline'
import { type BaseBridge } from '~/lib/bridge/base'
import type { BaseBridge } from '~/lib/bridge/base'
const { $accountBinding } = useNuxtApp()
+4 -3
View File
@@ -20,7 +20,7 @@
mount-menu-on-body
>
<template #something-selected="{ value }">
<span>{{ value.name }}</span>
<span>{{ isArray(value) ? value[0].name : value.name }}</span>
</template>
<template #option="{ item }">
<div class="flex items-center">
@@ -80,10 +80,11 @@ import {
createAutomationMutation
} from '~/lib/graphql/mutationsAndQueries'
import { provideApolloClient, useMutation, useQuery } from '@vue/apollo-composable'
import { useAccountStore } from '~/store/accounts'
import { useAccountStore, type DUIAccount } from '~/store/accounts'
import type { ApolloError } from '@apollo/client/errors'
import { formatVersionParams } from '~/lib/common/helpers/jsonSchema'
import { useJsonFormsChangeHandler } from '~/lib/core/composables/jsonSchema'
import { isArray } from 'lodash-es'
const props = defineProps<{
projectId: string
@@ -106,7 +107,7 @@ const toggleDialog = () => {
showAutomateDialog.value = !showAutomateDialog.value
}
const { mutate } = provideApolloClient(activeAccount.value.client)(() =>
const { mutate } = provideApolloClient((activeAccount.value as DUIAccount).client)(() =>
useMutation(createAutomationMutation)
)
@@ -9,7 +9,13 @@
<Component :is="iconAndColor.icon" :class="`w-4 h-4 ${iconAndColor.color}`" />
</div>
<div :class="`text-xs ${iconAndColor.color}`">
{{ result.category }}: {{ result.objectIds.length }} affected elements
{{ result.category }}:
{{
'objectIds' in props.result
? props.result.objectIds.length
: props.result.objectAppIds.length
}}
affected elements
</div>
</div>
<div v-if="result.message" class="text-xs text-foreground-2 pl-5">
@@ -19,16 +25,13 @@
</div>
</template>
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import {
XMarkIcon,
InformationCircleIcon,
ExclamationTriangleIcon
} from '@heroicons/vue/24/outline'
import type { Automate } from '@speckle/shared'
import { objectQuery } from '~/lib/graphql/mutationsAndQueries'
import type { IModelCard } from '~/lib/models/card'
import { useAccountStore } from '~/store/accounts'
type ObjectResult = Automate.AutomateTypes.ResultsSchema['values']['objectResults'][0]
@@ -37,38 +40,12 @@ const props = defineProps<{
result: ObjectResult
functionId?: string
}>()
const accStore = useAccountStore()
const app = useNuxtApp()
const projectAccount = computed(() =>
accStore.accountWithFallback(props.modelCard.accountId, props.modelCard.serverUrl)
)
const clientId = projectAccount.value.accountInfo.id
const applicationIds = ref<string[]>([])
type Data = {
applicationId?: string
}
// Loop over each objectId to run the query and collect application IDs
props.result.objectIds.forEach((objectId) => {
const { result: objectResult } = useQuery(
objectQuery,
() => ({
projectId: props.modelCard.projectId,
objectId
}),
() => ({ clientId })
)
watch(objectResult, (newValue) => {
const data = newValue?.project.object?.data as Data | undefined
const applicationId = data?.applicationId
if (applicationId && !applicationIds.value.includes(applicationId)) {
applicationIds.value.push(applicationId)
}
})
const applicationIds = computed(() => {
// Old schema ignore
if ('objectIds' in props.result) return []
return Object.values(props.result.objectAppIds).filter((id) => id !== null)
})
const handleClick = async () => {
+1
View File
@@ -329,6 +329,7 @@ onUnmounted(() => {
html.dialog-open {
overflow: visible !important;
}
html.dialog-open body {
overflow: hidden !important;
}
+29 -2
View File
@@ -17,9 +17,21 @@
</div>
<div class="flex items-center group">
<FormButton
v-if="notification.cta"
v-if="notification.secondaryCta"
v-tippy="notification.secondaryCta.tooltipText"
size="sm"
:color="notification.level === 'info' ? 'outline' : notification.level"
color="outline"
full-width
class="mr-1"
@click.stop="notification.secondaryCta.action"
>
{{ notification.secondaryCta.name }}
</FormButton>
<FormButton
v-if="notification.cta"
v-tippy="notification.cta.tooltipText"
size="sm"
color="primary"
full-width
@click.stop="notification.cta?.action"
>
@@ -59,6 +71,21 @@ if (props.notification.timeout) {
useTimeoutFn(() => emit('dismiss'), props.notification.timeout)
}
// const notificationButtonColor = (notificationLevel: ModelCardNotificationLevel) => {
// switch (notificationLevel) {
// case 'info':
// return 'outline'
// case 'danger':
// return 'danger'
// case 'success':
// return 'primary'
// case 'warning':
// return 'danger'
// default:
// return 'outline'
// }
// }
const textClassColor = computed(() => {
switch (props.notification.level) {
case 'danger':
+23 -10
View File
@@ -18,18 +18,24 @@
</div>
</div>
<div class="opacity-0 group-hover:opacity-100 transition flex">
<div
:class="
isPersonalProject ? '' : 'opacity-0 group-hover:opacity-100 transition flex'
"
>
<button
v-tippy="'Open project in browser'"
class="hover:text-primary flex items-center space-x-2 p-2"
v-tippy="projectNavigatorTippy"
class="hover:text-primary flex items-center space-x-2 p-2 relative animate-pulse"
>
<ArrowTopRightOnSquareIcon
class="w-4"
@click.stop="
$openUrl(projectUrl),
trackEvent('DUI3 Action', { name: 'Project View' }, project.accountId)
"
/>
<div class="relative w-4 h-4">
<ArrowTopRightOnSquareIcon
class="w-4 h-4"
@click.stop="
$openUrl(projectUrl),
trackEvent('DUI3 Action', { name: 'Project View' }, project.accountId)
"
/>
</div>
</button>
</div>
</button>
@@ -130,6 +136,13 @@ const projectAccount = computed(() =>
accountStore.accountWithFallback(props.project.accountId, props.project.serverUrl)
)
const isPersonalProject = computed(() => !projectDetails.value?.workspace)
const projectNavigatorTippy = computed(() =>
isPersonalProject.value
? 'Move personal project into a workspace'
: 'Open project in browser'
)
const clientId = projectAccount.value.accountInfo.id
const { result: projectDetailsResult, refetch: refetchProjectDetails } = useQuery(
+1 -1
View File
@@ -7,7 +7,7 @@
color="neutral"
size="xs"
hide-icon
class="mb-2 mt-1"
class="mt-1"
>
<template #description>
<div class="flex items-center">
+1 -1
View File
@@ -8,7 +8,7 @@
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import type { IDirectSelectionSendFilter, ISendFilter } from 'lib/models/card/send'
import type { IDirectSelectionSendFilter, ISendFilter } from '~/lib/models/card/send'
import { useHostAppStore } from '~~/store/hostApp'
import { useSelectionStore } from '~~/store/selection'
+23 -1
View File
@@ -12,8 +12,11 @@
full-width
color="foundation"
/>
<FormButton color="outline" size="sm" @click="selectAllCategories">
{{ allSelected ? 'Deselect all' : 'Select all' }}
</FormButton>
</div>
<div class="flex space-y-1 flex-col">
<div class="flex space-y-1 flex-col max-h-48 simple-scrollbar overflow-auto">
<div
v-for="cat in selectedCategoriesObjects.sort((a, b) =>
a.name.localeCompare(b.name)
@@ -93,6 +96,25 @@ const searchResults = computed(() => {
const selectedCategories = ref<string[]>(props.filter.selectedCategories || [])
const selectAllCategories = () => {
if (allSelected.value) {
selectedCategories.value = []
return
}
availableCategories.value.forEach((cat) => {
const index = selectedCategories.value.indexOf(cat.id)
if (index !== -1) {
return
} else {
selectedCategories.value.push(cat.id)
}
})
}
const allSelected = computed(() => {
return availableCategories.value.length === selectedCategories.value.length
})
const selectOrUnselectCategory = (id: string) => {
const index = selectedCategories.value.indexOf(id)
if (index !== -1) {
+14 -1
View File
@@ -63,7 +63,7 @@
class="w-4 text-foreground-disabled group-hover:text-foreground-2"
/>
</HeaderButton>
<HeaderButton v-tippy="'Send us feedback'" @click="showFeedbackDialog = true">
<HeaderButton v-tippy="'Send us feedback'" @click="openFeedbackDialog()">
<ChatBubbleLeftIcon
class="w-4 text-foreground-disabled group-hover:text-foreground-2"
/>
@@ -99,4 +99,17 @@ app.$baseBinding.on('documentChanged', () => {
showSendDialog.value = false
showReceiveDialog.value = false
})
const { $intercom } = useNuxtApp()
const openFeedbackDialog = () => {
if (
hostAppStore.hostAppName?.toLowerCase() === 'revit' &&
hostAppStore.hostAppVersion?.includes('2022')
) {
showFeedbackDialog.value = true
} else {
$intercom.show()
}
}
</script>
+4 -2
View File
@@ -159,7 +159,7 @@
>
<UserAvatarGroup
size="xs"
:users="[latestCommentNotification.comment?.author]"
:users="[latestCommentNotification.comment?.author as AvatarUserWithId]"
/>
<span class="line-clamp-1">
{{ latestCommentNotification.comment?.author.name }} just left a
@@ -208,6 +208,7 @@ import { useIntervalFn, useTimeoutFn } from '@vueuse/core'
import type { ProjectCommentsUpdatedMessage } from '~/lib/common/generated/gql/graphql'
import { useFunctionRunsStatusSummary } from '~/lib/automate/runStatus'
import { CursorArrowRaysIcon, XCircleIcon } from '@heroicons/vue/24/outline'
import type { AvatarUserWithId } from '@speckle/ui-components'
const app = useNuxtApp()
const store = useHostAppStore()
@@ -430,7 +431,8 @@ const { start: startCommentClearTimeout, stop: stopCommentClearTimeout } = useTi
)
onCommentResult((res) => {
latestCommentNotification.value = res.data?.projectCommentsUpdated
latestCommentNotification.value = res.data
?.projectCommentsUpdated as ProjectCommentsUpdatedMessage
startCommentClearTimeout()
})
-186
View File
@@ -1,186 +0,0 @@
<template>
<div class="p-0">
<slot name="activator" :toggle="toggleDialog"></slot>
<CommonDialog
v-model:open="showModelCreateDialog"
:title="canCreateModelInWorkspace ? `Create new model` : errorMessage?.title"
fullscreen="none"
>
<form v-if="canCreateModelInWorkspace" @submit="onSubmitCreateNewModel">
<div class="text-body-2xs mb-2 ml-1">Model name</div>
<FormTextInput
v-model="newModelName"
class="text-xs"
autocomplete="off"
name="name"
label="Model name"
color="foundation"
:show-clear="!!newModelName"
:placeholder="hostAppStore.documentInfo?.name"
:rules="[
ValidationHelpers.isRequired,
ValidationHelpers.isStringOfLength({ minLength: 3 })
]"
full-width
/>
<div class="mt-4 flex justify-end items-center space-x-2 w-full">
<FormButton size="sm" text @click="showModelCreateDialog = false">
Cancel
</FormButton>
<FormButton size="sm" submit :disabled="isCreatingModel">Create</FormButton>
</div>
</form>
<div v-else class="m-2">
{{ errorMessage?.description }}
<div class="flex mt-2 space-x-2 justify-end">
<FormButton size="sm" color="outline" @click="showModelCreateDialog = false">
Close
</FormButton>
<FormButton
v-if="errorMessage?.cta"
size="sm"
submit
@click="errorMessage?.cta?.action(), (showModelCreateDialog = false)"
>
{{ errorMessage?.cta?.name }}
</FormButton>
</div>
</div>
</CommonDialog>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useMutation, provideApolloClient, useQuery } from '@vue/apollo-composable'
import type { ModelListModelItemFragment } from '~/lib/common/generated/gql/graphql'
import { useForm } from 'vee-validate'
import { ValidationHelpers } from '@speckle/ui-components'
import type { DUIAccount } from '~/store/accounts'
import { useAccountStore } from '~/store/accounts'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { useHostAppStore } from '~/store/hostApp'
import {
canCreateModelInProjectQuery,
createModelMutation
} from '~/lib/graphql/mutationsAndQueries'
type WorkspacePermissionMessage = {
title: string
description: string
cta?: {
name: string
action: () => void
}
}
const { $openUrl } = useNuxtApp()
const showModelCreateDialog = ref(false)
const isCreatingModel = ref(false)
const props = defineProps<{
projectId: string
workspaceId?: string
workspaceSlug?: string
}>()
const emit = defineEmits<{
(e: 'model:created', model: ModelListModelItemFragment): void
}>()
const { trackEvent } = useMixpanel()
const accountStore = useAccountStore()
const hostAppStore = useHostAppStore()
const { activeAccount } = storeToRefs(accountStore)
const accountId = computed(() => activeAccount.value.accountInfo.id)
const newModelName = ref<string>()
const errorMessage = ref<WorkspacePermissionMessage>()
const toggleDialog = () => {
showModelCreateDialog.value = !showModelCreateDialog.value
}
const account = computed(() => {
return accountStore.accounts.find(
(acc) => acc.accountInfo.id === accountId.value
) as DUIAccount
})
const canCreateModelInWorkspace = ref<boolean>()
const { result: canCreateModelInWorkspaceResult } = useQuery(
canCreateModelInProjectQuery,
() => ({ projectId: props.projectId }),
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
watch(canCreateModelInWorkspaceResult, (val) => {
if (val?.project.permissions.canCreateModel.code !== 'OK') {
switch (val?.project.permissions.canCreateModel.code) {
case 'WorkspaceLimitsReached':
errorMessage.value = {
title: 'Plan limit reached',
description:
'The model limit for this workspace has been reached. Upgrade the workspace plan to create or move more models.',
cta: {
name: 'Explore Plans',
action: () =>
$openUrl(
`${account.value.accountInfo.serverInfo.url}/settings/workspaces/${props.workspaceSlug}/billing`
)
}
}
break
// TODO: we should add more cases later according to `code`
default:
errorMessage.value = {
title: 'Workspace warning',
description: val?.project.permissions.canCreateModel.message ?? 'error'
}
break
}
canCreateModelInWorkspace.value = false
} else {
canCreateModelInWorkspace.value = true
}
})
const createNewModel = async (name: string) => {
isCreatingModel.value = true
void trackEvent('DUI3 Action', { name: 'Model Create' }, account.value.accountInfo.id)
const { mutate } = provideApolloClient(account.value.client)(() =>
useMutation(createModelMutation)
)
const res = await mutate({ input: { projectId: props.projectId, name } })
if (res?.data?.modelMutations.create) {
emit('model:created', res?.data?.modelMutations.create)
// refetch() // Sorts the list with newly created model otherwise it will put the model at the bottom.
// emit('next', res?.data?.modelMutations.create)
} else {
let errorMessage = 'Undefined error'
if (res?.errors && res?.errors.length !== 0) {
errorMessage = res?.errors[0].message
}
hostAppStore.setNotification({
type: 1,
title: 'Failed to create model',
description: errorMessage
})
}
isCreatingModel.value = false
}
const { handleSubmit } = useForm<{ name: string }>()
const onSubmitCreateNewModel = handleSubmit(() => {
void createNewModel(newModelName.value as string)
})
</script>
+3 -14
View File
@@ -19,7 +19,7 @@
color="subtle"
class="block text-foreground-2 hover:text-foreground overflow-hidden max-w-full !justify-start"
full-width
:disabled="!!modelCard.progress || noReadAccess"
:disabled="!!modelCard.progress || !canEdit"
@click.stop="openVersionsDialog = true"
>
<span>
@@ -178,7 +178,7 @@ const expiredNotification = computed(() => {
if (props.modelCard.latestVersionId === props.modelCard.selectedVersionId) return
const notification = {} as ModelCardNotification
notification.dismissible = true
notification.level = 'info'
notification.level = 'success'
notification.text = 'Newer version available!'
notification.cta = {
name: 'Update',
@@ -203,13 +203,6 @@ const receiveResultNotificationText = computed(() => {
return 'Model loaded!'
})
const receiveResultNotificationLevel = computed(() => {
if (failRate.value > 80) {
return 'warning'
}
return 'info'
})
const receiveResultNotification = computed(() => {
if (
!props.modelCard.bakedObjectIds ||
@@ -219,7 +212,7 @@ const receiveResultNotification = computed(() => {
const notification = {} as ModelCardNotification
notification.dismissible = true
notification.level = receiveResultNotificationLevel.value
notification.level = 'success'
notification.text = receiveResultNotificationText.value
notification.report = props.modelCard.report
notification.cta = {
@@ -269,10 +262,6 @@ const latestVersionCreatedAt = computed(() => {
return dayjs(props.modelCard.latestVersionCreatedAt).from(dayjs())
})
const noReadAccess = computed(() => {
return props.canEdit
})
onMounted(() => {
refetch()
})
+118 -1
View File
@@ -41,6 +41,44 @@
</div>
</CommonDialog>
<CommonDialog
v-model:open="showSetMessageDialog"
title="Version message"
fullscreen="none"
>
<form @submit="setVersionMessage(versionMessage as string)">
<div class="text-body-2xs mb-2 ml-1">
Describe your latest changes to help keep track of design intent.
</div>
<FormTextArea
v-model="versionMessage"
class="text-xs"
placeholder="Moved elements to prevent clash"
autocomplete="off"
name="name"
label="Version message"
color="foundation"
:show-clear="!!versionMessage"
:rules="[ValidationHelpers.isStringOfLength({ minLength: 3 })]"
full-width
/>
<CommonLoadingBar v-if="isUpdatingVersionMessage" loading />
<div class="mt-4 flex justify-end items-center space-x-2 w-full">
<FormButton size="sm" text @click="showSetMessageDialog = false">
Cancel
</FormButton>
<FormButton
size="sm"
submit
:disabled="
isUpdatingVersionMessage || !versionMessage || versionMessage.length < 3
"
>
Save
</FormButton>
</div>
</form>
</CommonDialog>
<template #states>
<CommonModelNotification
v-if="expiredNotification"
@@ -74,6 +112,11 @@ import type { ISendFilter, ISenderModelCard } from '~/lib/models/card/send'
import type { ProjectModelGroup } from '~/store/hostApp'
import { useHostAppStore } from '~/store/hostApp'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { ToastNotificationType, ValidationHelpers } from '@speckle/ui-components'
import { provideApolloClient, useMutation } from '@vue/apollo-composable'
import { useAccountStore, type DUIAccount } from '~/store/accounts'
import { setVersionMessageMutation } from '~/lib/graphql/mutationsAndQueries'
const hostAppStore = useHostAppStore()
const { trackEvent } = useMixpanel()
const app = useNuxtApp()
@@ -97,6 +140,7 @@ const sendOrCancel = () => {
}
if (props.modelCard.progress) store.sendModelCancel(props.modelCard.modelCardId)
else store.sendModel(props.modelCard.modelCardId, 'ModelCardButton')
hasSetVersionMessage.value = false
}
let newFilter: ISendFilter
@@ -120,9 +164,63 @@ const saveFilter = async () => {
openFilterDialog.value = false
}
const showSetMessageDialog = ref(false)
const isUpdatingVersionMessage = ref(false)
const hasSetVersionMessage = ref(false)
const versionMessage = ref<string>()
const accountStore = useAccountStore()
const account = accountStore.accounts.find(
(acc) => acc.accountInfo.id === props.project.accountId
) as DUIAccount
const setVersionMessage = async (message: string) => {
if (!props.modelCard.latestCreatedVersionId) {
return
}
void trackEvent('DUI3 Action', {
name: 'Set version message'
})
isUpdatingVersionMessage.value = true
const { mutate } = provideApolloClient(account.client)(() =>
useMutation(setVersionMessageMutation)
)
const res = await mutate({
input: {
projectId: props.project.projectId,
versionId: props.modelCard.latestCreatedVersionId,
message
}
})
if (res?.data?.versionMutations.update.id) {
// seemed to noisy, and autoclose does not work for some reason.
// nicer ux to just close the dialog
// hostAppStore.setNotification({
// type: ToastNotificationType.Info,
// title: 'Version message saved',
// autoClose: true
// })
hasSetVersionMessage.value = true
} else {
hostAppStore.setNotification({
type: ToastNotificationType.Danger,
title: 'Request failed',
description: 'Failed to update version message.',
autoClose: true
})
}
showSetMessageDialog.value = false
isUpdatingVersionMessage.value = false
}
const saveFilterAndSend = async () => {
await saveFilter()
store.sendModel(props.modelCard.modelCardId, 'Filter')
hasSetVersionMessage.value = false
}
const expiredNotification = computed(() => {
@@ -139,6 +237,7 @@ const expiredNotification = computed(() => {
notification.cta = {
name: ctaType,
action: async () => {
hasSetVersionMessage.value = false
if (props.modelCard.progress) {
await store.sendModelCancel(props.modelCard.modelCardId)
}
@@ -188,8 +287,26 @@ const latestVersionNotification = computed(() => {
notification.level = sendResultNotificationLevel.value
notification.text = sendResultNotificationText.value
notification.report = props.modelCard.report
// NOTE: this prevents us displaying the set message button for non-updated
// connectors that send over the root object id over instead of the commit id
if (
props.modelCard.latestCreatedVersionId.length === 10 &&
!hasSetVersionMessage.value
) {
notification.secondaryCta = {
name: 'Set message',
tooltipText: 'Describe your changes',
action: () => {
showSetMessageDialog.value = true
versionMessage.value = ''
}
}
}
notification.cta = {
name: 'View version',
name: 'View',
tooltipText: 'Check your model in the browser!',
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
action: () => cardBase.value?.viewModel()
}
-220
View File
@@ -1,220 +0,0 @@
<template>
<div class="p-0">
<slot name="activator" :toggle="toggleDialog"></slot>
<CommonDialog
v-model:open="showProjectCreateDialog"
:title="`Create new project`"
fullscreen="none"
>
<form @submit="onSubmitCreateNewProject">
<div class="text-body-2xs mb-2 ml-1">Project name</div>
<FormTextInput
v-model="newProjectName"
class="text-xs"
placeholder="A Beautiful Home, A Small Bridge..."
autocomplete="off"
name="name"
label="Project name"
color="foundation"
:show-clear="!!newProjectName"
:rules="[
ValidationHelpers.isRequired,
ValidationHelpers.isStringOfLength({ minLength: 3 })
]"
full-width
/>
<div class="mt-4 flex justify-end items-center space-x-2 w-full">
<FormButton size="sm" text @click="showProjectCreateDialog = false">
Cancel
</FormButton>
<FormButton
size="sm"
submit
:disabled="isCreatingProject || !canCreateProject"
>
Create
</FormButton>
</div>
</form>
</CommonDialog>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useMutation, provideApolloClient, useQuery } from '@vue/apollo-composable'
import type { ProjectListProjectItemFragment } from '~/lib/common/generated/gql/graphql'
import {
canCreatePersonalProjectQuery,
canCreateProjectInWorkspaceQuery,
createProjectInWorkspaceMutation,
createProjectMutation
} from '~/lib/graphql/mutationsAndQueries'
import type { DUIAccount } from '~/store/accounts'
import { useAccountStore } from '~/store/accounts'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { useHostAppStore } from '~/store/hostApp'
import { useForm } from 'vee-validate'
import { ValidationHelpers } from '@speckle/ui-components'
const showProjectCreateDialog = ref(false)
const isCreatingProject = ref(false)
const props = defineProps<{ workspaceId?: string }>()
const emit = defineEmits<{
(e: 'project:created', result: ProjectListProjectItemFragment): void
}>()
const { trackEvent } = useMixpanel()
const accountStore = useAccountStore()
const hostAppStore = useHostAppStore()
const { activeAccount } = storeToRefs(accountStore)
const accountId = computed(() => activeAccount.value.accountInfo.id)
const newProjectName = ref<string>()
const errorMessageForWorkspace = ref<string>()
const errorMessageForPersonalProject = ref<string>()
const toggleDialog = () => {
showProjectCreateDialog.value = !showProjectCreateDialog.value
}
const account = computed(() => {
return accountStore.accounts.find(
(acc) => acc.accountInfo.id === accountId.value
) as DUIAccount
})
const canCreateProject = computed(() =>
props.workspaceId === 'personalProject'
? canCreatePersonalProject.value
: canCreateProjectInWorkspace.value
)
const { result: canCreatePersonalProjectResult } = useQuery(
canCreatePersonalProjectQuery,
() => ({}),
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
watch(canCreatePersonalProjectResult, (val) => {
if (val?.activeUser?.permissions.canCreatePersonalProject.code !== 'OK') {
errorMessageForPersonalProject.value =
val?.activeUser?.permissions.canCreatePersonalProject.message
}
})
const canCreatePersonalProject = computed(() => {
try {
return (
canCreatePersonalProjectResult.value?.activeUser?.permissions
.canCreatePersonalProject.code === 'OK'
)
} catch {
return true
}
})
const { result: canCreateProjectInWorkspaceResult } = useQuery(
canCreateProjectInWorkspaceQuery,
() => ({ workspaceId: props.workspaceId ?? 'null' }), // TODO: i do not know the potential cause here
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
watch(canCreateProjectInWorkspaceResult, (val) => {
if (val?.workspace.permissions.canCreateProject.code !== 'OK') {
errorMessageForWorkspace.value = val?.workspace.permissions.canCreateProject.message
}
})
const canCreateProjectInWorkspace = computed(() => {
try {
return (
canCreateProjectInWorkspaceResult.value?.workspace.permissions.canCreateProject
.code === 'OK'
)
} catch {
return true
}
})
const { handleSubmit } = useForm<{ name: string }>()
const onSubmitCreateNewProject = handleSubmit(() => {
// TODO: Chat with Fabians
// This works, but if we use handleSubmit(args) > args.name -> it is undefined in Production on netlify, but works fine on local dev
void createNewProject(newProjectName.value as string)
})
const createNewProject = async (name: string) => {
isCreatingProject.value = true
if (props.workspaceId !== 'personalProject' && props.workspaceId !== undefined) {
createNewProjectInWorkspace(name)
isCreatingProject.value = false
return
}
void trackEvent(
'DUI3 Action',
{ name: 'Project Create', workspace: false },
account.value.accountInfo.id
)
const { mutate } = provideApolloClient(account.value.client)(() =>
useMutation(createProjectMutation)
)
const res = await mutate({ input: { name } })
if (res?.data?.projectMutations.create) {
emit('project:created', res?.data?.projectMutations.create)
} else {
let errorMessage = 'Undefined error'
if (res?.errors && res?.errors.length !== 0) {
errorMessage = res?.errors[0].message
}
hostAppStore.setNotification({
type: 1,
title: 'Failed to create project',
description: errorMessage
})
}
isCreatingProject.value = false
}
const createNewProjectInWorkspace = async (name: string) => {
void trackEvent(
'DUI3 Action',
{ name: 'Project Create', workspace: true },
account.value.accountInfo.id
)
const { mutate } = provideApolloClient(account.value.client)(() =>
useMutation(createProjectInWorkspaceMutation)
)
const res = await mutate({
input: { name, workspaceId: props.workspaceId as string }
})
if (res?.data?.workspaceMutations.projects.create) {
emit('project:created', res?.data?.workspaceMutations.projects.create)
} else {
let errorMessage = 'Undefined error'
if (res?.errors && res?.errors.length !== 0) {
errorMessage = res?.errors[0].message
}
hostAppStore.setNotification({
type: 1,
title: 'Failed to create project',
description: errorMessage
})
}
}
</script>
@@ -1,141 +0,0 @@
<template>
<div class="p-0">
<slot name="activator" :toggle="toggleDialog"></slot>
<CommonDialog
v-model:open="showProjectCreateDialog"
:title="`Create new project`"
fullscreen="none"
>
<form @submit="onSubmitCreateNewProject">
<div class="text-body-2xs mb-2 ml-1">Project name</div>
<FormTextInput
v-model="newProjectName"
class="text-xs"
placeholder="A Beautiful Home, A Small Bridge..."
autocomplete="off"
name="name"
label="Project name"
color="foundation"
:show-clear="!!newProjectName"
:rules="[
ValidationHelpers.isRequired,
ValidationHelpers.isStringOfLength({ minLength: 3 })
]"
full-width
/>
<div class="mt-4 flex justify-end items-center space-x-2 w-full">
<FormButton size="sm" text @click="showProjectCreateDialog = false">
Cancel
</FormButton>
<FormButton
size="sm"
submit
:disabled="isCreatingProject || !canCreatePersonalProject"
>
Create
</FormButton>
</div>
</form>
</CommonDialog>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useMutation, provideApolloClient, useQuery } from '@vue/apollo-composable'
import type { ProjectListProjectItemFragment } from '~/lib/common/generated/gql/graphql'
import {
canCreatePersonalProjectQuery,
createProjectMutation
} from '~/lib/graphql/mutationsAndQueries'
import type { DUIAccount } from '~/store/accounts'
import { useAccountStore } from '~/store/accounts'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { useHostAppStore } from '~/store/hostApp'
import { useForm } from 'vee-validate'
import { ValidationHelpers } from '@speckle/ui-components'
const showProjectCreateDialog = ref(false)
const isCreatingProject = ref(false)
const emit = defineEmits<{
(e: 'project:created', result: ProjectListProjectItemFragment): void
}>()
const { trackEvent } = useMixpanel()
const accountStore = useAccountStore()
const hostAppStore = useHostAppStore()
const { activeAccount } = storeToRefs(accountStore)
const accountId = computed(() => activeAccount.value.accountInfo.id)
const newProjectName = ref<string>()
const errorMessage = ref<string>()
const toggleDialog = () => {
showProjectCreateDialog.value = !showProjectCreateDialog.value
}
const account = computed(() => {
return accountStore.accounts.find(
(acc) => acc.accountInfo.id === accountId.value
) as DUIAccount
})
const canCreatePersonalProject = ref<boolean>(false)
const { result: canCreatePersonalProjectResult } = useQuery(
canCreatePersonalProjectQuery,
() => ({}),
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
watch(canCreatePersonalProjectResult, (val) => {
if (val?.activeUser?.permissions.canCreatePersonalProject.code !== 'OK') {
errorMessage.value = val?.activeUser?.permissions.canCreatePersonalProject.message
canCreatePersonalProject.value = false
} else {
canCreatePersonalProject.value = true
}
})
const { handleSubmit } = useForm<{ name: string }>()
const onSubmitCreateNewProject = handleSubmit(() => {
// TODO: Chat with Fabians
// This works, but if we use handleSubmit(args) > args.name -> it is undefined in Production on netlify, but works fine on local dev
void createNewProject(newProjectName.value as string)
})
const createNewProject = async (name: string) => {
isCreatingProject.value = true
void trackEvent(
'DUI3 Action',
{ name: 'Project Create', workspace: false },
account.value.accountInfo.id
)
const { mutate } = provideApolloClient(account.value.client)(() =>
useMutation(createProjectMutation)
)
const res = await mutate({ input: { name } })
if (res?.data?.projectMutations.create) {
emit('project:created', res?.data?.projectMutations.create)
} else {
let errorMessage = 'Undefined error'
if (res?.errors && res?.errors.length !== 0) {
errorMessage = res?.errors[0].message
}
hostAppStore.setNotification({
type: 1,
title: 'Failed to create project',
description: errorMessage
})
}
isCreatingProject.value = false
}
</script>
@@ -1,198 +0,0 @@
<template>
<div class="p-0">
<slot name="activator" :toggle="toggleDialog"></slot>
<CommonDialog
v-model:open="showProjectCreateDialog"
:title="canCreateProjectInWorkspace ? `Create new project` : errorMessage?.title"
fullscreen="none"
>
<form v-if="canCreateProjectInWorkspace" @submit="onSubmitCreateNewProject">
<div class="text-body-2xs mb-2 ml-1">Project name</div>
<FormTextInput
v-model="newProjectName"
class="text-xs"
placeholder="A Beautiful Home, A Small Bridge..."
autocomplete="off"
name="name"
label="Project name"
color="foundation"
:show-clear="!!newProjectName"
:rules="[
ValidationHelpers.isRequired,
ValidationHelpers.isStringOfLength({ minLength: 3 })
]"
full-width
/>
<div class="mt-4 flex justify-end items-center space-x-2 w-full">
<FormButton
size="sm"
color="outline"
@click="showProjectCreateDialog = false"
>
Cancel
</FormButton>
<FormButton size="sm" submit :disabled="isCreatingProject">Create</FormButton>
</div>
</form>
<div v-else class="m-2">
{{ errorMessage?.description }}
<div class="flex mt-2 space-x-2 justify-end">
<FormButton
size="sm"
color="outline"
@click="showProjectCreateDialog = false"
>
Close
</FormButton>
<FormButton
v-if="errorMessage?.cta"
size="sm"
submit
@click="errorMessage?.cta?.action(), (showProjectCreateDialog = false)"
>
{{ errorMessage?.cta?.name }}
</FormButton>
</div>
</div>
</CommonDialog>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useMutation, provideApolloClient, useQuery } from '@vue/apollo-composable'
import type {
ProjectListProjectItemFragment,
WorkspaceListWorkspaceItemFragment
} from '~/lib/common/generated/gql/graphql'
import {
canCreateProjectInWorkspaceQuery,
createProjectInWorkspaceMutation
} from '~/lib/graphql/mutationsAndQueries'
import type { DUIAccount } from '~/store/accounts'
import { useAccountStore } from '~/store/accounts'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { useHostAppStore } from '~/store/hostApp'
import { useForm } from 'vee-validate'
import { ValidationHelpers } from '@speckle/ui-components'
type WorkspacePermissionMessage = {
title: string
description: string
cta?: {
name: string
action: () => void
}
}
const { $openUrl } = useNuxtApp()
const showProjectCreateDialog = ref(false)
const isCreatingProject = ref(false)
const props = defineProps<{ workspace?: WorkspaceListWorkspaceItemFragment }>()
const emit = defineEmits<{
(e: 'project:created', result: ProjectListProjectItemFragment): void
}>()
const { trackEvent } = useMixpanel()
const accountStore = useAccountStore()
const hostAppStore = useHostAppStore()
const { activeAccount } = storeToRefs(accountStore)
const accountId = computed(() => activeAccount.value.accountInfo.id)
const newProjectName = ref<string>()
const errorMessage = ref<WorkspacePermissionMessage>()
const toggleDialog = () => {
showProjectCreateDialog.value = !showProjectCreateDialog.value
}
const account = computed(() => {
return accountStore.accounts.find(
(acc) => acc.accountInfo.id === accountId.value
) as DUIAccount
})
const canCreateProjectInWorkspace = ref<boolean>()
const { result: canCreateProjectInWorkspaceResult } = useQuery(
canCreateProjectInWorkspaceQuery,
() => ({ workspaceId: props.workspace?.id ?? 'null' }), // TODO: i do not know the potential cause here
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
watch(canCreateProjectInWorkspaceResult, (val) => {
if (val?.workspace.permissions.canCreateProject.code !== 'OK') {
switch (val?.workspace.permissions.canCreateProject.code) {
case 'WorkspaceLimitsReached':
errorMessage.value = {
title: 'Plan limit reached',
description:
'The project limit for this workspace has been reached. Upgrade the workspace plan to create or move more projects.',
cta: {
name: 'Explore Plans',
action: () =>
$openUrl(
`${account.value.accountInfo.serverInfo.url}/settings/workspaces/${props.workspace?.slug}/billing`
)
}
}
break
// TODO: we should add more cases later according to `code`
default:
errorMessage.value = {
title: 'Workspace warning',
description: val?.workspace.permissions.canCreateProject.message ?? 'error'
}
break
}
canCreateProjectInWorkspace.value = false
} else {
canCreateProjectInWorkspace.value = true
}
})
const { handleSubmit } = useForm<{ name: string }>()
const onSubmitCreateNewProject = handleSubmit(() => {
// TODO: Chat with Fabians
// This works, but if we use handleSubmit(args) > args.name -> it is undefined in Production on netlify, but works fine on local dev
void createNewProjectInWorkspace(newProjectName.value as string)
})
const createNewProjectInWorkspace = async (name: string) => {
isCreatingProject.value = true
void trackEvent(
'DUI3 Action',
{ name: 'Project Create', workspace: true },
account.value.accountInfo.id
)
const { mutate } = provideApolloClient(account.value.client)(() =>
useMutation(createProjectInWorkspaceMutation)
)
const res = await mutate({
input: { name, workspaceId: props.workspace?.id as string }
})
if (res?.data?.workspaceMutations.projects.create) {
emit('project:created', res?.data?.workspaceMutations.projects.create)
} else {
let errorMessage = 'Undefined error'
if (res?.errors && res?.errors.length !== 0) {
errorMessage = res?.errors[0].message
}
hostAppStore.setNotification({
type: 1,
title: 'Failed to create project',
description: errorMessage
})
}
isCreatingProject.value = false
}
</script>
+2 -1
View File
@@ -12,6 +12,7 @@
<WizardProjectSelector
:is-sender="false"
:show-new-project="false"
:url-parse-error="urlParseError"
@next="selectProject"
@search-text-update="updateSearchText"
/>
@@ -39,7 +40,7 @@
/>
</div>
</div>
<div v-if="urlParseError" class="p-2 text-xs text-danger">{{ urlParseError }}</div>
<div v-if="urlParseError" class="p-2 text-danger">{{ urlParseError }}</div>
</CommonDialog>
</template>
<script setup lang="ts">
+1 -1
View File
@@ -75,7 +75,7 @@ import {
} from '@heroicons/vue/24/solid'
import type { ConversionResult } from '~/lib/conversions/conversionResult'
import { useAccountStore } from '~/store/accounts'
import type { IModelCard } from 'lib/models/card'
import type { IModelCard } from '~/lib/models/card'
import { useHostAppStore } from '~/store/hostApp'
const app = useNuxtApp()
+2 -1
View File
@@ -11,6 +11,7 @@
<WizardProjectSelector
is-sender
disable-no-write-access-projects
:url-parse-error="urlParseError"
@next="selectProject"
@search-text-update="updateSearchText"
/>
@@ -37,7 +38,7 @@
<FormButton full-width @click="addModel">Publish</FormButton>
</div>
</div>
<div v-if="urlParseError" class="p-2 text-xs text-danger">
<div v-if="urlParseError" class="p-2 text-danger">
{{ urlParseError }}
</div>
</CommonDialog>
+96 -30
View File
@@ -13,22 +13,51 @@
full-width
color="foundation"
/>
<ModelCreateDialog
:project-id="project.id"
:workspace-id="workspaceId"
:workspace-slug="workspaceSlug"
@model:created="(result: ModelListModelItemFragment) => handleModelCreated(result)"
<div
v-tippy="
canCreateModelResult?.project.permissions.canCreateModel.authorized
? 'Create new model'
: canCreateModelResult?.project.permissions.canCreateModel.message
"
>
<template #activator="{ toggle }">
<button
v-tippy="'New model'"
class="p-1.5 bg-foundation hover:bg-primary-muted rounded text-foreground border"
@click="toggle()"
<FormButton
color="outline"
:disabled="
!canCreateModelResult?.project.permissions.canCreateModel.authorized
"
:class="`p-1.5 bg-foundation hover:bg-primary-muted rounded text-foreground border`"
@click="showNewModelDialog = true"
>
<PlusIcon class="w-4 -mx-2" />
</FormButton>
</div>
</div>
<div
v-if="
canCreateModelResult &&
!canCreateModelResult.project.permissions.canCreateModel.authorized
"
>
<CommonAlert title="Cannot create new models" color="info" hide-icon>
<template #description>
{{ canCreateModelResult.project.permissions.canCreateModel.message }}
<FormButton
v-if="workspaceSlug"
full-width
color="primary"
size="sm"
class="mt-2"
@click="
$openUrl(
`${account.accountInfo.serverInfo.url}/settings/workspaces/${workspaceSlug}/billing`
)
"
>
<PlusIcon class="w-4" />
</button>
Explore Plans
</FormButton>
</template>
</ModelCreateDialog>
</CommonAlert>
</div>
<div class="relative grid grid-cols-1 gap-2">
<CommonLoadingBar v-if="loading" loading />
@@ -37,6 +66,7 @@
v-for="model in models"
:key="model.id"
:model="model"
:token="token"
@click="handleModelSelect(model)"
/>
@@ -77,6 +107,20 @@
</template>
</CommonDialog>
<FormButton
v-if="
models?.length === 0 &&
!!searchText &&
canCreateModelResult?.project.permissions.canCreateModel?.authorized
"
full-width
color="outline"
:disabled="isCreatingModel"
@click="createNewModel(searchText)"
>
Create "{{ searchText }}"
</FormButton>
<FormButton
v-else
color="outline"
full-width
:disabled="hasReachedEnd"
@@ -91,7 +135,7 @@
title="Create new model"
fullscreen="none"
>
<form @submit="onSubmit">
<form @submit="createNewModel(newModelName as string)">
<FormTextInput
v-model="newModelName"
:rules="rules"
@@ -107,7 +151,9 @@
<FormButton size="sm" text @click="showNewModelDialog = false">
Cancel
</FormButton>
<FormButton size="sm" submit :disabled="isCreatingModel">Create</FormButton>
<FormButton size="sm" submit :disabled="isCreatingModel || !newModelName">
Create
</FormButton>
</div>
</form>
</CommonDialog>
@@ -122,10 +168,10 @@ import type {
} from '~/lib/common/generated/gql/graphql'
import { useModelNameValidationRules } from '~/lib/validation'
import {
canCreateModelInProjectQuery,
createModelMutation,
projectModelsQuery
} from '~/lib/graphql/mutationsAndQueries'
import { useForm } from 'vee-validate'
import type { DUIAccount } from '~/store/accounts'
import { useAccountStore } from '~/store/accounts'
import { useHostAppStore } from '~/store/hostApp'
@@ -152,13 +198,20 @@ const props = withDefaults(
const accountStore = useAccountStore()
const account = computed(
() =>
accountStore.accounts.find(
(acc) => acc.accountInfo.id === props.accountId
) as DUIAccount
)
const showNewModelDialog = ref(false)
const showSelectionHasProblemsDialog = ref(false)
const searchText = ref<string>()
const newModelName = ref<string>()
const newModelName = ref<string>(hostAppStore.documentInfo?.name ?? 'unnamed model')
watch(searchText, () => (newModelName.value = searchText.value))
watch(searchText, () => (newModelName.value = searchText.value as string))
let selectedModel: ModelListModelItemFragment | undefined = undefined
const existingModelProblem = ref(false)
@@ -187,12 +240,6 @@ const confirmModelSelection = () => {
}
const rules = useModelNameValidationRules()
const { handleSubmit } = useForm<{ name: string }>()
const onSubmit = handleSubmit(() => {
// TODO: Chat with Fabians
// This works, but if we use handleSubmit(args) > args.name -> it is undefined in Production on netlify, but works fine on local dev
void createNewModel(newModelName.value as string)
})
const handleModelCreated = (result: ModelListModelItemFragment) => {
refetch() // Sorts the list with newly created project otherwise it will put the project at the bottom.
@@ -200,21 +247,30 @@ const handleModelCreated = (result: ModelListModelItemFragment) => {
}
const isCreatingModel = ref(false)
const createNewModel = async (name: string) => {
if (!canCreateModelResult.value?.project.permissions.canCreateModel.authorized) {
hostAppStore.setNotification({
type: 1,
title: 'Failed to create model',
description:
canCreateModelResult.value?.project.permissions.canCreateModel.message
})
return
}
isCreatingModel.value = true
const account = accountStore.accounts.find(
(acc) => acc.accountInfo.id === props.accountId
) as DUIAccount
void trackEvent('DUI3 Action', { name: 'Model Create' }, account.accountInfo.id)
void trackEvent('DUI3 Action', { name: 'Model Create' }, account.value.accountInfo.id)
const { mutate } = provideApolloClient(account.client)(() =>
const { mutate } = provideApolloClient(account.value.client)(() =>
useMutation(createModelMutation)
)
const res = await mutate({ input: { projectId: props.project.id, name } })
if (res?.data?.modelMutations.create) {
refetch() // Sorts the list with newly created model otherwise it will put the model at the bottom.
emit('next', res?.data?.modelMutations.create)
// emit('next', res?.data?.modelMutations.create)
handleModelCreated(res?.data?.modelMutations.create)
} else {
let errorMessage = 'Undefined error'
if (res?.errors && res?.errors.length !== 0) {
@@ -230,6 +286,15 @@ const createNewModel = async (name: string) => {
isCreatingModel.value = false
}
const { result: canCreateModelResult } = useQuery(
canCreateModelInProjectQuery,
() => ({ projectId: props.project.id }),
() => ({
clientId: props.accountId,
fetchPolicy: 'network-only'
})
)
const {
result: projectModelsResult,
loading,
@@ -247,6 +312,7 @@ const {
() => ({ clientId: props.accountId, debounce: 500, fetchPolicy: 'cache-and-network' })
)
const token = computed(() => account.value.accountInfo.token)
const models = computed(() => projectModelsResult.value?.project.models.items)
const totalCount = computed(() => projectModelsResult.value?.project.models.totalCount)
const hasReachedEnd = ref(false)
@@ -0,0 +1,99 @@
<template>
<div>
<div
class="px-3 py-1 rounded-md shadow transition overflow-hidden bg-foundation border-foundation-2 hover:shadow-md border-1 group"
>
<div class="flex flex-col sm:flex-row sm:gap-2 text-foreground">
<div class="flex flex-col gap-4">
<div class="text-body-xs">
<h1
class="mb-1 text-sm font-semibold w-full inline-block py-1 bg-clip-text"
>
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.
</span>
</p>
<FormButton
text
color="primary"
size="sm"
:class="showMore ? `mb-1` : ``"
:icon-right="showMore ? ChevronUpIcon : ChevronDownIcon"
@click="showMore = !showMore"
>
{{ showMore ? 'Show less' : 'Show timeline' }}
</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>
</div>
</div>
</template>
<script setup lang="ts">
import { ChevronUpIcon, ChevronDownIcon } from '@heroicons/vue/20/solid'
const { $openUrl } = useNuxtApp()
const showMore = ref(false)
</script>
+295 -57
View File
@@ -1,11 +1,9 @@
<template>
<div class="space-y-2">
<div class="space-y-2 relative">
<div
v-if="workspacesEnabled && workspaces"
class="flex items-center space-x-2 bg-foundation -mx-3 -mt-2 px-3 py-2 shadow-sm border-b"
>
<div v-if="workspacesEnabled && workspaces" class="flex items-center space-x-2">
<div class="flex-grow min-w-0">
<!-- NO WORKSPACE YET -->
<div v-if="workspaces.length === 0">
<FormButton
full-width
@@ -43,23 +41,13 @@
</template>
</WorkspaceMenu>
</div>
<div class="px-0.5 shrink-0">
<div class="shrink-0 pt-1 px-1">
<AccountsMenu
:current-selected-account-id="accountId"
@select="(e) => selectAccount(e)"
/>
</div>
</div>
<!-- we can message to user about the non-workspace scenario -->
<!-- <div v-if="workspaces && workspaces.length === 0">
<CommonAlert size="xs" :color="'warning'">
<template #description>
You are listing legacy personal projects which will be deprecated end of
2025. We suggest you to move your personal projects into a workspace before
then.
</template>
</CommonAlert>
</div> -->
<div class="space-y-2">
<div class="flex items-center space-x-1 justify-between">
<FormTextInput
@@ -72,36 +60,58 @@
color="foundation"
/>
<div class="flex justify-between items-center space-x-2">
<ProjectCreateWorkspaceDialog
v-if="selectedWorkspace && selectedWorkspace.id !== 'personalProject'"
:workspace="selectedWorkspace"
@project:created="(result : ProjectListProjectItemFragment) => handleProjectCreated(result)"
<div
v-tippy="
canCreateProject
? 'Create new project'
: canCreateProjectPermissionCheck?.message
"
>
<template #activator="{ toggle }">
<button
v-tippy="'New project in workspace'"
class="p-1.5 bg-foundation hover:bg-primary-muted rounded text-foreground border"
@click="toggle()"
>
<PlusIcon class="w-4" />
</button>
</template>
</ProjectCreateWorkspaceDialog>
<!-- TODO: once we deprecate personal projects, else block is bye bye -->
<ProjectCreatePersonalDialog
v-else
@project:created="(result : ProjectListProjectItemFragment) => handleProjectCreated(result)"
<FormButton
color="outline"
:disabled="!canCreateProject"
:class="`p-1.5 bg-foundation hover:bg-primary-muted rounded text-foreground border`"
@click="showProjectCreateDialog = true"
>
<PlusIcon class="w-4 -mx-2" />
</FormButton>
</div>
<CommonDialog
v-model:open="showProjectCreateDialog"
:title="`Create new project`"
fullscreen="none"
>
<template #activator="{ toggle }">
<button
v-tippy="'New personal project'"
class="p-1.5 bg-foundation hover:bg-primary-muted rounded text-foreground border"
@click="toggle()"
>
<PlusIcon class="w-4" />
</button>
</template>
</ProjectCreatePersonalDialog>
<form @submit="createProject(newProjectName as string)">
<div class="text-body-2xs mb-2 ml-1">Project name</div>
<FormTextInput
v-model="newProjectName"
class="text-xs"
placeholder="A Beautiful Home, A Small Bridge..."
autocomplete="off"
name="name"
label="Project name"
color="foundation"
:show-clear="!!newProjectName"
:rules="[
ValidationHelpers.isRequired,
ValidationHelpers.isStringOfLength({ minLength: 3 })
]"
full-width
/>
<div class="mt-4 flex justify-end items-center space-x-2 w-full">
<FormButton size="sm" text @click="showProjectCreateDialog = false">
Cancel
</FormButton>
<FormButton
size="sm"
submit
:disabled="isCreatingProject || !newProjectName"
>
Create
</FormButton>
</div>
</form>
</CommonDialog>
<div v-if="!workspacesEnabled || !workspaces" class="mt-1">
<AccountsMenu
:current-selected-account-id="accountId"
@@ -110,9 +120,34 @@
</div>
</div>
</div>
<CommonLoadingBar v-if="loading" loading />
<div
v-if="
canCreateProjectPermissionCheck &&
!canCreateProjectPermissionCheck.authorized
"
>
<CommonAlert color="info" hide-icon>
<template #description>
{{ canCreateProjectPermissionCheck.message }}
<FormButton
v-if="showUpgradeButton"
full-width
class="mt-2"
color="primary"
size="sm"
@click="upgradeButtonAction()"
>
Upgrade now
</FormButton>
</template>
</CommonAlert>
</div>
<WizardPersonalProjectsWarning v-if="isPersonalProjectsAsWorkspace" />
<CommonLoadingBar v-if="loading || isCreatingProject" loading />
</div>
<div class="grid grid-cols-1 gap-2 relative z-0">
<div v-if="!urlParseError" class="grid grid-cols-1 gap-2 relative z-0">
<WizardListProjectCard
v-for="project in projects"
:key="project.id"
@@ -120,7 +155,27 @@
:is-sender="isSender"
@click="handleProjectCardClick(project)"
/>
<p v-if="projects?.length === 0 && !!searchText" class="text-sm">
No projects found
</p>
<FormButton
v-if="
projects?.length === 0 &&
!!searchText &&
canCreateProjectPermissionCheck?.authorized
"
full-width
color="outline"
:disabled="isCreatingProject"
class="block truncate overflow-hidden"
@click="createProject(searchText)"
>
Create "{{
searchText.length > 10 ? searchText.substring(0, 10) + '...' : searchText
}}"
</FormButton>
<FormButton
v-else
full-width
:disabled="hasReachedEnd"
color="outline"
@@ -140,19 +195,25 @@ import type { DUIAccount } from '~/store/accounts'
import { useAccountStore } from '~/store/accounts'
import {
activeWorkspaceQuery,
canCreatePersonalProjectQuery,
createProjectInWorkspaceMutation,
createProjectMutation,
projectsListQuery,
serverInfoQuery,
setActiveWorkspaceMutation,
workspacesListQuery
} from '~/lib/graphql/mutationsAndQueries'
import { useMutation, provideApolloClient, useQuery } from '@vue/apollo-composable'
import { ValidationHelpers } from '@speckle/ui-components'
import type {
ProjectListProjectItemFragment,
WorkspaceListWorkspaceItemFragment
} from 'lib/common/generated/gql/graphql'
} from '~/lib/common/generated/gql/graphql'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import { useConfigStore } from '~/store/config'
import { useHostAppStore } from '~/store/hostApp'
const hostAppStore = useHostAppStore()
const { trackEvent } = useMixpanel()
const { $openUrl } = useNuxtApp()
@@ -174,6 +235,7 @@ const props = withDefaults(
* For the send wizard - not allowing selecting projects we can't write to.
*/
disableNoWriteAccessProjects?: boolean
urlParseError?: string
}>(),
{
showNewProject: true,
@@ -210,7 +272,11 @@ const handleProjectCreated = (result: ProjectListProjectItemFragment) => {
const { result: serverInfoResult, refetch: refetchServerInfo } = useQuery(
serverInfoQuery,
() => ({}),
() => ({ clientId: accountId.value, debounce: 500, fetchPolicy: 'network-only' })
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
const workspacesEnabled = computed(
@@ -222,7 +288,11 @@ const { result: workspacesResult, refetch: refetchWorkspaces } = useQuery(
() => ({
limit: 100
}),
() => ({ clientId: accountId.value, debounce: 500, fetchPolicy: 'network-only' })
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
const workspaces = computed(() => workspacesResult.value?.activeUser?.workspaces.items)
@@ -230,7 +300,11 @@ const workspaces = computed(() => workspacesResult.value?.activeUser?.workspaces
const { result: activeWorkspaceResult, refetch: refetchActiveWorkspace } = useQuery(
activeWorkspaceQuery,
() => ({}),
() => ({ clientId: accountId.value, debounce: 500, fetchPolicy: 'network-only' })
() => ({
clientId: accountId.value,
debounce: 500,
fetchPolicy: 'network-only'
})
)
const activeWorkspace = computed(() => {
@@ -243,15 +317,30 @@ const activeWorkspace = computed(() => {
return previouslySelectedWorkspace
}
}
// fallback to activeWorkspace query result
return activeWorkspaceResult.value?.activeUser
const activeWorkspace = activeWorkspaceResult.value?.activeUser
?.activeWorkspace as WorkspaceListWorkspaceItemFragment
// fallback to activeWorkspace query result
if (activeWorkspace) {
return activeWorkspace
}
// if activeWorkspace is null will mean that it is personal projects - this fallback wont be the case soon
return {
id: 'personalProject',
name: 'Personal Projects'
} as WorkspaceListWorkspaceItemFragment
})
const selectedWorkspace = ref<WorkspaceListWorkspaceItemFragment | undefined>(
activeWorkspace.value
)
const isPersonalProjectsAsWorkspace = computed(
() => selectedWorkspace.value?.id === 'personalProject'
)
watch(
workspaces,
(newItems) => {
@@ -320,12 +409,11 @@ const {
limit: 10, // stupid hack, increased it since we do manual filter to be able to see more project, see below TODO note, once we have `personalOnly` filter, decrease back to 10
filter: {
search: (searchText.value || '').trim() || null,
workspaceId:
selectedWorkspace.value?.id === 'personalProject'
? null
: selectedWorkspace.value?.id,
workspaceId: isPersonalProjectsAsWorkspace.value
? null
: selectedWorkspace.value?.id,
includeImplicitAccess: true,
personalOnly: selectedWorkspace.value?.id === 'personalProject'
personalOnly: isPersonalProjectsAsWorkspace.value
}
}),
() => ({
@@ -337,7 +425,7 @@ const {
)
const projects = computed(() =>
selectedWorkspace.value?.id === 'personalProject' // TODO: we need to replace this logic with `personalOnly` filter when it is implemented into app.speckle.systems
isPersonalProjectsAsWorkspace.value // TODO: we need to replace this logic with `personalOnly` filter when it is implemented into app.speckle.systems
? projectsResult.value?.activeUser?.projects.items.filter(
(i) => i.workspaceId === null
)
@@ -361,6 +449,156 @@ watch(projectsResult, (newVal) => {
}
})
const { result: canCreatePersonalProjectResult } = useQuery(
canCreatePersonalProjectQuery,
{},
() => ({
clientId: accountId.value
})
)
const canCreateProject = computed(() => {
// If a workspace is selected, return that permission check
if (selectedWorkspace.value && selectedWorkspace.value.permissions) {
return selectedWorkspace.value.permissions.canCreateProject.authorized //as boolean
}
// Otherwise, check for personal projects
if (canCreatePersonalProjectResult) {
return canCreatePersonalProjectResult.value?.activeUser?.permissions
.canCreatePersonalProject.authorized
}
// To be always safe, default to false
return false
})
const canCreateProjectPermissionCheck = computed(() => {
if (selectedWorkspace.value && selectedWorkspace.value.permissions) {
return selectedWorkspace.value.permissions.canCreateProject
}
if (canCreatePersonalProjectResult) {
return canCreatePersonalProjectResult.value?.activeUser?.permissions
.canCreatePersonalProject
}
return null
})
const upgradeButtonAction = () => {
if (!canCreateProjectPermissionCheck.value) return
if (canCreateProjectPermissionCheck.value.code === 'WorkspaceNoEditorSeat') {
// open url to workspace/settings/users
$openUrl(
`${account.value.accountInfo.serverInfo.url}/settings/workspaces/${selectedWorkspace.value?.slug}/members`
)
return
}
if (canCreateProjectPermissionCheck.value.code === 'WorkspaceLimitsReached') {
// open url to workspace/billing
$openUrl(
`${account.value.accountInfo.serverInfo.url}/settings/workspaces/${selectedWorkspace.value?.slug}/billing`
)
return
}
}
const showUpgradeButton = computed(() => {
if (!canCreateProjectPermissionCheck.value) return false
if (
canCreateProjectPermissionCheck.value.code === 'WorkspaceNoEditorSeat' ||
canCreateProjectPermissionCheck.value.code === 'WorkspaceLimitsReached'
) {
return true
}
return false
})
const isCreatingProject = ref(false)
const showProjectCreateDialog = ref(false)
const createProject = (name: string) => {
if (
canCreateProjectPermissionCheck.value &&
!canCreateProjectPermissionCheck.value.authorized
) {
hostAppStore.setNotification({
type: 1,
title: 'Failed to create project',
description: canCreateProjectPermissionCheck.value.message as string
})
return
}
if (isPersonalProjectsAsWorkspace.value || !selectedWorkspace.value) {
return void createNewPersonalProject(name)
} else {
return void createNewWorkspaceProject(name)
}
}
const account = computed(() => {
return accountStore.accounts.find(
(acc) => acc.accountInfo.id === accountId.value
) as DUIAccount
})
const createNewWorkspaceProject = async (name: string) => {
isCreatingProject.value = true
void trackEvent(
'DUI3 Action',
{ name: 'Project Create', workspace: true },
accountId.value
)
const { mutate, onError } = provideApolloClient(account.value.client)(() =>
useMutation(createProjectInWorkspaceMutation)
)
onError((err) => {
hostAppStore.setNotification({
type: 1,
title: 'Failed to create project',
description: err.cause?.message ?? err.message ?? 'Unknown error'
})
})
const res = await mutate({
input: { name, workspaceId: selectedWorkspace.value?.id as string }
})
if (res?.data?.workspaceMutations.projects.create) {
handleProjectCreated(res?.data?.workspaceMutations.projects.create)
}
isCreatingProject.value = false
}
const createNewPersonalProject = async (name: string) => {
isCreatingProject.value = true
void trackEvent(
'DUI3 Action',
{ name: 'Project Create', workspace: false },
account.value.accountInfo.id
)
const { mutate, onError } = provideApolloClient(account.value.client)(() =>
useMutation(createProjectMutation)
)
onError((err) => {
hostAppStore.setNotification({
type: 1,
title: 'Failed to create project',
description: err.cause?.message ?? err.message ?? 'Unknown error'
})
})
const res = await mutate({ input: { name } })
if (res?.data?.projectMutations.create) {
return handleProjectCreated(res?.data?.projectMutations.create)
}
isCreatingProject.value = false
}
const loadMore = () => {
fetchMore({
variables: { cursor: projectsResult.value?.activeUser?.projects.cursor },
-85
View File
@@ -1,85 +0,0 @@
<template>
<div>
<div class="text-body-2xs mb-2 ml-1">Project workspace</div>
<FormSelectBase
key="name"
v-model="selectedWorkspace"
clearable
label="Workspaces"
placeholder="Nothing selected"
name="Workspaces"
:items="workspaces"
:disabled-item-predicate="userCantCreateWorkspace"
mount-menu-on-body
>
<template #something-selected="{ value }">
<span>{{ value.name }}</span>
</template>
<template #option="{ item }">
<div
v-tippy="{
content: item.readOnly
? 'This workspace is read-only.'
: item.role === 'workspace:guest'
? 'You do not have write access on this workspace.'
: undefined,
disabled: !(item.readOnly || item.role === 'workspace:guest')
}"
class="flex items-center"
>
<span class="truncate">{{ item.name }}</span>
</div>
</template>
</FormSelectBase>
<div
v-if="selectedWorkspace"
class="text-body-sm caption rounded p-2 bg-blue-500/10 my-2"
>
Project will be created in the selected workspace.
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import { workspacesListQuery } from '~/lib/graphql/mutationsAndQueries'
import type { WorkspaceListWorkspaceItemFragment } from 'lib/common/generated/gql/graphql'
import { storeToRefs } from 'pinia'
import { useAccountStore } from '~/store/accounts'
const emit = defineEmits<{
(
e: 'update:selectedWorkspace',
value: WorkspaceListWorkspaceItemFragment | undefined
): void
}>()
const accountStore = useAccountStore()
const { activeAccount } = storeToRefs(accountStore)
const accountId = computed(() => activeAccount.value.accountInfo.id)
const searchText = ref<string>()
const { result: workspacesResult } = useQuery(
workspacesListQuery,
() => ({
limit: 5,
filter: {
search: (searchText.value || '').trim() || null
}
}),
() => ({ clientId: accountId.value, debounce: 500, fetchPolicy: 'network-only' })
)
const workspaces = computed(() => workspacesResult.value?.activeUser?.workspaces.items)
const selectedWorkspace = ref<WorkspaceListWorkspaceItemFragment>()
watch(selectedWorkspace, (newVal) => {
emit('update:selectedWorkspace', newVal)
})
// Utility function to check if the user cannot create a workspace
const userCantCreateWorkspace = (item: WorkspaceListWorkspaceItemFragment) =>
(!!item?.role && item.role === 'workspace:guest') || !!item.readOnly
</script>
+21 -3
View File
@@ -4,9 +4,15 @@
>
<div class="flex items-center space-x-2 max-[275px]:space-x-0">
<div class="max-[275px]:hidden">
<div v-if="model.previewUrl" class="h-12 w-12">
<div
v-if="model.versions.totalCount === 0"
class="h-12 w-12 bg-blue-500/10 rounded flex items-center justify-center"
>
<CubeTransparentIcon class="w-5 h-5 text-foreground-2" />
</div>
<div v-else-if="previewUrl" class="h-12 w-12">
<img
:src="model.previewUrl"
:src="previewUrl"
alt="preview image for model"
class="h-12 w-12 object-cover"
/>
@@ -15,7 +21,7 @@
v-else
class="h-12 w-12 bg-blue-500/10 rounded flex items-center justify-center"
>
<CubeTransparentIcon class="w-5 h-5 text-foreground-2" />
<CommonLoadingIcon />
</div>
</div>
<div class="min-w-0 w-full">
@@ -52,9 +58,16 @@ import { ClockIcon } from '@heroicons/vue/24/outline'
import type { SourceAppName } from '@speckle/shared'
import { SourceApps } from '@speckle/shared'
import type { ModelListModelItemFragment } from '~/lib/common/generated/gql/graphql'
import { computedAsync } from '@vueuse/core'
import { usePreviewUrl } from '~/lib/core/composables/previewUrl'
const props = defineProps<{
model: ModelListModelItemFragment
/**
* Token to retrieve preview url
* @note by convention we pass around `accountId` but it doesn't make sense to get token for every model card. more efficient with this way.
*/
token: string
}>()
const folderPath = computed(() => {
@@ -68,6 +81,11 @@ const updatedAgo = computed(() => {
return dayjs(props.model.updatedAt).from(dayjs())
})
const previewUrl = computedAsync(async () => {
if (props.model.previewUrl === null) return
return await usePreviewUrl(props.token, props.model.previewUrl)
})
const sourceApp = computed(() => {
if (props.model.versions.items.length === 0) return
const version = props.model.versions.items[0]
+27 -2
View File
@@ -5,7 +5,7 @@
>
<UserAvatar
v-tippy="`Authored by ${version.authorUser?.name}`"
:user="version.authorUser"
:user="{ avatar: version.authorUser?.avatar, name: version.authorUser?.name as string }"
size="sm"
class="absolute inset-1"
/>
@@ -24,7 +24,15 @@
</div>
</div>
<div v-else class="flex items-center justify-center w-full h-24">
<img :src="version.previewUrl" alt="version preview" />
<div v-if="previewUrl">
<img :src="previewUrl" alt="preview image for version" />
</div>
<div
v-else
class="h-12 w-12 bg-blue-500/10 rounded flex items-center justify-center"
>
<CommonLoadingIcon />
</div>
</div>
<div class="p-1.5 border-t dark:border-gray-700">
<div class="flex space-x-2 items-center min-w-0">
@@ -100,9 +108,14 @@ import dayjs from 'dayjs'
import type { SourceAppName } from '@speckle/shared'
import { SourceApps } from '@speckle/shared'
import type { VersionListItemFragment } from '~/lib/common/generated/gql/graphql'
import { useAccountStore, type DUIAccount } from '~/store/accounts'
import { computedAsync } from '@vueuse/core'
import { usePreviewUrl } from '~/lib/core/composables/previewUrl'
// import { objectQuery } from '~/lib/graphql/mutationsAndQueries'
// import { useQuery } from '@vue/apollo-composable'
const accountStore = useAccountStore()
const props = defineProps<{
version: VersionListItemFragment
index: number
@@ -120,6 +133,18 @@ const createdAgo = computed(() => {
const isLimited = computed(() => props.version.referencedObject === null)
const token = computed(() => {
const account = accountStore.accounts.find(
(acc) => acc.accountInfo.id === props.accountId
) as DUIAccount
return account.accountInfo.token
})
const previewUrl = computedAsync(async () => {
if (props.version.previewUrl === null) return
return await usePreviewUrl(token.value, props.version.previewUrl)
})
// NOTE!!!: This logic somehow caused regression on versionList fetchMore, but we do not know exactly why yet.
// const { result: objectQueryResult } = useQuery(
// objectQuery,
+2 -2
View File
@@ -14,7 +14,7 @@
@select="
$emit('workspace:selected', workspace), (showWorkspaceSelectorDialog = false)
"
></WorkspaceListItem>
/>
</CommonDialog>
</div>
</template>
@@ -34,7 +34,7 @@ defineEmits<{
}>()
const workspacesWithPersonalProjects = computed(() => [
...props.workspaces,
...props.workspaces.filter((w) => w.creationState?.completed !== false),
{
id: 'personalProject',
name: 'Personal Projects'
+53 -7
View File
@@ -1,7 +1,19 @@
import { omit } from 'lodash-es'
import { baseConfigs, globals, getESMDirname } from '../../eslint.config.mjs'
import withNuxt from './.nuxt/eslint.config.mjs'
import pluginVueA11y from 'eslint-plugin-vuejs-accessibility'
import globals from 'globals'
import { fileURLToPath } from 'url'
import { dirname } from 'path'
import prettierConfig from 'eslint-config-prettier'
import js from '@eslint/js'
/**
* Feed in import.meta.url in your .mjs module to get the equivalent of __dirname
* @param {string} importMetaUrl
*/
export const getESMDirname = (importMetaUrl) => {
return dirname(fileURLToPath(importMetaUrl))
}
const configs = await withNuxt([
{
@@ -62,6 +74,7 @@ const configs = await withNuxt([
'@typescript-eslint/require-await': 'error',
'no-undef': 'off',
'@typescript-eslint/no-empty-object-type': 'off', // too restrictive
'@typescript-eslint/unified-signatures': 'off', // DX sucks in vue event definitions
'@typescript-eslint/no-dynamic-delete': 'off', // too restrictive
'@typescript-eslint/restrict-template-expressions': 'off', // too restrictive
@@ -78,10 +91,7 @@ const configs = await withNuxt([
{
files: ['**/*.vue'],
rules: {
'vue/component-tags-order': [
'error',
{ order: ['docs', 'template', 'script', 'style'] }
],
'vue/block-order': ['error', { order: ['docs', 'template', 'script', 'style'] }],
'vue/require-default-prop': 'off',
'vue/multi-word-component-names': 'off',
'vue/component-name-in-template-casing': [
@@ -116,10 +126,46 @@ const configs = await withNuxt([
'./lib/common/generated/**/*',
'storybook-static',
'.nuxt/**',
'.output/**'
'.output/**',
'**/dist/**',
'**/dist-*/**',
'**/public/**',
'**/events.json',
'**/generated/**/*'
]
},
...baseConfigs
{
files: ['**/*.mjs'],
languageOptions: {
sourceType: 'module'
}
},
{
files: ['**/*.cjs'],
languageOptions: {
sourceType: 'commonjs'
}
},
{
files: ['**/*.{js,mjs,cjs}', '**/.*.{js,mjs,cjs}'],
...js.configs.recommended
},
prettierConfig,
{
rules: {
camelcase: [
1,
{
properties: 'always'
}
],
'no-var': 'error',
'no-alert': 'error',
eqeqeq: 'error',
'prefer-const': 'warn',
'object-shorthand': 'warn'
}
}
])
export default configs
-136
View File
@@ -1,136 +0,0 @@
import { ApolloClient, gql } from '@apollo/client/core'
import { ApolloClients } from '@vue/apollo-composable'
import type { ComputedRef, Ref } from 'vue'
import type { Account } from '~/lib/bindings/definitions/IBasicConnectorBinding'
import { resolveClientConfig } from '~/lib/core/configs/apollo'
export type DUIAccount = {
/** account info coming from the host app */
accountInfo: Account
/** the graphql client; a bit superflous */
client?: ApolloClient<unknown>
/** whether an intial serverinfo query succeeded. */
isValid: boolean
}
export type DUIAccountsState = {
accounts: Ref<DUIAccount[]>
validAccounts: ComputedRef<DUIAccount[]>
refreshAccounts: () => Promise<void>
defaultAccount: ComputedRef<DUIAccount | undefined>
loading: Ref<boolean>
}
const AccountsInjectionKey = 'DUI_ACCOUNTS_STATE'
/**
* Use this composable to set up the account bindings and graphql clients at the top of the app.
* TODO: Properly handle cases when user was not connected to the internet,
* and then actually got connected.
*/
export function useAccountsSetup(): DUIAccountsState {
const app = useNuxtApp()
const $baseBinding = app.$baseBinding
const accounts = ref<DUIAccount[]>([])
const apolloClients = {} as Record<string, ApolloClient<unknown>>
// Tries to connect to the accounts and sets their is valid prop to false if fails.
const testAccounts = async (accs: DUIAccount[]) => {
const accountTestQuery = gql`
query AcccountTestQuery {
serverInfo {
version
name
company
}
}
`
for (const acc of accs) {
if (!acc.client) continue
try {
await acc.client.query({ query: accountTestQuery })
acc.isValid = true
} catch {
// TODO: properly dispose and kill this client. It's unclear how to do it.
acc.isValid = false
// NOTE: we do not want to delete the client, as we might want to "refresh" in
// case the user was not connected to the interweb.
// acc.client.disableNetworkFetches = true
// acc.client.stop()
// delete acc.client
}
}
}
const loading = ref(false)
// Matches local accounts coming from the host app to app state.
const refreshAccounts = async () => {
loading.value = true
const accs = await $baseBinding.getAccounts()
// We create a whole new list of accounts that will replace the old list. This way we ensure we drop
// out of scope old accounts that not exist anymore (TODO: test), and we don't need to do complex diffing.
const newAccs = [] as DUIAccount[]
for (const acc of accs) {
const existing = accounts.value.find((a) => a.accountInfo.id === acc.id)
if (existing) {
newAccs.push(existing as DUIAccount)
continue
}
const client = new ApolloClient(
resolveClientConfig({
httpEndpoint: new URL('/graphql', acc.serverInfo.url).href,
authToken: () => acc.token
})
)
apolloClients[acc.id] = client
newAccs.push({
accountInfo: acc,
client,
isValid: true
})
}
// We test accounts here so we try to prevent the app from querying/using invalid accounts.
await testAccounts(newAccs)
// Once we have tested the new accounts, finally set them.
accounts.value = newAccs
loading.value = false
}
void refreshAccounts() // Promise that we do not want to await (convention with void)
const defaultAccount = computed(() =>
accounts.value.find((acc) => acc.accountInfo.isDefault)
)
const validAccounts = computed(() => {
return accounts.value.filter((a) => a.isValid)
})
const accState = {
accounts,
defaultAccount,
validAccounts,
refreshAccounts,
loading
}
app.vueApp.provide(ApolloClients, apolloClients)
provide(AccountsInjectionKey, accState)
return accState // as DUIAccountsState
}
/**
* Use this composable to access the users' local accounts and their corresponding graphql client.
*/
export function useInjectedAccounts(): DUIAccountsState {
const state = inject(AccountsInjectionKey) as DUIAccountsState
return state
}
+4 -1
View File
@@ -1,4 +1,7 @@
import type { IBinding, IBindingSharedEvents } from 'lib/bindings/definitions/IBinding'
import type {
IBinding,
IBindingSharedEvents
} from '~/lib/bindings/definitions/IBinding'
export const IAccountBindingKey = 'accountsBinding'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ConversionResult } from 'lib/conversions/conversionResult'
import type { ConversionResult } from '~/lib/conversions/conversionResult'
import type { IModelCardSharedEvents } from '~/lib/models/card'
import type { CardSetting } from '~/lib/models/card/setting'
import type {
+1 -1
View File
@@ -5,7 +5,7 @@ import type {
} from '~~/lib/bindings/definitions/IBinding'
import type { CardSetting } from '~/lib/models/card/setting'
import type { IModelCardSharedEvents } from '~/lib/models/card'
import type { ConversionResult } from 'lib/conversions/conversionResult'
import type { ConversionResult } from '~/lib/conversions/conversionResult'
import type { CreateVersionArgs } from '~/lib/bridge/server'
export const ISendBindingKey = 'sendBinding'
-156
View File
@@ -1,156 +0,0 @@
import { ArchicadBridge } from '~/lib/bridge/server'
import { BaseBridge } from '~/lib/bridge/base'
import type { IRawBridge } from '~/lib/bridge/definitions'
/**
* A generic bridge class for Webivew2 or CefSharp.
*/
export class GenericBridge extends BaseBridge {
private bridge: IRawBridge
private archicadBridge: ArchicadBridge | undefined
private requests = {} as Record<
string,
{
methodName: string
resolve: (value: unknown) => void
reject: (reason: string | Error) => void
rejectTimerId: number
}
>
// TOTHINK: as this is a fast timeout, it forces us for long await methods in .net to return results via events. Kind-of not cool, and i'd be in favour of bumping it to "endless", or remove it altogether
// An example is the send or receive operations: they can take fucking long :D
private TIMEOUT_MS = 1000 * 60 // 60 sec
constructor(object: IRawBridge, isArchicadBridge: boolean = false) {
super()
this.bridge = object
if (isArchicadBridge) {
this.archicadBridge = new ArchicadBridge(this.emitter)
}
}
public async create(): Promise<boolean> {
// NOTE: GetMethods is a call to the .NET side.
try {
this.availableMethodNames = await this.bridge.GetBindingsMethodNames()
} catch {
console.warn(`Failed to get method names from binding.`)
return false
}
// NOTE: hoisting original calls as lowerCasedMethodNames, but using the UpperCasedName for the .NET call
// This allows us to follow js convetions and keep .NET ones too (eg. bindings.sayHi('') => public string SayHi(string name) {}
for (const methodName of this.availableMethodNames) {
const lowercasedMethodName = lowercaseMethodName(methodName)
const hoistTarget = this as unknown as Record<string, object>
hoistTarget[lowercasedMethodName] = (...args: unknown[]) =>
this.runMethod(methodName, args)
}
return true
}
private async emitResponseReady(eventName: string, requestId: string) {
this.registerPromise(eventName, requestId)
const data = await this.bridge.GetCallResult(requestId)
const request = this.requests[requestId]
try {
const parsedData = data ? (JSON.parse(data) as Record<string, unknown>) : null
if (parsedData === null) {
throw new Error(`Data is not parsed successfuly on ${eventName}`)
}
if (this.archicadBridge) {
this.archicadBridge.emit(eventName, parsedData, this.runMethod.bind(this))
} else {
this.emitter.emit(eventName, parsedData)
}
request.resolve(parsedData)
} catch (e) {
console.error(e)
request.reject(e as Error)
} finally {
window.clearTimeout(request.rejectTimerId)
delete this.requests[requestId]
}
}
async runMethod(
methodName: string,
args: unknown[],
shouldTimeout: boolean = true
): Promise<unknown> {
const requestId = (Math.random() + 1).toString(36).substring(2) + '_' + methodName
const preserializedArgs = args.map((a) => JSON.stringify(a))
this.bridge.RunMethod(methodName, requestId, JSON.stringify(preserializedArgs))
return this.registerPromise(methodName, requestId, shouldTimeout)
}
private async registerPromise(
methodName: string,
requestId: string,
shouldTimeout: boolean = true
) {
return new Promise((resolve, reject) => {
this.requests[requestId] = {
methodName,
resolve,
reject,
rejectTimerId: window.setTimeout(
() => {
reject(
`.NET response timed out for call to ${methodName} - did not receive anything back in good time (${this.TIMEOUT_MS}ms).`
)
delete this.requests[requestId]
},
shouldTimeout ? this.TIMEOUT_MS : 3600000
)
}
})
}
private async responseReady(requestId: string) {
if (!this.requests[requestId])
throw new Error(
`.NET Bridge found no request to resolve with the id of ${requestId}. Something is weird!`
)
const request = this.requests[requestId]
const data = await this.bridge.GetCallResult(requestId)
try {
const parsedData = data ? (JSON.parse(data) as Record<string, unknown>) : null // TODO: check if data is undefined
// eslint-disable-next-line no-prototype-builtins
if (parsedData && parsedData.hasOwnProperty('error')) {
console.error(data)
this.emitter.emit('errorOnResponse', data)
throw new Error(
`Failed to run ${requestId}. The host app error is logged above.`
)
}
request.resolve(parsedData)
} catch (e) {
console.error(e)
request.reject(e as Error)
} finally {
window.clearTimeout(request.rejectTimerId)
delete this.requests[requestId]
}
}
public showDevTools() {
this.bridge.ShowDevTools()
}
public openUrl(url: string) {
this.bridge.OpenUrl(url)
}
}
const lowercaseMethodName = (name: string) =>
name.charAt(0).toLowerCase() + name.slice(1)
+105 -19
View File
@@ -1,30 +1,47 @@
import { ArchicadBridge } from '~/lib/bridge/server'
import { BaseBridge } from '~/lib/bridge/base'
import type { IRawBridge } from '~/lib/bridge/definitions'
/**
* A generic bridge class for Webivew2 or CefSharp.
*/
export class GenericBridge extends BaseBridge {
private bridge: IRawBridge
private archicadBridge: ArchicadBridge | undefined
private requests = {} as Record<
string,
{
methodName: string
resolve: (value: unknown) => void
reject: (reason: string | Error) => void
rejectTimerId: number
}
>
// TOTHINK: as this is a fast timeout, it forces us for long await methods in .net to return results via events. Kind-of not cool, and i'd be in favour of bumping it to "endless", or remove it altogether
// An example is the send or receive operations: they can take fucking long :D
private TIMEOUT_MS = 1000 * 60 // 60 sec
constructor(object: IRawBridge) {
constructor(object: IRawBridge, isArchicadBridge: boolean = false) {
super()
this.bridge = object
if (isArchicadBridge) {
this.archicadBridge = new ArchicadBridge(this.emitter)
}
}
public async create(): Promise<boolean> {
// NOTE: GetMethods is a call to the .NET side.
let availableMethodNames = [] as string[]
try {
availableMethodNames = await this.bridge.GetBindingsMethodNames()
this.availableMethodNames = await this.bridge.GetBindingsMethodNames()
} catch {
console.warn(`Failed to get method names.`)
console.warn(`Failed to get method names from binding.`)
return false
}
// NOTE: hoisting original calls as lowerCasedMethodNames, but using the UpperCasedName for the .NET call
// This allows us to follow js convetions and keep .NET ones too (eg. bindings.sayHi('') => public string SayHi(string name) {}
for (const methodName of availableMethodNames) {
for (const methodName of this.availableMethodNames) {
const lowercasedMethodName = lowercaseMethodName(methodName)
const hoistTarget = this as unknown as Record<string, object>
hoistTarget[lowercasedMethodName] = (...args: unknown[]) =>
@@ -34,27 +51,96 @@ export class GenericBridge extends BaseBridge {
return true
}
private async runMethod(methodName: string, args: unknown[]): Promise<unknown> {
private async emitResponseReady(eventName: string, requestId: string) {
this.registerPromise(eventName, requestId)
const data = await this.bridge.GetCallResult(requestId)
const request = this.requests[requestId]
try {
const parsedData = data ? (JSON.parse(data) as Record<string, unknown>) : null
if (parsedData === null) {
throw new Error(`Data is not parsed successfuly on ${eventName}`)
}
if (this.archicadBridge) {
this.archicadBridge.emit(eventName, parsedData, this.runMethod.bind(this))
} else {
this.emitter.emit(eventName, parsedData)
}
request.resolve(parsedData)
} catch (e) {
console.error(e)
request.reject(e as Error)
} finally {
window.clearTimeout(request.rejectTimerId)
delete this.requests[requestId]
}
}
async runMethod(
methodName: string,
args: unknown[],
shouldTimeout: boolean = true
): Promise<unknown> {
const requestId = (Math.random() + 1).toString(36).substring(2) + '_' + methodName
const preserializedArgs = args.map((a) => JSON.stringify(a))
// NOTE: RunMethod is a call to the .NET side.
const result = await this.bridge.RunMethod(
methodName,
JSON.stringify(preserializedArgs)
)
this.bridge.RunMethod(methodName, requestId, JSON.stringify(preserializedArgs))
const parsed = result ? (JSON.parse(result) as Record<string, unknown>) : null
return this.registerPromise(methodName, requestId, shouldTimeout)
}
if (parsed && parsed['error']) {
console.error(parsed)
private async registerPromise(
methodName: string,
requestId: string,
shouldTimeout: boolean = true
) {
return new Promise((resolve, reject) => {
this.requests[requestId] = {
methodName,
resolve,
reject,
rejectTimerId: window.setTimeout(
() => {
reject(
`.NET response timed out for call to ${methodName} - did not receive anything back in good time (${this.TIMEOUT_MS}ms).`
)
delete this.requests[requestId]
},
shouldTimeout ? this.TIMEOUT_MS : 3600000
)
}
})
}
private async responseReady(requestId: string) {
if (!this.requests[requestId])
throw new Error(
`Failed to run ${methodName} with args ${JSON.stringify(
args
)}. The host app error is logged above.`
`.NET Bridge found no request to resolve with the id of ${requestId}. Something is weird!`
)
}
return parsed
const request = this.requests[requestId]
const data = await this.bridge.GetCallResult(requestId)
try {
const parsedData = data ? (JSON.parse(data) as Record<string, unknown>) : null // TODO: check if data is undefined
// eslint-disable-next-line no-prototype-builtins
if (parsedData && parsedData.hasOwnProperty('error')) {
console.error(data)
this.emitter.emit('errorOnResponse', data)
throw new Error(
`Failed to run ${requestId}. The host app error is logged above.`
)
}
request.resolve(parsedData)
} catch (e) {
console.error(e)
request.reject(e as Error)
} finally {
window.clearTimeout(request.rejectTimerId)
delete this.requests[requestId]
}
}
public showDevTools() {
+4 -4
View File
@@ -189,7 +189,7 @@ export class ArchicadBridge {
const body: ArchicadReceiveRequest = {
accountId: eventPayload.accountId,
projectId: eventPayload.projectId,
referencedObject: result.data.project.model.version.referencedObject,
referencedObject: result.data.project.model.version.referencedObject as string,
xmlConverterPath: eventPayload.xmlConverterPath
}
@@ -272,7 +272,7 @@ export class ArchicadBridge {
serverUrl: account?.accountInfo.serverInfo.url as string,
token: account?.accountInfo.token as string,
streamId: eventPayload.projectId,
objectId: result.data.project.model.version.referencedObject
objectId: result.data.project.model.version.referencedObject as string
})
const updateProgress = (e: {
@@ -313,7 +313,7 @@ export class ArchicadBridge {
})
// CONVERSION WILL START AFTER THAT
await runMethod('afterGetObjects', args as unknown as unknown[])
await runMethod('afterGetObjects', args as unknown as unknown[], false)
}
private queuedPromises = {} as Record<string, Promise<Response>[]>
@@ -373,7 +373,7 @@ export class ArchicadBridge {
this.queuedPromises[modelCardId] = []
console.log(`🚀 Upload is completed in ${(performance.now() - start) / 1000} s!`)
const args = [eventPayload.modelCardId, referencedObjectId]
await runMethod('afterSendObjects', args as unknown as unknown[])
await runMethod('afterSendObjects', args as unknown as unknown[], false)
}
}
+2 -2
View File
@@ -86,7 +86,7 @@ export class SketchupBridge extends BaseBridge {
}
// NOTE: Overriden emit as we do not need to parse the data back - the Sketchup bridge already parses it for us.
emit(eventName: string, payload: string): void {
override emit(eventName: string, payload: string): void {
const eventPayload = payload as unknown as Record<string, unknown>
if (eventName === 'sendViaBrowser')
@@ -124,7 +124,7 @@ export class SketchupBridge extends BaseBridge {
serverUrl: account?.accountInfo.serverInfo.url as string,
token: account?.accountInfo.token as string,
streamId: eventPayload.projectId,
objectId: result.data.project.model.version.referencedObject
objectId: result.data.project.model.version.referencedObject as string
})
const updateProgress = (e: {
+50 -8
View File
@@ -11,17 +11,59 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
* 3. It does not support dead code elimination, so it will add unused operations.
*
* Therefore it is highly recommended to use the babel or swc plugin for production.
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
*/
const documents = {
"\n query AcccountTestQuery {\n serverInfo {\n version\n name\n company\n }\n }\n ": types.AcccountTestQueryDocument,
type Documents = {
"\n mutation SetActiveWorkspaceMutation($slug: String) {\n activeUserMutations {\n setActiveWorkspace(slug: $slug)\n }\n }\n": typeof types.SetActiveWorkspaceMutationDocument,
"\n mutation VersionMutations($input: CreateVersionInput!) {\n versionMutations {\n create(input: $input) {\n id\n }\n }\n }\n": typeof types.VersionMutationsDocument,
"\n mutation Update($input: UpdateVersionInput!) {\n versionMutations {\n update(input: $input) {\n id\n }\n }\n }\n": typeof types.UpdateDocument,
"\n mutation MarkReceivedVersion($input: MarkReceivedVersionInput!) {\n versionMutations {\n markReceived(input: $input)\n }\n }\n": typeof types.MarkReceivedVersionDocument,
"\n mutation CreateModel($input: CreateModelInput!) {\n modelMutations {\n create(input: $input) {\n ...ModelListModelItem\n }\n }\n }\n": typeof types.CreateModelDocument,
"\n mutation CreateProject($input: ProjectCreateInput) {\n projectMutations {\n create(input: $input) {\n ...ProjectListProjectItem\n }\n }\n }\n": typeof types.CreateProjectDocument,
"\n mutation CreateProjectInWorkspace($input: WorkspaceProjectCreateInput!) {\n workspaceMutations {\n projects {\n create(input: $input) {\n ...ProjectListProjectItem\n }\n }\n }\n }\n": typeof types.CreateProjectInWorkspaceDocument,
"\n mutation StreamAccessRequestCreate($input: String!) {\n streamAccessRequestCreate(streamId: $input) {\n id\n }\n }\n": typeof types.StreamAccessRequestCreateDocument,
"\n fragment WorkspaceListWorkspaceItem on Workspace {\n id\n slug\n name\n description\n createdAt\n updatedAt\n logo\n role\n readOnly\n creationState {\n completed\n }\n permissions {\n canCreateProject {\n authorized\n code\n message\n }\n }\n }\n": typeof types.WorkspaceListWorkspaceItemFragmentDoc,
"\n fragment AutomateFunctionItem on AutomateFunction {\n name\n isFeatured\n id\n creator {\n name\n }\n releases {\n items {\n inputSchema\n }\n }\n }\n": typeof types.AutomateFunctionItemFragmentDoc,
"\n mutation CreateAutomation($projectId: ID!, $input: ProjectAutomationCreateInput!) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n create(input: $input) {\n id\n name\n }\n }\n }\n }\n": typeof types.CreateAutomationDocument,
"\n fragment AutomateFunctionRunItem on AutomateFunctionRun {\n id\n status\n statusMessage\n results\n contextView\n function {\n id\n name\n logo\n }\n }\n": typeof types.AutomateFunctionRunItemFragmentDoc,
"\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 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,
"\n query ActiveWorkspace {\n activeUser {\n activeWorkspace {\n ...WorkspaceListWorkspaceItem\n }\n }\n }\n": typeof types.ActiveWorkspaceDocument,
"\n fragment ProjectListProjectItem on Project {\n id\n name\n role\n updatedAt\n workspaceId\n workspace {\n id\n name\n slug\n role\n }\n models {\n totalCount\n }\n permissions {\n canLoad {\n authorized\n code\n message\n }\n canPublish {\n authorized\n code\n message\n }\n }\n }\n": typeof types.ProjectListProjectItemFragmentDoc,
"\n query ProjectListQuery($limit: Int!, $filter: UserProjectsFilter, $cursor: String) {\n activeUser {\n id\n projects(limit: $limit, filter: $filter, cursor: $cursor) {\n totalCount\n cursor\n items {\n ...ProjectListProjectItem\n }\n }\n }\n }\n": typeof types.ProjectListQueryDocument,
"\n fragment ModelListModelItem on Model {\n displayName\n name\n id\n previewUrl\n updatedAt\n versions(limit: 1) {\n totalCount\n items {\n ...VersionListItem\n }\n }\n }\n": typeof types.ModelListModelItemFragmentDoc,
"\n query ProjectModels(\n $projectId: String!\n $cursor: String\n $limit: Int!\n $filter: ProjectModelsFilter\n ) {\n project(id: $projectId) {\n id\n models(cursor: $cursor, limit: $limit, filter: $filter) {\n totalCount\n cursor\n items {\n ...ModelListModelItem\n }\n }\n }\n }\n": typeof types.ProjectModelsDocument,
"\n fragment VersionListItem on Version {\n id\n referencedObject\n message\n sourceApplication\n authorUser {\n avatar\n id\n name\n }\n createdAt\n previewUrl\n }\n": typeof types.VersionListItemFragmentDoc,
"\n query ModelVersions(\n $modelId: String!\n $projectId: String!\n $limit: Int!\n $cursor: String\n $filter: ModelVersionsFilter\n ) {\n project(id: $projectId) {\n id\n model(id: $modelId) {\n id\n versions(limit: $limit, cursor: $cursor, filter: $filter) {\n totalCount\n cursor\n items {\n ...VersionListItem\n }\n }\n }\n }\n }\n": typeof types.ModelVersionsDocument,
"\n query ObjectQuery($projectId: String!, $objectId: String!) {\n project(id: $projectId) {\n object(id: $objectId) {\n id\n data\n }\n }\n }\n": typeof types.ObjectQueryDocument,
"\n query ProjectAddByUrlQueryWithVersion(\n $projectId: String!\n $modelId: String!\n $versionId: String!\n ) {\n project(id: $projectId) {\n ...ProjectListProjectItem\n model(id: $modelId) {\n ...ModelListModelItem\n version(id: $versionId) {\n ...VersionListItem\n }\n }\n }\n }\n": typeof types.ProjectAddByUrlQueryWithVersionDocument,
"\n query ProjectAddByUrlQueryWithoutVersion($projectId: String!, $modelId: String!) {\n project(id: $projectId) {\n ...ProjectListProjectItem\n model(id: $modelId) {\n ...ModelListModelItem\n }\n }\n }\n": typeof types.ProjectAddByUrlQueryWithoutVersionDocument,
"\n query ProjectDetails($projectId: String!) {\n project(id: $projectId) {\n id\n role\n name\n workspace {\n name\n slug\n readOnly\n role\n }\n team {\n user {\n avatar\n id\n name\n }\n }\n visibility\n permissions {\n canLoad {\n authorized\n code\n message\n }\n canPublish {\n authorized\n code\n message\n }\n }\n }\n }\n": typeof types.ProjectDetailsDocument,
"\n query AutomateFunctions {\n automateFunctions {\n items {\n ...AutomateFunctionItem\n }\n }\n }\n": typeof types.AutomateFunctionsDocument,
"\n query ModelDetails($modelId: String!, $projectId: String!) {\n project(id: $projectId) {\n id\n name\n model(id: $modelId) {\n id\n displayName\n name\n versions {\n totalCount\n items {\n id\n }\n }\n author {\n id\n name\n avatar\n }\n }\n }\n }\n": typeof types.ModelDetailsDocument,
"\n query VersionDetails($projectId: String!, $versionId: String!, $modelId: String!) {\n project(id: $projectId) {\n id\n name\n model(id: $modelId) {\n id\n name\n versions(limit: 1) {\n items {\n id\n createdAt\n sourceApplication\n authorUser {\n id\n }\n }\n }\n version(id: $versionId) {\n id\n referencedObject\n message\n sourceApplication\n createdAt\n previewUrl\n }\n }\n }\n }\n": typeof types.VersionDetailsDocument,
"\n query ServerInfo {\n serverInfo {\n workspaces {\n workspacesEnabled\n }\n }\n }\n": typeof types.ServerInfoDocument,
"\n subscription OnProjectVersionsUpdate($projectId: String!) {\n projectVersionsUpdated(id: $projectId) {\n id\n type\n version {\n id\n createdAt\n message\n sourceApplication\n authorUser {\n id\n name\n avatar\n }\n model {\n id\n name\n displayName\n }\n }\n }\n }\n": typeof types.OnProjectVersionsUpdateDocument,
"\n subscription ProjectTriggeredAutomationsStatusUpdated($projectId: String!) {\n projectTriggeredAutomationsStatusUpdated(projectId: $projectId) {\n type\n version {\n id\n }\n model {\n id\n }\n project {\n id\n }\n run {\n ...AutomationRunItem\n }\n }\n }\n": typeof types.ProjectTriggeredAutomationsStatusUpdatedDocument,
"\n subscription OnUserProjectsUpdated {\n userProjectsUpdated {\n id\n project {\n id\n visibility\n team {\n id\n role\n }\n }\n }\n }\n": typeof types.OnUserProjectsUpdatedDocument,
"\n subscription ProjectUpdated($projectId: String!) {\n projectUpdated(id: $projectId) {\n id\n project {\n visibility\n }\n }\n }\n": typeof types.ProjectUpdatedDocument,
"\n subscription Subscription($target: ViewerUpdateTrackingTarget!) {\n viewerUserActivityBroadcasted(target: $target) {\n userName\n userId\n sessionId\n user {\n name\n id\n avatar\n }\n status\n }\n }\n": typeof types.SubscriptionDocument,
"\n subscription ProjectCommentsUpdated($target: ViewerUpdateTrackingTarget!) {\n projectCommentsUpdated(target: $target) {\n comment {\n author {\n avatar\n id\n name\n }\n id\n hasParent\n parent {\n id\n }\n }\n type\n }\n }\n": typeof types.ProjectCommentsUpdatedDocument,
};
const documents: Documents = {
"\n mutation SetActiveWorkspaceMutation($slug: String) {\n activeUserMutations {\n setActiveWorkspace(slug: $slug)\n }\n }\n": types.SetActiveWorkspaceMutationDocument,
"\n mutation VersionMutations($input: CreateVersionInput!) {\n versionMutations {\n create(input: $input) {\n id\n }\n }\n }\n": types.VersionMutationsDocument,
"\n mutation Update($input: UpdateVersionInput!) {\n versionMutations {\n update(input: $input) {\n id\n }\n }\n }\n": types.UpdateDocument,
"\n mutation MarkReceivedVersion($input: MarkReceivedVersionInput!) {\n versionMutations {\n markReceived(input: $input)\n }\n }\n": types.MarkReceivedVersionDocument,
"\n mutation CreateModel($input: CreateModelInput!) {\n modelMutations {\n create(input: $input) {\n ...ModelListModelItem\n }\n }\n }\n": types.CreateModelDocument,
"\n mutation CreateProject($input: ProjectCreateInput) {\n projectMutations {\n create(input: $input) {\n ...ProjectListProjectItem\n }\n }\n }\n": types.CreateProjectDocument,
"\n mutation CreateProjectInWorkspace($input: WorkspaceProjectCreateInput!) {\n workspaceMutations {\n projects {\n create(input: $input) {\n ...ProjectListProjectItem\n }\n }\n }\n }\n": types.CreateProjectInWorkspaceDocument,
"\n mutation StreamAccessRequestCreate($input: String!) {\n streamAccessRequestCreate(streamId: $input) {\n id\n }\n }\n": types.StreamAccessRequestCreateDocument,
"\n fragment WorkspaceListWorkspaceItem on Workspace {\n id\n slug\n name\n description\n createdAt\n updatedAt\n logo\n role\n readOnly\n }\n": types.WorkspaceListWorkspaceItemFragmentDoc,
"\n fragment WorkspaceListWorkspaceItem on Workspace {\n id\n slug\n name\n description\n createdAt\n updatedAt\n logo\n role\n readOnly\n creationState {\n completed\n }\n permissions {\n canCreateProject {\n authorized\n code\n message\n }\n }\n }\n": types.WorkspaceListWorkspaceItemFragmentDoc,
"\n fragment AutomateFunctionItem on AutomateFunction {\n name\n isFeatured\n id\n creator {\n name\n }\n releases {\n items {\n inputSchema\n }\n }\n }\n": types.AutomateFunctionItemFragmentDoc,
"\n mutation CreateAutomation($projectId: ID!, $input: ProjectAutomationCreateInput!) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n create(input: $input) {\n id\n name\n }\n }\n }\n }\n": types.CreateAutomationDocument,
"\n fragment AutomateFunctionRunItem on AutomateFunctionRun {\n id\n status\n statusMessage\n results\n contextView\n function {\n id\n name\n logo\n }\n }\n": types.AutomateFunctionRunItemFragmentDoc,
@@ -68,10 +110,6 @@ const documents = {
*/
export function graphql(source: string): unknown;
/**
* 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 AcccountTestQuery {\n serverInfo {\n version\n name\n company\n }\n }\n "): (typeof documents)["\n query AcccountTestQuery {\n serverInfo {\n version\n name\n company\n }\n }\n "];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -80,6 +118,10 @@ export function graphql(source: "\n mutation SetActiveWorkspaceMutation($slug:
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation VersionMutations($input: CreateVersionInput!) {\n versionMutations {\n create(input: $input) {\n id\n }\n }\n }\n"): (typeof documents)["\n mutation VersionMutations($input: CreateVersionInput!) {\n versionMutations {\n create(input: $input) {\n id\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation Update($input: UpdateVersionInput!) {\n versionMutations {\n update(input: $input) {\n id\n }\n }\n }\n"): (typeof documents)["\n mutation Update($input: UpdateVersionInput!) {\n versionMutations {\n update(input: $input) {\n id\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -103,7 +145,7 @@ export function graphql(source: "\n mutation StreamAccessRequestCreate($input:
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment WorkspaceListWorkspaceItem on Workspace {\n id\n slug\n name\n description\n createdAt\n updatedAt\n logo\n role\n readOnly\n }\n"): (typeof documents)["\n fragment WorkspaceListWorkspaceItem on Workspace {\n id\n slug\n name\n description\n createdAt\n updatedAt\n logo\n role\n readOnly\n }\n"];
export function graphql(source: "\n fragment WorkspaceListWorkspaceItem on Workspace {\n id\n slug\n name\n description\n createdAt\n updatedAt\n logo\n role\n readOnly\n creationState {\n completed\n }\n permissions {\n canCreateProject {\n authorized\n code\n message\n }\n }\n }\n"): (typeof documents)["\n fragment WorkspaceListWorkspaceItem on Workspace {\n id\n slug\n name\n description\n createdAt\n updatedAt\n logo\n role\n readOnly\n creationState {\n completed\n }\n permissions {\n canCreateProject {\n authorized\n code\n message\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
+81 -29
View File
@@ -1191,8 +1191,12 @@ export type LimitedUserWorkspaceRoleArgs = {
/** Workspace metadata visible to non-workspace members. */
export type LimitedWorkspace = {
__typename?: 'LimitedWorkspace';
/** Workspace admins ordered by join date */
adminTeam: Array<LimitedWorkspaceCollaborator>;
/** Workspace description */
description?: Maybe<Scalars['String']['output']>;
/** If true, the users with a matching domain may join the workspace directly */
discoverabilityAutoJoinEnabled: Scalars['Boolean']['output'];
/** Workspace id */
id: Scalars['ID']['output'];
/** Optional base64 encoded workspace logo image */
@@ -2022,6 +2026,9 @@ export type Project = {
commentThreads: ProjectCommentCollection;
createdAt: Scalars['DateTime']['output'];
description?: Maybe<Scalars['String']['output']>;
/** Public project-level configuration for embedded viewer */
embedOptions: ProjectEmbedOptions;
hasAccessToFeature: Scalars['Boolean']['output'];
id: Scalars['ID']['output'];
invitableCollaborators: WorkspaceCollaboratorCollection;
/** Collaborators who have been invited, but not yet accepted. */
@@ -2060,7 +2067,7 @@ export type Project = {
versions: VersionCollection;
/** Return metadata about resources being requested in the viewer */
viewerResources: Array<ViewerResourceGroup>;
visibility: SimpleProjectVisibility;
visibility: ProjectVisibility;
webhooks: WebhookCollection;
workspace?: Maybe<Workspace>;
workspaceId?: Maybe<Scalars['String']['output']>;
@@ -2103,6 +2110,11 @@ export type ProjectCommentThreadsArgs = {
};
export type ProjectHasAccessToFeatureArgs = {
featureName: WorkspaceFeatureName;
};
export type ProjectInvitableCollaboratorsArgs = {
cursor?: InputMaybe<Scalars['String']['input']>;
filter?: InputMaybe<InvitableCollaboratorsFilter>;
@@ -2353,6 +2365,11 @@ export type ProjectCreateInput = {
visibility?: InputMaybe<ProjectVisibility>;
};
export type ProjectEmbedOptions = {
__typename?: 'ProjectEmbedOptions';
hideSpeckleBranding: Scalars['Boolean']['output'];
};
export type ProjectFileImportUpdatedMessage = {
__typename?: 'ProjectFileImportUpdatedMessage';
/** Upload ID */
@@ -2573,6 +2590,7 @@ export type ProjectPermissionChecks = {
canCreateComment: PermissionCheckResult;
canCreateModel: PermissionCheckResult;
canDelete: PermissionCheckResult;
canInvite: PermissionCheckResult;
canLeave: PermissionCheckResult;
canLoad: PermissionCheckResult;
canMoveToWorkspace: PermissionCheckResult;
@@ -2671,9 +2689,14 @@ export enum ProjectVersionsUpdatedMessageType {
}
export enum ProjectVisibility {
/** Only accessible to explicit collaborators */
Private = 'PRIVATE',
/** Accessible to everyone (even non-logged in users) */
Public = 'PUBLIC',
Unlisted = 'UNLISTED'
/** Legacy - same as public */
Unlisted = 'UNLISTED',
/** Accessible to everyone in the project's workspace */
Workspace = 'WORKSPACE'
}
export type Query = {
@@ -3219,12 +3242,6 @@ export type SetPrimaryUserEmailInput = {
id: Scalars['ID']['input'];
};
/** Visibility without the "discoverable" option */
export enum SimpleProjectVisibility {
Private = 'PRIVATE',
Unlisted = 'UNLISTED'
}
export type SmartTextEditorValue = {
__typename?: 'SmartTextEditorValue';
/** File attachments, if any */
@@ -3300,6 +3317,7 @@ export type Stream = {
/**
* Whether the stream (if public) can be found on public stream exploration pages
* and searches
* @deprecated Discoverability as a feature has been removed.
*/
isDiscoverable: Scalars['Boolean']['output'];
/** Whether the stream can be viewed by non-contributors */
@@ -4094,12 +4112,14 @@ export type UserMeta = {
__typename?: 'UserMeta';
legacyProjectsExplainerCollapsed: Scalars['Boolean']['output'];
newWorkspaceExplainerDismissed: Scalars['Boolean']['output'];
speckleConBannerDismissed: Scalars['Boolean']['output'];
};
export type UserMetaMutations = {
__typename?: 'UserMetaMutations';
setLegacyProjectsExplainerCollapsed: Scalars['Boolean']['output'];
setNewWorkspaceExplainerDismissed: Scalars['Boolean']['output'];
setSpeckleConBannerDismissed: Scalars['Boolean']['output'];
};
@@ -4112,6 +4132,11 @@ export type UserMetaMutationsSetNewWorkspaceExplainerDismissedArgs = {
value: Scalars['Boolean']['input'];
};
export type UserMetaMutationsSetSpeckleConBannerDismissedArgs = {
value: Scalars['Boolean']['input'];
};
export type UserProjectCollection = {
__typename?: 'UserProjectCollection';
cursor?: Maybe<Scalars['String']['output']>;
@@ -4178,6 +4203,7 @@ export type UserUpdateInput = {
};
export type UserWorkspacesFilter = {
completed?: InputMaybe<Scalars['Boolean']['input']>;
search?: InputMaybe<Scalars['String']['input']>;
};
@@ -4436,13 +4462,19 @@ export type Workspace = {
* region.
*/
defaultRegion?: Maybe<ServerRegionItem>;
/** The default seat assigned to users that join a workspace. Used during workspace discovery or on invites without seat types. */
defaultSeatType: WorkspaceSeatType;
description?: Maybe<Scalars['String']['output']>;
/** If true, allow users to automatically join discoverable workspaces (instead of requesting to join) */
discoverabilityAutoJoinEnabled: Scalars['Boolean']['output'];
/** Enable/Disable discovery of the workspace */
discoverabilityEnabled: Scalars['Boolean']['output'];
/** Enable/Disable restriction to invite users to workspace as Guests only */
domainBasedMembershipProtectionEnabled: Scalars['Boolean']['output'];
/** Verified workspace domains */
domains?: Maybe<Array<WorkspaceDomain>>;
/** Workspace-level configuration for models in embedded viewer */
embedOptions: WorkspaceEmbedOptions;
hasAccessToFeature: Scalars['Boolean']['output'];
id: Scalars['ID']['output'];
/** Only available to workspace owners/members */
@@ -4534,6 +4566,7 @@ export type WorkspaceBillingMutationsUpgradePlanArgs = {
/** Overridden by `WorkspaceCollaboratorGraphQLReturn` */
export type WorkspaceCollaborator = {
__typename?: 'WorkspaceCollaborator';
email?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
/** Date that the user joined the workspace. */
joinDate: Scalars['DateTime']['output'];
@@ -4594,8 +4627,14 @@ export type WorkspaceDomainDeleteInput = {
workspaceId: Scalars['ID']['input'];
};
export type WorkspaceEmbedOptions = {
__typename?: 'WorkspaceEmbedOptions';
hideSpeckleBranding: Scalars['Boolean']['output'];
};
export enum WorkspaceFeatureName {
DomainBasedSecurityPolicies = 'domainBasedSecurityPolicies',
HideSpeckleBranding = 'hideSpeckleBranding',
OidcSso = 'oidcSso',
WorkspaceDataRegionSpecificity = 'workspaceDataRegionSpecificity'
}
@@ -4671,6 +4710,7 @@ export type WorkspaceInviteUseInput = {
export type WorkspaceJoinRequest = {
__typename?: 'WorkspaceJoinRequest';
createdAt: Scalars['DateTime']['output'];
email?: Maybe<Scalars['String']['output']>;
id: Scalars['String']['output'];
status: WorkspaceJoinRequestStatus;
user: LimitedUser;
@@ -4721,7 +4761,6 @@ export type WorkspaceMutations = {
/** Dismiss a workspace from the discoverable list, behind the scene a join request is created with the status "dismissed" */
dismiss: Scalars['Boolean']['output'];
invites: WorkspaceInviteMutations;
join: Workspace;
leave: Scalars['Boolean']['output'];
projects: WorkspaceProjectMutations;
requestToJoin: Scalars['Boolean']['output'];
@@ -4729,6 +4768,7 @@ export type WorkspaceMutations = {
setDefaultRegion: Workspace;
update: Workspace;
updateCreationState: Scalars['Boolean']['output'];
updateEmbedOptions: WorkspaceEmbedOptions;
updateRole: Workspace;
updateSeatType: Workspace;
};
@@ -4764,11 +4804,6 @@ export type WorkspaceMutationsDismissArgs = {
};
export type WorkspaceMutationsJoinArgs = {
input: JoinWorkspaceInput;
};
export type WorkspaceMutationsLeaveArgs = {
id: Scalars['ID']['input'];
};
@@ -4795,6 +4830,11 @@ export type WorkspaceMutationsUpdateCreationStateArgs = {
};
export type WorkspaceMutationsUpdateEmbedOptionsArgs = {
input: WorkspaceUpdateEmbedOptionsInput;
};
export type WorkspaceMutationsUpdateRoleArgs = {
input: WorkspaceRoleUpdateInput;
};
@@ -4821,7 +4861,10 @@ export enum WorkspacePaymentMethod {
export type WorkspacePermissionChecks = {
__typename?: 'WorkspacePermissionChecks';
canCreateProject: PermissionCheckResult;
canEditEmbedOptions: PermissionCheckResult;
canInvite: PermissionCheckResult;
canMoveProjectToWorkspace: PermissionCheckResult;
canReadMemberEmail: PermissionCheckResult;
};
@@ -5052,8 +5095,15 @@ export type WorkspaceTeamFilter = {
seatType?: InputMaybe<WorkspaceSeatType>;
};
export type WorkspaceUpdateEmbedOptionsInput = {
hideSpeckleBranding: Scalars['Boolean']['input'];
workspaceId: Scalars['String']['input'];
};
export type WorkspaceUpdateInput = {
defaultSeatType?: InputMaybe<WorkspaceSeatType>;
description?: InputMaybe<Scalars['String']['input']>;
discoverabilityAutoJoinEnabled?: InputMaybe<Scalars['Boolean']['input']>;
discoverabilityEnabled?: InputMaybe<Scalars['Boolean']['input']>;
domainBasedMembershipProtectionEnabled?: InputMaybe<Scalars['Boolean']['input']>;
id: Scalars['String']['input'];
@@ -5077,11 +5127,6 @@ export type WorkspaceUpdatedMessage = {
workspace: Workspace;
};
export type AcccountTestQueryQueryVariables = Exact<{ [key: string]: never; }>;
export type AcccountTestQueryQuery = { __typename?: 'Query', serverInfo: { __typename?: 'ServerInfo', version?: string | null, name: string, company?: string | null } };
export type SetActiveWorkspaceMutationMutationVariables = Exact<{
slug?: InputMaybe<Scalars['String']['input']>;
}>;
@@ -5096,6 +5141,13 @@ export type VersionMutationsMutationVariables = Exact<{
export type VersionMutationsMutation = { __typename?: 'Mutation', versionMutations: { __typename?: 'VersionMutations', create: { __typename?: 'Version', id: string } } };
export type UpdateMutationVariables = Exact<{
input: UpdateVersionInput;
}>;
export type UpdateMutation = { __typename?: 'Mutation', versionMutations: { __typename?: 'VersionMutations', update: { __typename?: 'Version', id: string } } };
export type MarkReceivedVersionMutationVariables = Exact<{
input: MarkReceivedVersionInput;
}>;
@@ -5131,7 +5183,7 @@ export type StreamAccessRequestCreateMutationVariables = Exact<{
export type StreamAccessRequestCreateMutation = { __typename?: 'Mutation', streamAccessRequestCreate: { __typename?: 'StreamAccessRequest', id: string } };
export type WorkspaceListWorkspaceItemFragment = { __typename?: 'Workspace', id: string, slug: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null, role?: string | null, readOnly: boolean };
export type WorkspaceListWorkspaceItemFragment = { __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 } } };
export type AutomateFunctionItemFragment = { __typename?: 'AutomateFunction', name: string, isFeatured: boolean, id: string, creator?: { __typename?: 'LimitedUser', name: string } | null, releases: { __typename?: 'AutomateFunctionReleaseCollection', items: Array<{ __typename?: 'AutomateFunctionRelease', inputSchema?: {} | null }> } };
@@ -5162,7 +5214,7 @@ 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 }> } } | null };
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 CanCreatePersonalProjectQueryVariables = Exact<{ [key: string]: never; }>;
@@ -5186,7 +5238,7 @@ export type CanCreateModelInProjectQuery = { __typename?: 'Query', project: { __
export type ActiveWorkspaceQueryVariables = Exact<{ [key: string]: never; }>;
export type ActiveWorkspaceQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', activeWorkspace?: { __typename?: 'Workspace', id: string, slug: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null, role?: string | null, readOnly: boolean } | null } | null };
export type ActiveWorkspaceQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', activeWorkspace?: { __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 } | null };
export type ProjectListProjectItemFragment = { __typename?: 'Project', id: string, name: string, role?: string | null, updatedAt: string, workspaceId?: string | null, workspace?: { __typename?: 'Workspace', id: string, name: string, slug: string, role?: string | null } | null, models: { __typename?: 'ModelCollection', totalCount: number }, permissions: { __typename?: 'ProjectPermissionChecks', canLoad: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string }, canPublish: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string } } };
@@ -5254,7 +5306,7 @@ export type ProjectDetailsQueryVariables = Exact<{
}>;
export type ProjectDetailsQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, role?: string | null, name: string, visibility: SimpleProjectVisibility, workspace?: { __typename?: 'Workspace', name: string, slug: string, readOnly: boolean, role?: string | null } | null, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', avatar?: string | null, id: string, name: string } }>, permissions: { __typename?: 'ProjectPermissionChecks', canLoad: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string }, canPublish: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string } } } };
export type ProjectDetailsQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, role?: string | null, name: string, visibility: ProjectVisibility, workspace?: { __typename?: 'Workspace', name: string, slug: string, readOnly: boolean, role?: string | null } | null, team: Array<{ __typename?: 'ProjectCollaborator', user: { __typename?: 'LimitedUser', avatar?: string | null, id: string, name: string } }>, permissions: { __typename?: 'ProjectPermissionChecks', canLoad: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string }, canPublish: { __typename?: 'PermissionCheckResult', authorized: boolean, code: string, message: string } } } };
export type AutomateFunctionsQueryVariables = Exact<{ [key: string]: never; }>;
@@ -5300,14 +5352,14 @@ export type ProjectTriggeredAutomationsStatusUpdatedSubscription = { __typename?
export type OnUserProjectsUpdatedSubscriptionVariables = Exact<{ [key: string]: never; }>;
export type OnUserProjectsUpdatedSubscription = { __typename?: 'Subscription', userProjectsUpdated: { __typename?: 'UserProjectsUpdatedMessage', id: string, project?: { __typename?: 'Project', id: string, visibility: SimpleProjectVisibility, team: Array<{ __typename?: 'ProjectCollaborator', id: string, role: string }> } | null } };
export type OnUserProjectsUpdatedSubscription = { __typename?: 'Subscription', userProjectsUpdated: { __typename?: 'UserProjectsUpdatedMessage', id: string, project?: { __typename?: 'Project', id: string, visibility: ProjectVisibility, team: Array<{ __typename?: 'ProjectCollaborator', id: string, role: string }> } | null } };
export type ProjectUpdatedSubscriptionVariables = Exact<{
projectId: Scalars['String']['input'];
}>;
export type ProjectUpdatedSubscription = { __typename?: 'Subscription', projectUpdated: { __typename?: 'ProjectUpdatedMessage', id: string, project?: { __typename?: 'Project', visibility: SimpleProjectVisibility } | null } };
export type ProjectUpdatedSubscription = { __typename?: 'Subscription', projectUpdated: { __typename?: 'ProjectUpdatedMessage', id: string, project?: { __typename?: 'Project', visibility: ProjectVisibility } | null } };
export type SubscriptionSubscriptionVariables = Exact<{
target: ViewerUpdateTrackingTarget;
@@ -5323,16 +5375,16 @@ export type ProjectCommentsUpdatedSubscriptionVariables = Exact<{
export type ProjectCommentsUpdatedSubscription = { __typename?: 'Subscription', projectCommentsUpdated: { __typename?: 'ProjectCommentsUpdatedMessage', type: ProjectCommentsUpdatedMessageType, comment?: { __typename?: 'Comment', id: string, hasParent: boolean, author: { __typename?: 'LimitedUser', avatar?: string | null, id: string, name: string }, parent?: { __typename?: 'Comment', id: string } | null } | null } };
export const WorkspaceListWorkspaceItemFragmentDoc = {"kind":"Document","definitions":[{"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"}}]}}]} as unknown as DocumentNode<WorkspaceListWorkspaceItemFragment, unknown>;
export const WorkspaceListWorkspaceItemFragmentDoc = {"kind":"Document","definitions":[{"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<WorkspaceListWorkspaceItemFragment, unknown>;
export const AutomateFunctionItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateFunctionItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isFeatured"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"creator"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"releases"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inputSchema"}}]}}]}}]}}]} as unknown as DocumentNode<AutomateFunctionItemFragment, unknown>;
export const AutomateFunctionRunItemFragmentDoc = {"kind":"Document","definitions":[{"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"}}]}}]}}]} as unknown as DocumentNode<AutomateFunctionRunItemFragment, unknown>;
export const AutomationRunItemFragmentDoc = {"kind":"Document","definitions":[{"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"}}]}}]}},{"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"}}]}}]}}]} as unknown as DocumentNode<AutomationRunItemFragment, unknown>;
export const ProjectListProjectItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectListProjectItem"},"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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"Field","name":{"kind":"Name","value":"models"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canLoad"},"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":"canPublish"},"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<ProjectListProjectItemFragment, unknown>;
export const VersionListItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"VersionListItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"authorUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"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":"previewUrl"}}]}}]} as unknown as DocumentNode<VersionListItemFragment, unknown>;
export const ModelListModelItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelListModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"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":"FragmentSpread","name":{"kind":"Name","value":"VersionListItem"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"VersionListItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"authorUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"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":"previewUrl"}}]}}]} as unknown as DocumentNode<ModelListModelItemFragment, unknown>;
export const AcccountTestQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AcccountTestQuery"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}}]}}]}}]} as unknown as DocumentNode<AcccountTestQueryQuery, AcccountTestQueryQueryVariables>;
export const SetActiveWorkspaceMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SetActiveWorkspaceMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUserMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setActiveWorkspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}]}]}}]}}]} as unknown as DocumentNode<SetActiveWorkspaceMutationMutation, SetActiveWorkspaceMutationMutationVariables>;
export const VersionMutationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"VersionMutations"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateVersionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionMutations"},"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"}}]}}]}}]}}]} as unknown as DocumentNode<VersionMutationsMutation, VersionMutationsMutationVariables>;
export const UpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Update"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateVersionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update"},"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"}}]}}]}}]}}]} as unknown as DocumentNode<UpdateMutation, UpdateMutationVariables>;
export const MarkReceivedVersionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkReceivedVersion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MarkReceivedVersionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markReceived"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode<MarkReceivedVersionMutation, MarkReceivedVersionMutationVariables>;
export const CreateModelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateModel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateModelInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelMutations"},"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":"FragmentSpread","name":{"kind":"Name","value":"ModelListModelItem"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"VersionListItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"authorUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"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":"previewUrl"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelListModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"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":"FragmentSpread","name":{"kind":"Name","value":"VersionListItem"}}]}}]}}]}}]} as unknown as DocumentNode<CreateModelMutation, CreateModelMutationVariables>;
export const CreateProjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCreateInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"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":"FragmentSpread","name":{"kind":"Name","value":"ProjectListProjectItem"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectListProjectItem"},"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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"Field","name":{"kind":"Name","value":"models"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canLoad"},"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":"canPublish"},"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<CreateProjectMutation, CreateProjectMutationVariables>;
@@ -5340,11 +5392,11 @@ export const CreateProjectInWorkspaceDocument = {"kind":"Document","definitions"
export const StreamAccessRequestCreateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StreamAccessRequestCreate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequestCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<StreamAccessRequestCreateMutation, StreamAccessRequestCreateMutationVariables>;
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"}}]}}]} as unknown as DocumentNode<WorkspaceListQueryQuery, WorkspaceListQueryQueryVariables>;
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 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>;
export const ActiveWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ActiveWorkspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeWorkspace"},"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"}}]}}]} as unknown as DocumentNode<ActiveWorkspaceQuery, ActiveWorkspaceQueryVariables>;
export const ActiveWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ActiveWorkspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeWorkspace"},"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<ActiveWorkspaceQuery, ActiveWorkspaceQueryVariables>;
export const ProjectListQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectListQuery"},"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":"UserProjectsFilter"}}},{"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":"projects"},"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":"ProjectListProjectItem"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectListProjectItem"},"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":"role"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"Field","name":{"kind":"Name","value":"models"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"canLoad"},"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":"canPublish"},"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<ProjectListQueryQuery, ProjectListQueryQueryVariables>;
export const ProjectModelsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModels"},"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":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"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":"ProjectModelsFilter"}}}],"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"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"}}}],"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":"ModelListModelItem"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"VersionListItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"authorUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"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":"previewUrl"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelListModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"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":"FragmentSpread","name":{"kind":"Name","value":"VersionListItem"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectModelsQuery, ProjectModelsQueryVariables>;
export const ModelVersionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ModelVersions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"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":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"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":"ModelVersionsFilter"}}}],"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":"id"}},{"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"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":"VersionListItem"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"VersionListItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Version"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"authorUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"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":"previewUrl"}}]}}]} as unknown as DocumentNode<ModelVersionsQuery, ModelVersionsQueryVariables>;
+17
View File
@@ -0,0 +1,17 @@
/**
* @param previewUrl url that server returns but does not return the corresponding image if the project is private
* @param token auth token to get proper image over url
*/
export async function usePreviewUrl(
token: string,
previewUrl?: string
): Promise<string | undefined> {
if (!previewUrl) return previewUrl
const res = await fetch(previewUrl, {
headers: { Authorization: `Bearer ${token}` }
})
if (!res.ok) return previewUrl //
const blob = await res.blob()
return URL.createObjectURL(blob)
}
-23
View File
@@ -1,6 +1,5 @@
import type { ToastNotification } from '@speckle/ui-components'
import { ToastNotificationType } from '@speckle/ui-components'
import { useConfigStore } from '~/store/config'
import { useHostAppStore } from '~/store/hostApp'
type Versions = {
@@ -18,31 +17,13 @@ export type Version = {
export function useUpdateConnector() {
const hostApp = useHostAppStore()
const config = useConfigStore()
const { $openUrl } = useNuxtApp()
const versions = ref<Version[]>([])
const latestAvailableVersion = ref<Version | null>(null)
const isUpToDate = computed(
() => hostApp.connectorVersion === latestAvailableVersion.value?.Number
)
async function checkUpdate() {
try {
await getVersions()
if (!isUpToDate.value && !config.isDevMode) {
const notification: ToastNotification = {
type: ToastNotificationType.Success,
title: `New connector update available`,
description: latestAvailableVersion.value?.Number.replace('+0', ''), // TODO: currently versions end with "+0" Alan will have a look
cta: {
title: `Update`,
onClick: () => downloadLatestVersion()
}
}
hostApp.setNotification(notification)
}
} catch (e) {
console.error(e)
const notification: ToastNotification = {
@@ -74,9 +55,5 @@ export function useUpdateConnector() {
hostApp.setLatestAvailableVersion(sortedVersions[0])
}
function downloadLatestVersion() {
$openUrl(latestAvailableVersion.value?.Url as string)
}
return { checkUpdate }
}
+20
View File
@@ -18,6 +18,16 @@ export const createVersionMutation = graphql(`
}
`)
export const setVersionMessageMutation = graphql(`
mutation Update($input: UpdateVersionInput!) {
versionMutations {
update(input: $input) {
id
}
}
}
`)
export const markReceivedVersionMutation = graphql(`
mutation MarkReceivedVersion($input: MarkReceivedVersionInput!) {
versionMutations {
@@ -77,6 +87,16 @@ export const workspaceListFragment = graphql(`
logo
role
readOnly
creationState {
completed
}
permissions {
canCreateProject {
authorized
code
message
}
}
}
`)
+3 -3
View File
@@ -1,7 +1,7 @@
import crs from 'crypto-random-string'
import type { AutomationRunItemFragment } from 'lib/common/generated/gql/graphql'
import type { ConversionResult } from 'lib/conversions/conversionResult'
import type { CardSetting } from 'lib/models/card/setting'
import type { AutomationRunItemFragment } from '~/lib/common/generated/gql/graphql'
import type { ConversionResult } from '~/lib/conversions/conversionResult'
import type { CardSetting } from '~/lib/models/card/setting'
import type { IDiscriminatedObject } from '~~/lib/bindings/definitions/common'
import { DiscriminatedObject } from '~~/lib/bindings/definitions/common'
+10 -2
View File
@@ -1,11 +1,19 @@
import type { ConversionResult } from 'lib/conversions/conversionResult'
import type { ConversionResult } from '~/lib/conversions/conversionResult'
export type ModelCardNotificationLevel = 'info' | 'danger' | 'warning' | 'success'
export type ModelCardNotification = {
modelCardId: string
text: string
level: 'info' | 'danger' | 'warning' | 'success'
level: ModelCardNotificationLevel
secondaryCta?: {
name: string
tooltipText?: string
action: () => void
}
cta?: {
name: string
tooltipText?: string
action: () => void
}
/**
+3 -4
View File
@@ -17,9 +17,6 @@ export default defineNuxtConfig({
// lodash: 'lodash-es'
},
// pinia: {
// autoImports: ['defineStore', 'storeToRefs']
// },
runtimeConfig: {
public: {
mixpanelApiHost: 'UNDEFINED',
@@ -29,7 +26,8 @@ export default defineNuxtConfig({
speckleUserId: process.env.SPECKLE_USER_ID,
speckleUrl: process.env.SPECKLE_URL,
speckleSampleProjectId: process.env.SPECKLE_SAMPLE_PROJECT_ID,
speckleSampleModelId: process.env.SPECKLE_SAMPLE_MODEL_ID
speckleSampleModelId: process.env.SPECKLE_SAMPLE_MODEL_ID,
intercomAppId: ''
}
},
vite: {
@@ -53,6 +51,7 @@ export default defineNuxtConfig({
]
},
ssr: false,
devtools: { enabled: false },
build: {
transpile: [
/^@apollo\/client/,
+16 -14
View File
@@ -15,7 +15,7 @@
"postinstall": "nuxt prepare",
"lint:js": "eslint .",
"lint:tsc": "vue-tsc --noEmit",
"lint:prettier": "prettier --config ../../.prettierrc --ignore-path ../../.prettierignore --check .",
"lint:prettier": "prettier --config .prettierrc --ignore-path .prettierignore --check .",
"lint:css": "stylelint \"**/*.{css,vue}\"",
"lint": "yarn lint:js && yarn lint:tsc && yarn lint:prettier && yarn lint:css",
"lint:ci": "yarn lint:tsc && yarn lint:css",
@@ -26,16 +26,17 @@
"@apollo/client": "^3.7.14",
"@headlessui/vue": "^1.7.13",
"@heroicons/vue": "^2.0.12",
"@intercom/messenger-js-sdk": "^0.0.14",
"@jsonforms/core": "3.1.0",
"@jsonforms/vue": "3.1.0",
"@jsonforms/vue-vanilla": "3.1.0",
"@pinia/nuxt": "^0.4.11",
"@speckle/objectloader": "^2.24.0",
"@speckle/objectsender": "^2.24.0",
"@speckle/shared": "^2.24.0",
"@speckle/tailwind-theme": "2.24.1-alpha.0",
"@speckle/ui-components": "^2.24.0",
"@speckle/ui-components-nuxt": "^2.24.0",
"@speckle/objectloader": "^2.25.0",
"@speckle/objectsender": "^2.25.0",
"@speckle/shared": "^2.25.0",
"@speckle/tailwind-theme": "2.25.0",
"@speckle/ui-components": "^2.25.0",
"@speckle/ui-components-nuxt": "^2.25.0",
"@vue/apollo-composable": "^4.0.0-beta.5",
"@vueuse/core": "^9.13.0",
"apollo-upload-client": "^17.0.0",
@@ -55,11 +56,11 @@
"devDependencies": {
"@graphql-codegen/cli": "^5.0.5",
"@graphql-codegen/client-preset": "^4.3.0",
"@nuxt/eslint": "^0.3.13",
"@nuxt/eslint": "^1.3.1",
"@nuxtjs/tailwindcss": "^6.14.0",
"@parcel/watcher": "^2.5.1",
"@types/apollo-upload-client": "^17.0.1",
"@types/eslint": "^8.56.10",
"@types/eslint": "^9.6.1",
"@types/lodash-es": "^4.17.6",
"@types/node": "^18",
"@typescript-eslint/eslint-plugin": "^8.20.0",
@@ -77,10 +78,9 @@
"postcss-html": "^1.8.0",
"postcss-nesting": "^13.0.1",
"prettier": "^2.8.7",
"stylelint": "^15.10.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^26.0.0",
"stylelint": "^16.19.1",
"stylelint-config-recommended-vue": "^1.6.0",
"stylelint-config-standard": "^38.0.0",
"tailwindcss": "^3.4.17",
"type-fest": "^3.5.1",
"typescript": "^5.7.3",
@@ -89,7 +89,9 @@
},
"resolutions": {
"core-js": "3.22.4",
"core-js-compat/semver": "^7.5.4"
"core-js-compat/semver": "^7.5.4",
"@babel/plugin-transform-classes/globals": "13.13.0",
"@babel/traverse/globals": "13.13.0"
},
"packageManager": "yarn@4.9.1"
}
+1 -1
View File
@@ -102,7 +102,7 @@
</div>
</LayoutPanel>
</div>
<div v-if="accounts.length !== 0 && !hasNoModelCards" class="space-y-2">
<div v-if="accounts.length !== 0 && !hasNoModelCards" class="space-y-2 pb-24">
<div v-for="project in store.projectModelGroups" :key="project.projectId">
<CommonProjectModelGroup :project="project" />
</div>
+1 -1
View File
@@ -79,7 +79,7 @@
</p>
</div>
<FormButton
color="card"
:color="'outline'"
full-width
class="sticky top-10 top-16"
@click="runTests()"
+1 -1
View File
@@ -1,5 +1,5 @@
import type { IRawBridge } from '~/lib/bridge/definitions'
import { GenericBridge } from '~/lib/bridge/generic-v2'
import { GenericBridge } from '~/lib/bridge/generic'
import { SketchupBridge } from '~/lib/bridge/sketchup'
import type { IBasicConnectorBinding } from '~/lib/bindings/definitions/IBasicConnectorBinding'
+116
View File
@@ -0,0 +1,116 @@
import { watch, computed, ref } from 'vue'
import Intercom, {
shutdown,
show,
hide,
update,
trackEvent
} from '@intercom/messenger-js-sdk'
import { useAccountStore } from '~/store/accounts'
import { storeToRefs } from 'pinia'
const disabledRoutes: string[] = []
export const useIntercom = () => {
const route = useRoute()
const accountStore = useAccountStore()
const { activeAccount } = storeToRefs(accountStore)
const isInitialized = ref(false)
const isRouteBlacklisted = computed(() => {
return disabledRoutes.some((disabledRoute) => route.path.includes(disabledRoute))
})
const shouldEnableIntercom = computed(() => !isRouteBlacklisted.value)
const bootIntercom = () => {
if (!shouldEnableIntercom.value || isInitialized.value || !activeAccount.value)
return
isInitialized.value = true
Intercom({
/* eslint-disable camelcase */
app_id: 'hoiaq4wn', // note: needs to be harcoded as this is statically served
user_id: activeAccount.value.accountInfo.userInfo.id || '',
name: activeAccount.value.accountInfo.userInfo.name || '',
email: activeAccount.value.accountInfo.userInfo.email || ''
})
window.Intercom = Intercom
}
const showIntercom = () => {
if (!isInitialized.value) return
show()
}
const hideIntercom = () => {
if (!isInitialized.value) return
hide()
}
const shutdownIntercom = () => {
if (!isInitialized.value) return
shutdown()
isInitialized.value = false
}
const trackIntercom = (event: string, metadata?: Record<string, unknown>) => {
if (!isInitialized.value) return
trackEvent(event, metadata)
}
const updateConnectorDetails = (
hostAppName: string,
hostAppVersion: string,
connectorVersion: string
) => {
update({
page_title: `CNX: (hostApp: ${hostAppName}:v${hostAppVersion}),(version: ${connectorVersion})`
})
}
// On route change, check if we need to shutodwn or boot Intercom
watch(route, () => {
if (isRouteBlacklisted.value) {
shutdownIntercom()
} else {
bootIntercom()
}
})
watch(activeAccount, (newValue) => {
if (newValue) {
if (!isInitialized.value) {
bootIntercom() // if active account changed and itercom is not initialised, do it
return // we do not need to update, as that's done by default in the init
}
update({
user_id: activeAccount.value.accountInfo.userInfo.id || '',
name: activeAccount.value.accountInfo.userInfo.name,
email: activeAccount.value.accountInfo.userInfo.email
})
} else {
if (isInitialized.value) {
shutdownIntercom()
}
}
})
return {
show: showIntercom,
hide: hideIntercom,
shutdown: shutdownIntercom,
track: trackIntercom,
updateConnectorDetails
}
}
export default defineNuxtPlugin(() => {
return {
provide: {
intercom: useIntercom()
}
}
})
+2 -3
View File
@@ -15,7 +15,6 @@ import { onError, type ErrorResponse } from '@apollo/client/link/error'
import { getMainDefinition } from '@apollo/client/utilities'
import { setContext } from '@apollo/client/link/context'
import { useHostAppStore } from '~/store/hostApp'
import type { ToastNotification } from '@speckle/ui-components'
import { ToastNotificationType } from '@speckle/ui-components'
export type DUIAccount = {
@@ -86,7 +85,7 @@ export const useAccountStore = defineStore('accountStore', () => {
try {
await acc.client.query({ query: accountTestQuery })
acc.isValid = true
} catch (error) {
} catch {
// TODO: properly dispose and kill this client. It's unclear how to do it.
acc.isValid = false
// NOTE: we do not want to delete the client, as we might want to "refresh" in
@@ -116,7 +115,7 @@ export const useAccountStore = defineStore('accountStore', () => {
if (res.graphQLErrors) {
if (
res.graphQLErrors?.some(
(err) => err.extensions.code === 'SSO_SESSION_MISSING_OR_EXPIRED_ERROR'
(err) => err.extensions?.code === 'SSO_SESSION_MISSING_OR_EXPIRED_ERROR'
)
) {
hostAppStore.setNotification({
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ConnectorConfig } from 'lib/bindings/definitions/IConfigBinding'
import type { ConnectorConfig } from '~/lib/bindings/definitions/IConfigBinding'
import { defineStore } from 'pinia'
export const useConfigStore = defineStore('configStore', () => {
+25 -5
View File
@@ -2,7 +2,7 @@ import type {
DocumentInfo,
DocumentModelStore
} from '~/lib/bindings/definitions/IBasicConnectorBinding'
import type { IModelCard, ModelCardProgress } from 'lib/models/card'
import type { IModelCard, ModelCardProgress } from '~/lib/models/card'
import { useMixpanel } from '~/lib/core/composables/mixpanel'
import type { IReceiverModelCard } from '~/lib/models/card/receiver'
import type {
@@ -12,11 +12,11 @@ import type {
ISenderModelCard,
RevitViewsSendFilter,
SendFilterSelect
} from 'lib/models/card/send'
} from '~/lib/models/card/send'
import type { ToastNotification } from '@speckle/ui-components'
import type { Nullable } from '@speckle/shared'
import type { HostAppError } from '~/lib/bridge/errorHandler'
import type { ConversionResult } from 'lib/conversions/conversionResult'
import type { ConversionResult } from '~/lib/conversions/conversionResult'
import { defineStore } from 'pinia'
import type { CardSetting } from '~/lib/models/card/setting'
import type { DUIAccount } from '~/store/accounts'
@@ -489,7 +489,10 @@ export const useHostAppStore = defineStore('hostAppStore', () => {
if (typeof args.error === 'string') {
model.error = { errorMessage: args.error as string, dismissible: true }
} else {
model.error = args.error as { errorMessage: string; dismissible: boolean }
model.error = args.error as {
errorMessage: string
dismissible: boolean
}
}
}
@@ -674,7 +677,7 @@ export const useHostAppStore = defineStore('hostAppStore', () => {
'documentChanged',
() =>
setTimeout(async () => {
void trackEvent('DUI3 Action', { name: 'Document changed' })
// void trackEvent('DUI3 Action', { name: 'Document changed' }) // noisy
void refreshDocumentInfo()
await refreshDocumentModelStore() // need to awaited since upgrading the card settings need documentModelStore in place
void refreshSendFilters()
@@ -691,6 +694,23 @@ export const useHostAppStore = defineStore('hostAppStore', () => {
await refreshSendFilters()
await getSendSettings()
tryToUpgradeModelCardSettings(sendSettings.value || [], 'SenderModelCard')
// Intercom shenanningans below
// Do not poke intercom in ancient revit version
if (
hostAppName.value?.toLowerCase() === 'revit' &&
hostAppVersion.value?.includes('2022')
)
return
// guards against intercom being sometimes slower to init
setTimeout(() => {
app.$intercom.updateConnectorDetails(
hostAppName.value as string,
hostAppVersion.value as string,
connectorVersion.value as string
)
}, 1000)
}
initializeApp()
+2 -6
View File
@@ -1,9 +1,5 @@
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-recommended-vue',
'stylelint-config-prettier'
],
extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue'],
// add your custom config here
// https://stylelint.io/user-guide/configuration
rules: {
@@ -14,7 +10,7 @@ module.exports = {
ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen']
}
],
'declaration-block-trailing-semicolon': null,
'at-rule-no-deprecated': [true, { ignoreAtRules: ['apply'] }],
'no-descending-specificity': null
},
overrides: [
+1 -1
View File
@@ -1,4 +1,4 @@
import {plugin as speckleThemePlugin} from '@speckle/tailwind-theme'
import { plugin as speckleThemePlugin } from '@speckle/tailwind-theme'
import { tailwindContentEntries as themeEntries } from '@speckle/tailwind-theme/tailwind-configure'
import { tailwindContentEntries as uiLibEntries } from '@speckle/ui-components/tailwind-configure'
import formsPlugin from '@tailwindcss/forms'
+801 -1201
View File
File diff suppressed because it is too large Load Diff