Skip to content

Commit

Permalink
FE2/Automate: Viewer Page Integration (#1851)
Browse files Browse the repository at this point in the history
* feat(ui): adds link to all models from project dashboard card

* feat(ui): refactoring layout to focus on function runs

* chore(server): a bunch of comments regarding some unclear automate api parts

* feat(fe2/automate): cleans up automate results dialog summary

* feat(fe2/automate): adds automation status to model list view

* feat(fe2/automate): adds automation status to version card

* feat(fe2/automate): adds automation status to version card properly

* feat(fe2/automate): linting & adds id back to func name for refs

* fix(fe2): linting errs/fe2 tests

* fix(fe2): types

* fix(fe2): types...

* fix(fe2/automate): brings back status update tracking 🤞

* fix(fe2/automate): scaffolds viewer automate panel

* fix(fe2/automate): revamps viewer resource query to include automation status, adds proper sidebar button for automation status

* fix(fe2/automate): more WIP work on automation status integration in viewer page

* feat(fe2/automate): adds important note to self

* feat(fe2/automate): more results display wokr

* feat(fe2/automate): adds resulting models to query, improves layout

* feat(fe2/automate): wraps up result model overlays

* feat(fe2/automate): fixes up multiple attachment layout errors

* feat(fe2/automate): adds view results button in lieu of context view, but in the presence of resultVersions

* feat(fe2/automate): minor fix to viewer layout panel to only show actions if slot is populated

* feat(fe2/automate): cleanup and type fixes

* feat(fe2/automate): automate dialog main action > view model WIP

* feat(fe2/automate): wraps up view model in automation status dialog

* feat(fe2/automate): copy change in automation status dialog

* feat(fe2/automate): eslint fix

* feat(fe2/automate): prettier :/

* feat(fe2/automate): adds gradient colouring

* feat(fe2/automate): comments

* feat(fe2/automate): limits initial length of displayed runs, adds poor man's pagination, and reverts to run result click click behaviour

* feat(fe2/automate): lint
  • Loading branch information
didimitrie authored Nov 4, 2023
1 parent bc3989f commit 53214cc
Show file tree
Hide file tree
Showing 15 changed files with 742 additions and 365 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<template>
<div class="">
<FormButton v-tippy="fileInfo.name" text size="xs" @click="onDownloadClick">
<span class="max-w-[5rem] truncate">{{ fileInfo.name }}</span>
<PaperClipIcon class="w-3 h-3 text-primary" />
</FormButton>
</div>
<FormButton v-tippy="fileInfo.name" text @click="onDownloadClick">
<span :class="`${restrictWidth ? 'max-w-[5rem]' : ''} truncate`">
{{ fileInfo.name }}
</span>
<PaperClipIcon class="w-3 h-3 text-primary" />
</FormButton>
</template>
<script setup lang="ts">
import { PaperClipIcon } from '@heroicons/vue/20/solid'
Expand All @@ -14,16 +14,22 @@ import { useFileDownload } from '~~/lib/core/composables/fileUpload'
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
import { ensureError } from '@speckle/shared'
const projectId = inject<string>('projectId') as string
const { download } = useFileDownload()
const { triggerNotification } = useGlobalToast()
const props = defineProps<{
blobId: string
}>()
const props = withDefaults(
defineProps<{
blobId: string
projectId: string
restrictWidth: boolean
}>(),
{
restrictWidth: true
}
)
const { result } = useQuery(blobInfoQuery, () => ({
streamId: projectId,
streamId: props.projectId,
blobId: props.blobId
}))
Expand All @@ -40,7 +46,7 @@ const onDownloadClick = async () => {
await download({
blobId: props.blobId,
fileName: fileInfo.value.name as string,
projectId
projectId: props.projectId
})
} catch (e) {
triggerNotification({
Expand Down
209 changes: 209 additions & 0 deletions packages/frontend-2/components/automation/ViewerFunctionRunItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<template>
<div
:class="`border border-blue-500/10 rounded-md space-y-2 overflow-hidden ${
expanded ? 'shadow' : ''
}`"
>
<button
class="flex space-x-1 items-center max-w-full w-full px-1 py-1 h-8 transition hover:bg-primary-muted"
@click="expanded = !expanded"
>
<div>
<Component
:is="statusMetaData.icon"
v-tippy="functionRun.status"
:class="['h-4 w-4 outline-none', statusMetaData.iconColor]"
/>
</div>
<div
class="bg-blue-500/10 text-primary font-bold h-4 w-4 rounded-md shrink-0 flex justify-center text-center items-center overflow-hidden"
>
<img
v-if="functionRun.functionLogo"
:src="functionRun.functionLogo"
alt="function logo"
/>
<span v-else class="text-xs">λ</span>
</div>

<div class="font-bold text-xs truncate">
{{ automationName ? automationName + ' / ' : '' }}{{ functionRun.functionName }}
</div>

<div class="h-full grow flex justify-end">
<button
class="hover:bg-primary-muted hover:text-primary flex h-full items-center justify-center rounded"
>
<ChevronDownIcon
:class="`h-3 w-3 transition ${!expanded ? '-rotate-90' : 'rotate-0'}`"
/>
</button>
</div>
</button>
<div v-if="expanded" class="px-2 pb-2 space-y-4">
<!-- Status message -->
<div class="space-y-1">
<div class="text-xs font-bold text-foreground-2">Status</div>
<div
v-if="
functionRun.status === AutomationRunStatus.Initializing ||
functionRun.status === AutomationRunStatus.Running
"
class="text-xs text-foreground-2 italic"
>
Function is {{ functionRun.status.toLowerCase() }}.
</div>
<div v-else class="text-xs text-foreground-2 italic">
{{ functionRun.statusMessage || 'No status message' }}
</div>
</div>

<!-- Attachments -->
<div
v-if="attachments.length !== 0"
class="border-t pt-2 border-foreground-2 space-y-1"
>
<div class="text-xs font-bold text-foreground-2">Attachments</div>
<div class="ml-[2px] justify-start">
<AutomationAttachmentButton
v-for="id in attachments"
:key="id"
:blob-id="id"
:project-id="projectId"
size="xs"
link
class="mr-2"
/>
</div>
</div>
<!-- TODO: Overlay result versions -->
<div
v-if="typedFunctionRun.resultVersions.length !== 0"
class="border-t pt-2 border-foreground-2"
>
<div class="text-xs font-bold text-foreground-2 mb-2">Resulting Models</div>
<!-- <div class="text-xs">{{ typedFunctionRun.resultVersions }}</div> -->
<div v-for="version in typedFunctionRun.resultVersions" :key="version.id">
<FormButton
v-if="!hasResource(version)"
size="xs"
link
class="truncate max-w-full"
@click="loadResultVersion(version)"
>
Overlay "{{ version.model.name }}"
</FormButton>
<FormButton v-else size="xs" link class="truncate max-w-full" disabled>
"{{ version.model.name }}" is already overlaid
</FormButton>
</div>
</div>
<!-- Results -->
<div
v-if="
typedFunctionRun.results &&
typedFunctionRun.results.values &&
typedFunctionRun.results.values.objectResults &&
typedFunctionRun.results.values.objectResults.length !== 0
"
class="border-t pt-2 border-foreground-2"
>
<div class="text-xs font-bold text-foreground-2 mb-2">Results</div>
<div class="space-y-1">
<AutomationViewerResultRowItem
v-for="(
result, index
) in typedFunctionRun.results.values.objectResults.slice(0, pageRunLimit)"
:key="index"
:function-id="typedFunctionRun.functionId"
:result="result"
/>
<FormButton
v-if="pageRunLimit < typedFunctionRun.results.values.objectResults.length"
size="xs"
color="card"
class="w-full"
@click="pageRunLimit += 10"
>
Load more ({{
typedFunctionRun.results.values.objectResults.length - pageRunLimit
}}
hidden results)
</FormButton>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ChevronDownIcon } from '@heroicons/vue/24/outline'
import {
AutomationFunctionRun,
AutomationRunStatus,
Version
} from '~~/lib/common/generated/gql/graphql'
import { SpeckleViewer } from '@speckle/shared'
import { resolveStatusMetadata } from '~~/lib/automations/helpers/resolveStatusMetadata'
import {
useInjectedViewerState,
useInjectedViewerRequestedResources
} from '~~/lib/viewer/composables/setup'
const { projectId } = useInjectedViewerState()
const { items } = useInjectedViewerRequestedResources()
type ObjectResult = {
category: string
objectIds: string[]
message: string
level: 'ERROR' | 'WARNING' | 'INFO'
}
const props = defineProps<{
functionRun: AutomationFunctionRun
automationName: string
}>()
const pageRunLimit = ref(5)
const typedFunctionRun = computed(() => {
return props.functionRun as AutomationFunctionRun & {
results: { values: { blobIds: string[]; objectResults: ObjectResult[] } }
}
})
const expanded = ref(false)
const attachments = computed(() => {
if (
!typedFunctionRun.value.results ||
!typedFunctionRun.value.results.values ||
!typedFunctionRun.value.results.values.blobIds
)
return []
return typedFunctionRun.value.results?.values?.blobIds.filter((b) => !!b)
})
const statusMetaData = resolveStatusMetadata(props.functionRun.status)
const hasResource = (version: Version) => {
for (const res of items.value) {
const typedRes = res as unknown as { modelId: string; versionId: string }
if (typedRes.modelId === version.model.id && typedRes.versionId === version.id)
return true
}
return false
}
const loadResultVersion = async (version: Version) => {
const modelId = version.model.id
const versionId = version.id
await items.update([
...items.value,
...SpeckleViewer.ViewerRoute.resourceBuilder()
.addModel(modelId, versionId)
.toResources()
])
}
</script>
Loading

0 comments on commit 53214cc

Please sign in to comment.