@revenexx/studio-integrations 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/TemplateCard.vue +12 -1
- package/dist/runtime/composables/useIntegrationsTemplates.js +14 -5
- package/dist/runtime/i18n/messages/de.json +1 -0
- package/dist/runtime/i18n/messages/en.json +1 -0
- package/dist/runtime/pages/integrations/templates/index.vue +1 -1
- package/dist/runtime/types/integrations.d.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @revenexx/studio-integrations
|
|
2
2
|
|
|
3
|
+
## 3.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 259fe08: A template card whose definition could not be fetched now says "Couldn't load the flow" instead of showing "Loading the flow" for the rest of the visit, on every surface that draws the card. Sorting the gallery by most steps puts templates whose step count is not known after every template whose count is, rather than ranking them as having no steps (PO-249).
|
|
8
|
+
|
|
3
9
|
## 3.0.0
|
|
4
10
|
|
|
5
11
|
### Major Changes
|
package/dist/module.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, resolveComponent } from "vue";
|
|
3
|
-
import { RefreshCircle as Loader2 } from "#solar-icons";
|
|
3
|
+
import { RefreshCircle as Loader2, DangerTriangle as AlertTriangle } from "#solar-icons";
|
|
4
4
|
import { triggerGlyph as glyphForTrigger } from "../../data/triggerGlyphs";
|
|
5
5
|
import { templateCategoryLabel, templatePackageLabel } from "../../data/integrationTemplates";
|
|
6
6
|
import TemplateFlowPreview from "./TemplateFlowPreview.vue";
|
|
@@ -68,6 +68,17 @@ const chipSize = computed(() => full.value ? "px-2 py-0.5 text-xs" : "px-1.5 py-
|
|
|
68
68
|
:height="full ? 116 : 96"
|
|
69
69
|
:class="full ? 'mt-7' : ''"
|
|
70
70
|
/>
|
|
71
|
+
<!-- The read came back without a definition. Said in words, because a
|
|
72
|
+
spinner here would wait for the rest of the visit, and the card would
|
|
73
|
+
go on looking like a template still worth waiting for (PO-249). -->
|
|
74
|
+
<div
|
|
75
|
+
v-else-if="template.detailFailed"
|
|
76
|
+
role="status"
|
|
77
|
+
class="flex h-full items-center justify-center gap-1.5 px-3 text-xs text-muted-foreground"
|
|
78
|
+
>
|
|
79
|
+
<AlertTriangle :size="full ? 14 : 12" class="shrink-0" />
|
|
80
|
+
{{ t("integration.templates.card.state.flowUnavailable") }}
|
|
81
|
+
</div>
|
|
71
82
|
<!-- Named, because "still on its way" and "this template draws nothing"
|
|
72
83
|
are the same sight otherwise — and a spinner nobody can address is a
|
|
73
84
|
promise no test can hold to. -->
|
|
@@ -95,10 +95,12 @@ export function useIntegrationsTemplates() {
|
|
|
95
95
|
const t = useIntegrationsT();
|
|
96
96
|
const rawList = useState("integrations.templates.raw", () => []);
|
|
97
97
|
const rawDetails = useState("integrations.templates.rawDetails", () => ({}));
|
|
98
|
+
const failedDetails = useState("integrations.templates.failedDetails", () => ({}));
|
|
98
99
|
const templates = computed(() => rawList.value.map((item) => {
|
|
99
100
|
const base = mapListItem(item);
|
|
100
101
|
const detail = rawDetails.value[item.slug];
|
|
101
|
-
|
|
102
|
+
if (detail) return applyDetail(base, detail);
|
|
103
|
+
return failedDetails.value[item.slug] ? { ...base, detailFailed: true } : base;
|
|
102
104
|
}));
|
|
103
105
|
const loaded = useState("integrations.templates.loaded", () => false);
|
|
104
106
|
const loading = useState("integrations.templates.loading", () => false);
|
|
@@ -112,11 +114,13 @@ export function useIntegrationsTemplates() {
|
|
|
112
114
|
const list2 = unwrapList(await api.get("/templates"));
|
|
113
115
|
rawList.value = list2;
|
|
114
116
|
rawDetails.value = {};
|
|
117
|
+
failedDetails.value = {};
|
|
115
118
|
loaded.value = true;
|
|
116
119
|
} catch (e) {
|
|
117
120
|
error.value = errorReason(e, t("integration.templates.state.error.fallback"));
|
|
118
121
|
rawList.value = [];
|
|
119
122
|
rawDetails.value = {};
|
|
123
|
+
failedDetails.value = {};
|
|
120
124
|
loaded.value = true;
|
|
121
125
|
} finally {
|
|
122
126
|
gate.release();
|
|
@@ -142,14 +146,19 @@ export function useIntegrationsTemplates() {
|
|
|
142
146
|
});
|
|
143
147
|
function remember(details) {
|
|
144
148
|
const found = [...details].filter((entry) => entry[1] !== null);
|
|
145
|
-
|
|
146
|
-
rawDetails.value = { ...rawDetails.value, ...Object.fromEntries(found) };
|
|
149
|
+
const missing = [...details].filter(([, detail]) => detail === null).map(([slug]) => slug);
|
|
150
|
+
if (found.length) rawDetails.value = { ...rawDetails.value, ...Object.fromEntries(found) };
|
|
151
|
+
if (found.length || missing.length) {
|
|
152
|
+
const failed = { ...failedDetails.value };
|
|
153
|
+
for (const [slug] of found) delete failed[slug];
|
|
154
|
+
for (const slug of missing) failed[slug] = true;
|
|
155
|
+
failedDetails.value = failed;
|
|
156
|
+
}
|
|
147
157
|
}
|
|
148
158
|
async function fetchDetail(slug) {
|
|
149
159
|
const base = get(slug);
|
|
150
160
|
if (!base?.fromApi) return base;
|
|
151
|
-
|
|
152
|
-
if (detail) remember(/* @__PURE__ */ new Map([[slug, detail]]));
|
|
161
|
+
remember(/* @__PURE__ */ new Map([[slug, await loader.one(slug)]]));
|
|
153
162
|
return get(slug);
|
|
154
163
|
}
|
|
155
164
|
async function ensureDetails(slugs, _concurrency = 4) {
|
|
@@ -1113,6 +1113,7 @@
|
|
|
1113
1113
|
"integration.secrets.table.column.key": "Schlüssel",
|
|
1114
1114
|
"integration.secrets.table.column.updated": "Aktualisiert",
|
|
1115
1115
|
"integration.shell.action.skipToContent": "Zum Inhalt springen",
|
|
1116
|
+
"integration.templates.card.state.flowUnavailable": "Der Ablauf konnte nicht geladen werden",
|
|
1116
1117
|
"integration.templates.card.state.loadingFlow": "Der Ablauf wird geladen",
|
|
1117
1118
|
"integration.templates.card.trigger.event": "Event-gesteuert",
|
|
1118
1119
|
"integration.templates.card.trigger.schedule": "Geplant",
|
|
@@ -1113,6 +1113,7 @@
|
|
|
1113
1113
|
"integration.secrets.table.column.key": "Key",
|
|
1114
1114
|
"integration.secrets.table.column.updated": "Updated",
|
|
1115
1115
|
"integration.shell.action.skipToContent": "Skip to content",
|
|
1116
|
+
"integration.templates.card.state.flowUnavailable": "Couldn't load the flow",
|
|
1116
1117
|
"integration.templates.card.state.loadingFlow": "Loading the flow",
|
|
1117
1118
|
"integration.templates.card.trigger.event": "Event-driven",
|
|
1118
1119
|
"integration.templates.card.trigger.schedule": "Scheduled",
|
|
@@ -49,7 +49,7 @@ const filtered = computed(() => {
|
|
|
49
49
|
const sorted = [...items];
|
|
50
50
|
if (sortBy.value === "name") sorted.sort((a, b) => a.name.localeCompare(b.name));
|
|
51
51
|
else if (sortBy.value === "level") sorted.sort((a, b) => (LEVEL_RANK[a.level] ?? 0) - (LEVEL_RANK[b.level] ?? 0) || a.name.localeCompare(b.name));
|
|
52
|
-
else sorted.sort((a, b) => b.nodeCount - a.nodeCount || a.name.localeCompare(b.name));
|
|
52
|
+
else sorted.sort((a, b) => Number(!!b.detailLoaded) - Number(!!a.detailLoaded) || b.nodeCount - a.nodeCount || a.name.localeCompare(b.name));
|
|
53
53
|
return sorted;
|
|
54
54
|
});
|
|
55
55
|
watch(() => templates.value.length, () => {
|
|
@@ -616,6 +616,8 @@ export interface WorkflowTemplate {
|
|
|
616
616
|
}>;
|
|
617
617
|
/** True once the heavy definition blob has been merged via fetchDetail. */
|
|
618
618
|
detailLoaded?: boolean;
|
|
619
|
+
/** True when the definition was asked for and did not arrive — not "still on its way". */
|
|
620
|
+
detailFailed?: boolean;
|
|
619
621
|
/** Source node package this template ships with (e.g. Business Central). */
|
|
620
622
|
package?: {
|
|
621
623
|
name: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revenexx/studio-integrations",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Integration Studio (core: workflows, node editor, runs, schedules, credentials, secrets, audit) as a standalone, publishable Nuxt module.",
|
|
6
6
|
"repository": {
|