@galaxy-tool-util/gxwf-report-shell 0.0.0

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/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@galaxy-tool-util/gxwf-report-shell",
3
+ "version": "0.0.0",
4
+ "description": "Pre-built Vue 3 Galaxy workflow report components for CDN delivery",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./src/index.ts",
9
+ "import": "./src/index.ts"
10
+ },
11
+ "./*.vue": "./src/*.vue"
12
+ },
13
+ "files": [
14
+ "src",
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "dependencies": {
20
+ "@primevue/themes": "^4.3.0",
21
+ "primevue": "^4.3.0",
22
+ "vue": "^3.5.0",
23
+ "@galaxy-tool-util/schema": "0.2.0"
24
+ },
25
+ "devDependencies": {
26
+ "@vitejs/plugin-vue": "^5.2.0",
27
+ "vite": "^6.2.0",
28
+ "vue-tsc": "^2.2.0"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/jmchilton/galaxy-tool-util-ts",
33
+ "directory": "packages/gxwf-report-shell"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public",
37
+ "provenance": true
38
+ },
39
+ "license": "MIT",
40
+ "scripts": {
41
+ "build": "vite build --config vite.config.shell.ts",
42
+ "typecheck": "vue-tsc --noEmit",
43
+ "lint": "eslint src/",
44
+ "format": "prettier --check 'src/**/*.{ts,vue}'",
45
+ "format-fix": "prettier --write 'src/**/*.{ts,vue}'"
46
+ }
47
+ }
@@ -0,0 +1,112 @@
1
+ <template>
2
+ <div class="operation-report">
3
+ <div class="summary-row">
4
+ <Tag
5
+ :value="`${report.total_removed} key${report.total_removed !== 1 ? 's' : ''} removed`"
6
+ :severity="report.total_removed > 0 ? 'warn' : 'success'"
7
+ />
8
+ <Tag
9
+ :value="`${report.steps_with_removals} step${report.steps_with_removals !== 1 ? 's' : ''} affected`"
10
+ severity="secondary"
11
+ />
12
+ </div>
13
+
14
+ <DataTable :value="report.results" size="small">
15
+ <Column field="step" header="Step" />
16
+ <Column header="Tool">
17
+ <template #body="{ data }: { data: CleanStepResult }">
18
+ <ToolId :tool-id="data.tool_id" />
19
+ </template>
20
+ </Column>
21
+ <Column header="Removed Keys">
22
+ <template #body="{ data }: { data: CleanStepResult }">
23
+ <span v-if="data.skipped" class="skip-reason">{{ data.skip_reason || "skipped" }}</span>
24
+ <span v-else-if="data.removed_keys.length === 0" class="no-changes">—</span>
25
+ <ul v-else class="key-list">
26
+ <li v-for="(k, i) in data.removed_keys" :key="i">
27
+ <code>{{ k }}</code>
28
+ </li>
29
+ </ul>
30
+ </template>
31
+ </Column>
32
+ </DataTable>
33
+
34
+ <Panel
35
+ v-if="report.before_content != null || report.after_content != null"
36
+ header="Workflow content"
37
+ :toggleable="true"
38
+ :collapsed="true"
39
+ >
40
+ <Tabs value="before">
41
+ <TabList>
42
+ <Tab value="before">Before</Tab>
43
+ <Tab value="after">After</Tab>
44
+ </TabList>
45
+ <TabPanels>
46
+ <TabPanel value="before">
47
+ <pre class="content-pre">{{ report.before_content ?? "(none)" }}</pre>
48
+ </TabPanel>
49
+ <TabPanel value="after">
50
+ <pre class="content-pre">{{ report.after_content ?? "(none)" }}</pre>
51
+ </TabPanel>
52
+ </TabPanels>
53
+ </Tabs>
54
+ </Panel>
55
+ </div>
56
+ </template>
57
+
58
+ <script setup lang="ts">
59
+ import DataTable from "primevue/datatable";
60
+ import Panel from "primevue/panel";
61
+ import Tab from "primevue/tab";
62
+ import TabList from "primevue/tablist";
63
+ import TabPanel from "primevue/tabpanel";
64
+ import TabPanels from "primevue/tabpanels";
65
+ import Tabs from "primevue/tabs";
66
+ import type { SingleCleanReport, CleanStepResult } from "@galaxy-tool-util/schema";
67
+ import Column from "primevue/column";
68
+ import Tag from "primevue/tag";
69
+ import ToolId from "./ToolId.vue";
70
+
71
+ defineProps<{
72
+ report: SingleCleanReport;
73
+ }>();
74
+ </script>
75
+
76
+ <style scoped>
77
+ .operation-report {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: 0.75rem;
81
+ }
82
+
83
+ .summary-row {
84
+ display: flex;
85
+ gap: 0.5rem;
86
+ flex-wrap: wrap;
87
+ }
88
+
89
+ .key-list {
90
+ margin: 0;
91
+ padding-left: 1.25rem;
92
+ }
93
+
94
+ .skip-reason,
95
+ .no-changes {
96
+ color: var(--p-text-color-secondary, #6c757d);
97
+ font-style: italic;
98
+ }
99
+
100
+ .content-pre {
101
+ margin: 0;
102
+ padding: 0.5rem;
103
+ font-size: 0.75rem;
104
+ background: var(--p-content-background);
105
+ border: 1px solid var(--p-content-border-color);
106
+ border-radius: 4px;
107
+ color: var(--p-text-color);
108
+ overflow: auto;
109
+ max-height: 500px;
110
+ white-space: pre;
111
+ }
112
+ </style>
@@ -0,0 +1,171 @@
1
+ <template>
2
+ <div class="operation-report">
3
+ <div class="summary-row">
4
+ <Tag :value="`${sourceFormatLabel} → ${targetFormatLabel}`" severity="info" />
5
+ <Tag v-if="result.dry_run" value="Dry run" severity="warn" />
6
+ <Tag v-if="removedPath" :value="`Removes ${removedPath}`" severity="danger" />
7
+ <Tag :value="summaryLabel" :severity="allOk ? 'success' : 'warn'" />
8
+ </div>
9
+
10
+ <div class="path-row">
11
+ <div>
12
+ <span class="path-label">Source:</span> <code>{{ result.source_path }}</code>
13
+ </div>
14
+ <div>
15
+ <span class="path-label">Output:</span> <code>{{ result.output_path }}</code>
16
+ </div>
17
+ <div v-if="removedPath">
18
+ <span class="path-label">Removed:</span> <code>{{ removedPath }}</code>
19
+ </div>
20
+ </div>
21
+
22
+ <DataTable v-if="stepRows.length > 0" :value="stepRows" size="small">
23
+ <Column field="step_id" header="Step" />
24
+ <Column field="step_label" header="Label">
25
+ <template #body="{ data }: { data: ExportStepRow }">
26
+ <span v-if="data.step_label">{{ data.step_label }}</span>
27
+ <span v-else class="muted">—</span>
28
+ </template>
29
+ </Column>
30
+ <Column header="Tool">
31
+ <template #body="{ data }: { data: ExportStepRow }">
32
+ <ToolId v-if="data.tool_id" :tool-id="data.tool_id" />
33
+ <span v-else class="muted">—</span>
34
+ </template>
35
+ </Column>
36
+ <Column header="Status">
37
+ <template #body="{ data }: { data: ExportStepRow }">
38
+ <Tag
39
+ :value="data.ok ? 'OK' : (data.error ?? 'Failed')"
40
+ :severity="data.ok ? 'success' : 'danger'"
41
+ />
42
+ </template>
43
+ </Column>
44
+ </DataTable>
45
+
46
+ <Panel
47
+ v-if="result.content != null"
48
+ header="Converted content"
49
+ :toggleable="true"
50
+ :collapsed="true"
51
+ >
52
+ <pre class="content-pre">{{ result.content }}</pre>
53
+ </Panel>
54
+ </div>
55
+ </template>
56
+
57
+ <script setup lang="ts">
58
+ import { computed } from "vue";
59
+ import DataTable from "primevue/datatable";
60
+ import Column from "primevue/column";
61
+ import Panel from "primevue/panel";
62
+ import Tag from "primevue/tag";
63
+ import type {
64
+ ExportResult,
65
+ ConvertResult,
66
+ SingleExportReport,
67
+ ToNativeResult,
68
+ StepEncodeStatus,
69
+ } from "@galaxy-tool-util/schema";
70
+ import ToolId from "./ToolId.vue";
71
+
72
+ interface ExportStepRow {
73
+ step_id: string;
74
+ step_label: string | null;
75
+ tool_id: string | null;
76
+ ok: boolean;
77
+ error: string | null;
78
+ }
79
+
80
+ const props = defineProps<{
81
+ result: ExportResult | ConvertResult;
82
+ }>();
83
+
84
+ const removedPath = computed(() =>
85
+ "removed_path" in props.result ? props.result.removed_path : null,
86
+ );
87
+
88
+ const isToNative = computed(() => props.result.target_format === "native");
89
+
90
+ const sourceFormatLabel = computed(() =>
91
+ props.result.source_format === "native" ? "Native (.ga)" : "Format2 (.gxwf.yml)",
92
+ );
93
+ const targetFormatLabel = computed(() =>
94
+ props.result.target_format === "native" ? "Native (.ga)" : "Format2 (.gxwf.yml)",
95
+ );
96
+
97
+ const stepRows = computed<ExportStepRow[]>(() => {
98
+ if (isToNative.value) {
99
+ const r = props.result.report as ToNativeResult;
100
+ return (r.steps ?? []).map((s: StepEncodeStatus) => ({
101
+ step_id: s.step_id,
102
+ step_label: s.step_label,
103
+ tool_id: s.tool_id,
104
+ ok: s.encoded,
105
+ error: s.error,
106
+ }));
107
+ }
108
+ // Native → format2 report (SingleExportReport) carries counts but no per-step list.
109
+ return [];
110
+ });
111
+
112
+ const allOk = computed(() => {
113
+ if (isToNative.value) {
114
+ return (props.result.report as ToNativeResult).all_encoded;
115
+ }
116
+ return (props.result.report as SingleExportReport).ok;
117
+ });
118
+
119
+ const summaryLabel = computed(() => {
120
+ if (isToNative.value) {
121
+ return (props.result.report as ToNativeResult).summary;
122
+ }
123
+ const r = props.result.report as SingleExportReport;
124
+ const total = r.steps_converted + r.steps_fallback;
125
+ return `${r.steps_converted}/${total} step${total !== 1 ? "s" : ""} converted`;
126
+ });
127
+ </script>
128
+
129
+ <style scoped>
130
+ .operation-report {
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: 0.75rem;
134
+ }
135
+
136
+ .summary-row {
137
+ display: flex;
138
+ gap: 0.5rem;
139
+ flex-wrap: wrap;
140
+ }
141
+
142
+ .path-row {
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: 0.25rem;
146
+ font-size: 0.9rem;
147
+ }
148
+
149
+ .path-label {
150
+ color: var(--p-text-color-secondary, #6c757d);
151
+ margin-right: 0.25rem;
152
+ }
153
+
154
+ .muted {
155
+ color: var(--p-text-color-secondary, #6c757d);
156
+ font-style: italic;
157
+ }
158
+
159
+ .content-pre {
160
+ margin: 0;
161
+ padding: 0.5rem;
162
+ font-size: 0.75rem;
163
+ background: var(--p-content-background);
164
+ border: 1px solid var(--p-content-border-color);
165
+ border-radius: 4px;
166
+ color: var(--p-text-color);
167
+ overflow: auto;
168
+ max-height: 500px;
169
+ white-space: pre;
170
+ }
171
+ </style>
@@ -0,0 +1,90 @@
1
+ <template>
2
+ <div class="operation-report">
3
+ <template v-if="structureErrors.length || encodingErrors.length">
4
+ <Message severity="error" :closable="false">
5
+ <ul class="error-list">
6
+ <li v-for="(e, i) in structureErrors" :key="'s' + i">{{ e }}</li>
7
+ <li v-for="(e, i) in encodingErrors" :key="'e' + i">{{ e }}</li>
8
+ </ul>
9
+ </Message>
10
+ </template>
11
+
12
+ <div class="summary-row">
13
+ <Tag
14
+ :value="`${report.lint_errors} error${report.lint_errors !== 1 ? 's' : ''}`"
15
+ :severity="report.lint_errors > 0 ? 'danger' : 'success'"
16
+ />
17
+ <Tag
18
+ :value="`${report.lint_warnings} warning${report.lint_warnings !== 1 ? 's' : ''}`"
19
+ :severity="report.lint_warnings > 0 ? 'warn' : 'secondary'"
20
+ />
21
+ </div>
22
+
23
+ <DataTable :value="report.results" size="small" class="results-table">
24
+ <Column field="step" header="Step" />
25
+ <Column header="Tool">
26
+ <template #body="{ data }: { data: ValidationStepResult }">
27
+ <ToolId :tool-id="data.tool_id" />
28
+ </template>
29
+ </Column>
30
+ <Column header="Status">
31
+ <template #body="{ data }: { data: ValidationStepResult }">
32
+ <Tag :value="data.status" :severity="statusSeverity(data.status)" />
33
+ </template>
34
+ </Column>
35
+ <Column header="Errors">
36
+ <template #body="{ data }: { data: ValidationStepResult }">
37
+ <ul v-if="data.errors.length" class="error-list">
38
+ <li v-for="(e, i) in data.errors" :key="i">{{ e }}</li>
39
+ </ul>
40
+ </template>
41
+ </Column>
42
+ </DataTable>
43
+ </div>
44
+ </template>
45
+
46
+ <script setup lang="ts">
47
+ import { computed } from "vue";
48
+ import type { SingleLintReport, ValidationStepResult } from "@galaxy-tool-util/schema";
49
+ import DataTable from "primevue/datatable";
50
+ import Column from "primevue/column";
51
+ import Tag from "primevue/tag";
52
+ import Message from "primevue/message";
53
+ import ToolId from "./ToolId.vue";
54
+
55
+ const props = defineProps<{
56
+ report: SingleLintReport;
57
+ }>();
58
+
59
+ const structureErrors = computed(() => props.report.structure_errors ?? []);
60
+ const encodingErrors = computed(() => props.report.encoding_errors ?? []);
61
+
62
+ function statusSeverity(status: ValidationStepResult["status"]) {
63
+ if (status === "ok") return "success";
64
+ if (status === "fail") return "danger";
65
+ return "secondary";
66
+ }
67
+ </script>
68
+
69
+ <style scoped>
70
+ .operation-report {
71
+ display: flex;
72
+ flex-direction: column;
73
+ gap: 0.75rem;
74
+ }
75
+
76
+ .summary-row {
77
+ display: flex;
78
+ gap: 0.5rem;
79
+ flex-wrap: wrap;
80
+ }
81
+
82
+ .results-table {
83
+ margin-top: 0.5rem;
84
+ }
85
+
86
+ .error-list {
87
+ margin: 0;
88
+ padding-left: 1.25rem;
89
+ }
90
+ </style>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <pre class="raw-json"><code>{{ formatted }}</code></pre>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { computed } from "vue";
7
+
8
+ const props = defineProps<{ data: unknown }>();
9
+
10
+ const formatted = computed(() => JSON.stringify(props.data, null, 2));
11
+ </script>
12
+
13
+ <style scoped>
14
+ .raw-json {
15
+ margin: 0;
16
+ padding: 1rem;
17
+ overflow: auto;
18
+ background: var(--p-content-background);
19
+ border: 1px solid var(--p-content-border-color);
20
+ border-radius: 6px;
21
+ font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace;
22
+ font-size: 0.85rem;
23
+ line-height: 1.5;
24
+ color: var(--p-text-color);
25
+ white-space: pre;
26
+ max-height: 70vh;
27
+ }
28
+ </style>
@@ -0,0 +1,147 @@
1
+ <template>
2
+ <div class="gxwf-report-shell">
3
+ <header class="report-header">
4
+ <span class="report-type">{{ typeLabel }}</span>
5
+ <span v-if="workflowPath" class="report-path">{{ workflowPath }}</span>
6
+ </header>
7
+ <ValidationReport
8
+ v-if="report.type === 'validate'"
9
+ :report="report.data as SingleValidationReport"
10
+ />
11
+ <LintReport v-else-if="report.type === 'lint'" :report="report.data as SingleLintReport" />
12
+ <CleanReport v-else-if="report.type === 'clean'" :report="report.data as SingleCleanReport" />
13
+ <RoundtripReport
14
+ v-else-if="report.type === 'roundtrip'"
15
+ :report="report.data as SingleRoundTripReport"
16
+ />
17
+ <ExportReport
18
+ v-else-if="report.type === 'export' || report.type === 'convert'"
19
+ :result="report.data as ExportResult | ConvertResult"
20
+ />
21
+ <TreeValidationReport
22
+ v-else-if="report.type === 'validate-tree'"
23
+ :report="report.data as TreeValidationReportData"
24
+ />
25
+ <TreeLintReport
26
+ v-else-if="report.type === 'lint-tree'"
27
+ :report="report.data as LintTreeReportData"
28
+ />
29
+ <TreeCleanReport
30
+ v-else-if="report.type === 'clean-tree'"
31
+ :report="report.data as TreeCleanReportData"
32
+ />
33
+ <TreeRoundtripReport
34
+ v-else-if="report.type === 'roundtrip-tree'"
35
+ :report="report.data as RoundTripTreeReportData"
36
+ />
37
+ <p v-else class="unknown-type">Unknown report type: {{ report.type }}</p>
38
+ </div>
39
+ </template>
40
+
41
+ <script setup lang="ts">
42
+ import { computed } from "vue";
43
+ import type {
44
+ SingleValidationReport,
45
+ SingleLintReport,
46
+ SingleCleanReport,
47
+ SingleRoundTripReport,
48
+ ExportResult,
49
+ ConvertResult,
50
+ TreeValidationReport as TreeValidationReportData,
51
+ LintTreeReport as LintTreeReportData,
52
+ TreeCleanReport as TreeCleanReportData,
53
+ RoundTripTreeReport as RoundTripTreeReportData,
54
+ } from "@galaxy-tool-util/schema";
55
+ import ValidationReport from "./ValidationReport.vue";
56
+ import LintReport from "./LintReport.vue";
57
+ import CleanReport from "./CleanReport.vue";
58
+ import RoundtripReport from "./RoundtripReport.vue";
59
+ import ExportReport from "./ExportReport.vue";
60
+ import TreeValidationReport from "./TreeValidationReport.vue";
61
+ import TreeLintReport from "./TreeLintReport.vue";
62
+ import TreeCleanReport from "./TreeCleanReport.vue";
63
+ import TreeRoundtripReport from "./TreeRoundtripReport.vue";
64
+
65
+ interface ReportPayload {
66
+ type:
67
+ | "validate"
68
+ | "lint"
69
+ | "clean"
70
+ | "roundtrip"
71
+ | "export"
72
+ | "convert"
73
+ | "validate-tree"
74
+ | "lint-tree"
75
+ | "clean-tree"
76
+ | "roundtrip-tree";
77
+ data: unknown;
78
+ }
79
+
80
+ const props = defineProps<{ report: ReportPayload }>();
81
+
82
+ const typeLabel = computed(() => {
83
+ const labels: Record<ReportPayload["type"], string> = {
84
+ validate: "Validate",
85
+ lint: "Lint",
86
+ clean: "Clean",
87
+ roundtrip: "Roundtrip",
88
+ export: "Export",
89
+ convert: "Convert",
90
+ "validate-tree": "Validate Tree",
91
+ "lint-tree": "Lint Tree",
92
+ "clean-tree": "Clean Tree",
93
+ "roundtrip-tree": "Roundtrip Tree",
94
+ };
95
+ return labels[props.report.type] ?? props.report.type;
96
+ });
97
+
98
+ const workflowPath = computed(() => {
99
+ const d = props.report.data as Record<string, unknown> | null;
100
+ if (typeof d?.workflow === "string") return d.workflow;
101
+ if (typeof d?.source_path === "string") return d.source_path;
102
+ if (typeof d?.root === "string") return d.root;
103
+ return null;
104
+ });
105
+ </script>
106
+
107
+ <style scoped>
108
+ .gxwf-report-shell {
109
+ font-family:
110
+ system-ui,
111
+ -apple-system,
112
+ sans-serif;
113
+ padding: 1.5rem;
114
+ max-width: 1200px;
115
+ margin: 0 auto;
116
+ }
117
+
118
+ .report-header {
119
+ display: flex;
120
+ align-items: baseline;
121
+ gap: 0.75rem;
122
+ margin-bottom: 1.25rem;
123
+ padding-bottom: 0.75rem;
124
+ border-bottom: 1px solid var(--p-content-border-color, #dee2e6);
125
+ }
126
+
127
+ .report-type {
128
+ font-size: 1.25rem;
129
+ font-weight: 600;
130
+ text-transform: capitalize;
131
+ color: var(--p-text-color, #212529);
132
+ }
133
+
134
+ .report-path {
135
+ font-family: monospace;
136
+ font-size: 0.9rem;
137
+ color: var(--p-text-color-secondary, #6c757d);
138
+ overflow: hidden;
139
+ text-overflow: ellipsis;
140
+ white-space: nowrap;
141
+ }
142
+
143
+ .unknown-type {
144
+ color: var(--p-text-color-secondary, #6c757d);
145
+ font-style: italic;
146
+ }
147
+ </style>