Merge branch 'main' of github.com:specklesystems/speckle-server into alessandro/web-2362-list-workspace-pending-requests

This commit is contained in:
Alessandro Magionami
2025-01-14 16:32:12 +01:00
8 changed files with 80 additions and 53 deletions
@@ -1,5 +1,5 @@
<template>
<div v-if="detailedRender">
<div v-if="detailedRender" class="mb-2">
<div class="relative">
<div v-if="detailedRender.status === 'COMPLETED' && renderUrl" class="group">
<button class="relative flex cursor-zoom-in" @click="openPreview">
@@ -55,7 +55,7 @@
</div>
<div
v-if="detailedRender.status !== 'COMPLETED'"
class="relative w-full h-32 rounded-lg flex items-center justify-center"
class="relative w-full h-40 rounded-lg flex items-center justify-center"
>
<div
:class="`absolute rounded-lg w-full h-full top-0 ${
@@ -66,7 +66,7 @@
<ExclamationCircleIcon v-else class="w-6 text-danger" />
</div>
<div
class="absolute bottom-2 left-2 right-2 space-x-2 flex items-center min-w-0 max-w-full overflow-hidden z-10"
class="absolute bottom-2 left-0 gap-x-2 px-2 flex items-center min-w-0 max-w-full overflow-hidden z-10"
>
<div
class="bg-foundation p-0.5 flex items-center gap-x-1 min-w-0 max-w-full rounded-md"
@@ -1,6 +1,6 @@
<template>
<div
class="flex flex-col gap-y-2 max-h-64 2xl:max-h-96 overflow-y-scroll overflow-x-hidden simple-scroll pb-4"
class="flex flex-col gap-y-2 max-h-64 2xl:max-h-96 overflow-y-auto overflow-x-hidden simple-scrollbar"
>
<ViewerGendoItem
v-for="render in renders"
@@ -15,14 +15,12 @@
</CommonBadge>
</div>
</template>
<div class="pt-2">
<div class="px-4 flex flex-col gap-y-2">
<CommonAlert v-if="showAlert" :color="alertColor" size="xs">
<template #title>
{{ alertMessage }}
</template>
<div class="pt-3">
<div class="px-3 flex flex-col gap-y-3">
<CommonAlert v-if="!limits" color="danger" size="xs">
<template #title>No credits available</template>
</CommonAlert>
<div class="flex flex-col gap-y-2">
<div class="flex flex-col gap-y-3">
<FormTextArea
v-model="prompt"
name="prompt"
@@ -31,6 +29,9 @@
color="foundation"
:disabled="isLoading || timeOutWait || isOutOfCredits"
textarea-classes="sm:!min-h-24"
@keypress.enter.prevent="
!isLoading && !timeOutWait && !isOutOfCredits && prompt && enqueMagic()
"
/>
<div class="flex justify-between gap-2 items-center text-foreground-2">
<FormButton
@@ -41,28 +42,32 @@
target="_blank"
>
<div class="flex items-center gap-1 text-foreground-2 font-normal">
<span>Writing prompts</span>
<span>Learn to prompt</span>
<ArrowTopRightOnSquareIcon class="h-3 w-3" />
</div>
</FormButton>
<FormButton
:disabled="!prompt || isLoading || timeOutWait || isOutOfCredits"
@click="enqueMagic()"
<View
:key="`gendo-credits-${isOutOfCredits}`"
v-tippy="isOutOfCredits ? 'No credits remaining' : undefined"
>
Generate
</FormButton>
<FormButton
:disabled="!prompt || isLoading || timeOutWait || isOutOfCredits"
@click="enqueMagic()"
>
Generate
</FormButton>
</View>
</div>
</div>
<ViewerGendoList @reuse-prompt="prompt = $event" />
</div>
<div
class="flex w-full items-center justify-between gap-2 border-t border-outline-2 py-1 px-2"
class="flex w-full items-center justify-between gap-2 border-t border-outline-2 py-1 px-1"
>
<FormButton color="subtle" size="sm" @click="isFeedbackOpen = true">
<div class="flex items-center gap-1 text-foreground-2 font-normal">
<IconFeedback class="h-3 w-3" />
<span>Feedback</span>
<span>Give us feedback</span>
</div>
</FormButton>
<FormButton
@@ -74,12 +79,11 @@
>
<div class="flex items-center gap-1 text-foreground-2 font-normal">
<span>Terms</span>
<ArrowTopRightOnSquareIcon class="h-3 w-3" />
</div>
</FormButton>
</div>
</div>
<template v-if="!showAlert && limits" #actions>
<template v-if="limits" #actions>
<div class="text-body-2xs p-1">
{{ limits.used }}/{{ limits.limit }} free renders used
<span class="hidden-under-250">this month</span>
@@ -121,11 +125,11 @@ const timeOutWait = ref(false)
const isFeedbackOpen = ref(false)
const suggestedPrompts = ref<string[]>([
'Example: Minimalist Scandinavian interior with warm natural lighting',
'Example: Luxury penthouse with floor-to-ceiling windows and city views',
'Example: Cozy industrial loft with exposed brick and steel elements',
'Example: Modern office space with biophilic design elements',
'Example: High-end retail space with dramatic lighting'
'Example: Minimalist Scandinavian interior with warm natural lighting...',
'Example: Luxury penthouse with floor-to-ceiling windows and city views...',
'Example: Cozy industrial loft with exposed brick and steel elements...',
'Example: Modern office space with biophilic design elements...',
'Example: High-end retail space with dramatic lighting...'
])
const { result, refetch } = useQuery(activeUserGendoLimits)
@@ -143,27 +147,6 @@ const isOutOfCredits = computed(() => {
return (limits.value?.used || 0) >= (limits.value?.limit || 0)
})
const isNearingLimit = computed(() => {
if (!limits.value) return
const usagePercent = (limits.value?.used / limits.value?.limit) * 100
return usagePercent >= 80
})
const alertColor = computed(() => {
if (isOutOfCredits.value) return 'danger'
if (isNearingLimit.value) return 'warning'
return 'neutral'
})
const alertMessage = computed(() => {
if (!limits.value) return 'No credits available'
return `${limits.value.used}/${limits.value.limit} free renders used this month`
})
const showAlert = computed(
() => !limits.value || isNearingLimit.value || isOutOfCredits.value
)
const enqueMagic = async () => {
isLoading.value = true
const [depthData, width, height] = await viewerInstance
@@ -4285,6 +4285,10 @@ export type WorkspaceCreationStateInput = {
workspaceId: Scalars['ID']['input'];
};
export type WorkspaceDismissInput = {
workspaceId: Scalars['ID']['input'];
};
export type WorkspaceDomain = {
__typename?: 'WorkspaceDomain';
domain: Scalars['String']['output'];
@@ -4378,6 +4382,8 @@ export type WorkspaceMutations = {
delete: Scalars['Boolean']['output'];
deleteDomain: Workspace;
deleteSsoProvider: Scalars['Boolean']['output'];
/** 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'];
@@ -4415,6 +4421,11 @@ export type WorkspaceMutationsDeleteSsoProviderArgs = {
};
export type WorkspaceMutationsDismissArgs = {
input: WorkspaceDismissInput;
};
export type WorkspaceMutationsJoinArgs = {
input: JoinWorkspaceInput;
};
@@ -8117,6 +8128,7 @@ export type WorkspaceMutationsFieldArgs = {
delete: WorkspaceMutationsDeleteArgs,
deleteDomain: WorkspaceMutationsDeleteDomainArgs,
deleteSsoProvider: WorkspaceMutationsDeleteSsoProviderArgs,
dismiss: WorkspaceMutationsDismissArgs,
invites: {},
join: WorkspaceMutationsJoinArgs,
leave: WorkspaceMutationsLeaveArgs,
@@ -9,11 +9,18 @@ import {
getServerInfoFactory,
updateServerInfoFactory,
getPublicRolesFactory,
getPublicScopesFactory
getPublicScopesFactory,
getServerInfoFromCacheFactory,
storeServerInfoInCacheFactory
} from '@/modules/core/repositories/server'
import { db } from '@/db/knex'
import { Resolvers } from '@/modules/core/graph/generated/graphql'
import { LRUCache } from 'lru-cache'
import { ServerInfo } from '@/modules/core/helpers/types'
const cache = new LRUCache<string, ServerInfo>({ max: 1, ttl: 60 * 1000 })
const getServerInfoFromCache = getServerInfoFromCacheFactory({ cache })
const storeServerInfoInCache = storeServerInfoInCacheFactory({ cache })
const getServerInfo = getServerInfoFactory({ db })
const updateServerInfo = updateServerInfoFactory({ db })
const getPublicRoles = getPublicRolesFactory({ db })
@@ -22,7 +29,11 @@ const getPublicScopes = getPublicScopesFactory({ db })
export = {
Query: {
async serverInfo() {
return await getServerInfo()
const cachedServerInfo = getServerInfoFromCache()
if (cachedServerInfo) return cachedServerInfo
const serverInfo = await getServerInfo()
storeServerInfoInCache({ serverInfo })
return serverInfo
}
},
ServerInfo: {
@@ -58,6 +69,9 @@ export = {
const update = removeNullOrUndefinedKeys(args.info)
await updateServerInfo(update)
// we're currently going to ignore, that this should be propagated to all
// backend instances, and going to rely on the TTL in the cache to propagate the changes
cache.clear()
return true
},
serverInfoMutations: () => ({})
@@ -18,6 +18,7 @@ import {
getServerVersion
} from '@/modules/shared/helpers/envHelper'
import { Knex } from 'knex'
import { LRUCache } from 'lru-cache'
const ServerConfig = buildTableHelper('server_config', [
'id',
@@ -38,6 +39,21 @@ const tables = {
scopes: (db: Knex) => db<ScopeRecord>(Scopes.name)
}
const SERVER_CONFIG_CACHE_KEY = 'server_config'
export const getServerInfoFromCacheFactory =
({ cache }: { cache: LRUCache<string, ServerInfo> }) =>
() => {
const serverInfo = cache.get(SERVER_CONFIG_CACHE_KEY)
return serverInfo ?? null
}
export const storeServerInfoInCacheFactory =
({ cache }: { cache: LRUCache<string, ServerInfo> }) =>
({ serverInfo }: { serverInfo: ServerInfo }) => {
cache.set(SERVER_CONFIG_CACHE_KEY, serverInfo)
}
export const getServerInfoFactory =
(deps: { db: Knex }): GetServerInfo =>
async () => {
+3 -1
View File
@@ -459,12 +459,14 @@ const getStream = () => {
// 'https://speckle.xyz/streams/27e89d0ad6/commits/5ed4b74252'
//Gingerbread
'https://latest.speckle.systems/projects/387050bffe/models/48f7eb26fb'
// 'https://latest.speckle.systems/projects/387050bffe/models/48f7eb26fb'
// DUI3 Mesh Colors
// 'https://app.speckle.systems/projects/93200a735d/models/cbacd3eaeb@344a397239'
// Instance toilets
// 'https://app.speckle.systems/projects/e89b61b65c/models/2a0995f124'
'https://latest.speckle.systems/projects/3fe1880c36/models/65bb4287a8'
)
}
@@ -286,9 +286,9 @@ export class SpeckleGeometryConverter extends GeometryConverter {
Logger.warn(
`Mesh (id ${node.raw.id}) colours are mismatched with vertice counts. The number of colours must equal the number of vertices.`
)
}
} else
/** We want the colors in linear space */
colors = this.unpackColors(colorsRaw, true)
colors = this.unpackColors(colorsRaw, true)
}
return {