Skip to content

Commit a8a7904

Browse files
committed
feat(master-recipe): stabilize import/export flow and improve dialog UX
- improve `MasterAddDialog` with deterministic progress (counter + percent), disabled controls during processing, and automatic initial MTP file load - update progress styling in `AddDialogContainer` (real width transition instead of looping animation, plus progress meta display) - adjust workspace/sidebar defaults: initialize chart elements as Previous/Next Operation Indicator and hide currently unsupported element types - refine master workspace shape styling and remove redundant recipe-node SCSS definitions from `main.scss`
1 parent 7ff9aa5 commit a8a7904

File tree

15 files changed

+395
-239
lines changed

15 files changed

+395
-239
lines changed

client/src/features/general-recipe/ui/sidebar/GeneralElementWindow.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ const initialPackages = computed(() => {
7272
case 'ChartElements':
7373
return [
7474
{ type: 'chart_element', name: 'Previous Operation Indicator', procedureChartElementType: 'Previous Operation Indicator' },
75-
{ type: 'chart_element', name: 'Next Operation Indicator', procedureChartElementType: 'Next Operation Indicator' },
75+
{ type: 'chart_element', name: 'Next Operation Indicator', procedureChartElementType: 'Next Operation Indicator' }/*,
7676
{ type: 'chart_element', name: 'Start Parallel Indicator', procedureChartElementType: 'Start Parallel Indicator' },
77-
{ type: 'chart_element', name: 'End Parallel Indicator', procedureChartElementType: 'End Parallel Indicator' }/*,
77+
{ type: 'chart_element', name: 'End Parallel Indicator', procedureChartElementType: 'End Parallel Indicator' },
7878
{ type: 'chart_element', name: 'Start Optional Parallel Indicator', procedureChartElementType: 'Start Optional Parallel Indicator' },
7979
{ type: 'chart_element', name: 'End Optional Parallel Indicator', procedureChartElementType: 'End Optional Parallel Indicator' },
8080
{ type: 'chart_element', name: 'Annotation', procedureChartElementType: 'Annotation' },

client/src/features/master-recipe/ui/sidebar/MasterAddDialog.vue

Lines changed: 104 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
<template>
22
<AddDialogContainer :elementType="element_type" @close="$emit('close')">
33
<form class="dialog-form">
4-
<span>Select MTP/AAS: </span>
4+
<span>Select MTP/AAS:</span>
55
<div class="row-flex">
66
<select v-model="current_file_type" name="FileType" id="fileTypeSelect"
7-
@change="readServerFiles(current_file_type)">
8-
<option value="">Select file type</option>
7+
:disabled="isProcessingFile" @change="readServerFiles(current_file_type)">
8+
<!--<option value="">Select file type</option>-->
99
<option value="mtp">MTP Files</option>
1010
<option value="aas">AAS Files</option>
1111
</select>
12-
<button type="button" class="icon-btn" @click="readServerFiles(current_file_type)" title="Reload files">
12+
<button type="button" class="icon-btn" @click="readServerFiles(current_file_type)" title="Reload files"
13+
:disabled="isProcessingFile || isLoadingFiles">
1314
<span class="reload">&#x21bb;</span>
1415
</button>
1516
</div>
1617
<div v-if="current_file_type && serverFiles.length > 0">
1718
<label for="file_select">Select File: </label>
18-
<select id="file_select" v-model="current_file_name" name="file">
19+
<select id="file_select" v-model="current_file_name" name="file" :disabled="isProcessingFile">
1920
<option v-for="item in serverFiles" :value="item" :key="item">{{ item }}</option>
2021
</select>
2122
<div class="row-flex">
@@ -26,20 +27,26 @@
2627
</button>
2728
</div>
2829
<div v-if="isProcessingFile" class="processing-progress">
29-
<div class="progress-bar">
30-
<div class="progress-fill"></div>
30+
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100"
31+
:aria-valuenow="progressPercent">
32+
<div class="progress-fill" :style="{ width: `${progressPercent}%` }"></div>
3133
</div>
3234
<p>{{ processingProgress }}</p>
35+
<div class="progress-meta">
36+
<span>{{ progressCounterText }}</span>
37+
<span>{{ progressPercent }}%</span>
38+
</div>
3339
</div>
3440
</div>
3541
<div v-if="current_file_type" class="uploader-section">
3642
<span>Upload new {{ current_file_type.toUpperCase() }} file:</span>
3743
<div>
3844
<input ref="fileInput" @change="onFileChange" type="file"
39-
:accept="getFileAcceptTypes(current_file_type)" style="width:100%;" />
45+
:accept="getFileAcceptTypes(current_file_type)" style="width:100%;"
46+
:disabled="isProcessingFile || isLoadingFiles" />
4047
<div class="row-flex" style="margin-top: 8px;">
4148
<button class="button" type="button" @click="uploadFileToServer(current_file_type)"
42-
:disabled="isLoadingFiles">
49+
:disabled="isLoadingFiles || isProcessingFile">
4350
<span v-if="isLoadingFiles">Uploading...</span>
4451
<span v-else>Upload {{ current_file_type.toUpperCase() }} to Server</span>
4552
</button>
@@ -51,7 +58,7 @@
5158
</template>
5259

5360
<script setup>
54-
import { ref, toRefs } from 'vue';
61+
import { computed, onMounted, ref, toRefs } from 'vue';
5562
import axios from 'axios';
5663
import AddDialogContainer from '@/shell/ui/sidebar/AddDialogContainer.vue';
5764
@@ -62,25 +69,71 @@ const { element_type } = toRefs(props);
6269
6370
const emit = defineEmits(['close', 'add']);
6471
65-
const current_file_type = ref('');
72+
const current_file_type = ref('mtp');
6673
const current_file_name = ref('');
6774
const serverFiles = ref([]);
6875
const fileInput = ref(null);
6976
7077
const isLoadingFiles = ref(false);
7178
const isProcessingFile = ref(false);
7279
const processingProgress = ref('');
80+
const progressTotalProcedures = ref(0);
81+
const progressLoadedProcedures = ref(0);
82+
const progressParseDone = ref(false);
7383
7484
const client = axios.create({
7585
baseURL: ''
7686
});
7787
88+
const progressPercent = computed(() => {
89+
const totalUnits = progressTotalProcedures.value + 1;
90+
const doneUnits = (progressParseDone.value ? 1 : 0) + progressLoadedProcedures.value;
91+
const rawPercent = Math.round((doneUnits / totalUnits) * 100);
92+
if (!Number.isFinite(rawPercent)) return 0;
93+
return Math.max(0, Math.min(100, rawPercent));
94+
});
95+
96+
const progressCounterText = computed(
97+
() => `${progressLoadedProcedures.value}/${progressTotalProcedures.value}`
98+
);
99+
100+
onMounted(() => {
101+
readServerFiles(current_file_type.value);
102+
});
103+
78104
function getFileNameWithoutExtension(name) {
79105
if (!name || typeof name !== 'string') return 'Imported Elements';
80106
const normalized = name.replace(/\\/g, '/').split('/').pop() || '';
81107
return normalized.replace(/\.[^/.]+$/, '') || normalized || 'Imported Elements';
82108
}
83109
110+
function resetProgressState() {
111+
progressTotalProcedures.value = 0;
112+
progressLoadedProcedures.value = 0;
113+
progressParseDone.value = false;
114+
processingProgress.value = '';
115+
}
116+
117+
function initializeProgress(totalProcedures) {
118+
progressTotalProcedures.value = Math.max(0, Number(totalProcedures) || 0);
119+
progressLoadedProcedures.value = 0;
120+
progressParseDone.value = false;
121+
}
122+
123+
function markParseDone() {
124+
progressParseDone.value = true;
125+
}
126+
127+
function markProcedureLoaded() {
128+
if (progressLoadedProcedures.value < progressTotalProcedures.value) {
129+
progressLoadedProcedures.value += 1;
130+
}
131+
}
132+
133+
function setProgressMessage(message) {
134+
processingProgress.value = message || '';
135+
}
136+
84137
async function readServerFiles(fileType) {
85138
if (!fileType) {
86139
serverFiles.value = [];
@@ -146,20 +199,25 @@ async function addElementsFromFile(fileType, fileName) {
146199
}
147200
148201
isProcessingFile.value = true;
149-
processingProgress.value = 'Parsing file...';
202+
initializeProgress(0);
203+
setProgressMessage('Parsing file...');
150204
151205
try {
152-
processingProgress.value = 'Fetching process data...';
206+
setProgressMessage('Fetching process data...');
153207
const response = await client.get(`/${fileType}/${encodeURIComponent(fileName)}/parse`);
154208
console.log(`Parsed ${fileType} result:`, response.data);
155209
156210
if (fileType === 'mtp' && Array.isArray(response.data.procs)) {
157-
const mtpProcesses = [];
158-
159-
processingProgress.value = 'Processing processes and equipment data...';
211+
const procedures = response.data.procs;
212+
initializeProgress(procedures.length);
213+
markParseDone();
214+
if (procedures.length > 0) {
215+
setProgressMessage(`Loading procedures ${progressCounterText.value}...`);
216+
} else {
217+
setProgressMessage('No procedures found.');
218+
}
160219
161-
const equipmentPromises = response.data.procs.map(async (proc, index) => {
162-
processingProgress.value = `Fetching equipment data for ${proc.name} (${index + 1}/${response.data.procs.length})...`;
220+
const equipmentPromises = procedures.map(async (proc) => {
163221
try {
164222
const equipmentResponse = await client.get(`/${fileType}/${encodeURIComponent(fileName)}/master-recipe-equipment/${encodeURIComponent(proc.name)}`);
165223
return {
@@ -172,14 +230,16 @@ async function addElementsFromFile(fileType, fileName) {
172230
proc,
173231
equipmentData: null
174232
};
233+
} finally {
234+
markProcedureLoaded();
235+
setProgressMessage(`Loading procedures ${progressCounterText.value}...`);
175236
}
176237
});
177238
178239
const results = await Promise.all(equipmentPromises);
179240
180-
processingProgress.value = 'Building process objects...';
181-
results.forEach(({ proc, equipmentData }) => {
182-
mtpProcesses.push({
241+
setProgressMessage('Building process objects...');
242+
const mtpProcesses = results.map(({ proc, equipmentData }) => ({
183243
name: proc.name,
184244
type: 'procedure',
185245
processElementType: 'MTP Operation',
@@ -196,10 +256,9 @@ async function addElementsFromFile(fileType, fileName) {
196256
dataType: p.paramElem?.Type,
197257
})),
198258
equipmentInfo: equipmentData
199-
});
200-
});
259+
}));
201260
202-
processingProgress.value = 'Adding processes to sidebar...';
261+
setProgressMessage('Adding processes to sidebar...');
203262
console.log('Adding parsed MTP processes:', mtpProcesses);
204263
emit('add', {
205264
title: getFileNameWithoutExtension(fileName),
@@ -208,20 +267,33 @@ async function addElementsFromFile(fileType, fileName) {
208267
}
209268
210269
if (fileType === 'aas' && Array.isArray(response.data)) {
211-
processingProgress.value = 'Processing AAS capabilities...';
270+
const importableCapabilities = response.data
271+
.filter(item => item.realized_by && item.realized_by.length > 0);
272+
273+
initializeProgress(importableCapabilities.length);
274+
setProgressMessage('Fetching equipment info...');
212275
const equipmentResponse = await client.get(`/${fileType}/${encodeURIComponent(fileName)}/equipment-info`);
213276
console.log(`Equipment info for ${fileType}:`, equipmentResponse.data);
214277
215-
const parsedProcesses = response.data
216-
.filter(item => item.realized_by && item.realized_by.length > 0)
217-
.map(item => ({
278+
markParseDone();
279+
if (importableCapabilities.length > 0) {
280+
setProgressMessage(`Loading procedures ${progressCounterText.value}...`);
281+
} else {
282+
setProgressMessage('No procedures found.');
283+
}
284+
285+
const parsedProcesses = importableCapabilities.map(item => {
286+
markProcedureLoaded();
287+
setProgressMessage(`Loading procedures ${progressCounterText.value}...`);
288+
return {
218289
name: item.capability[0].capability_name,
219290
type: 'procedure',
220291
processElementType: 'AAS Capability',
221292
equipmentInfo: equipmentResponse.data
222-
}));
293+
};
294+
});
223295
224-
processingProgress.value = 'Adding processes to sidebar...';
296+
setProgressMessage('Adding processes to sidebar...');
225297
console.log('Adding parsed AAS processes:', parsedProcesses);
226298
emit('add', {
227299
title: getFileNameWithoutExtension(fileName),
@@ -231,15 +303,10 @@ async function addElementsFromFile(fileType, fileName) {
231303
232304
} catch (error) {
233305
console.error(`Error parsing ${fileType} file:`, error);
234-
processingProgress.value = `Error: ${error.response?.data?.message || error.message || 'Unknown error occurred'}`;
235-
setTimeout(() => {
236-
if (isProcessingFile.value) {
237-
processingProgress.value = '';
238-
}
239-
}, 3000);
306+
setProgressMessage(`Error: ${error.response?.data?.message || error.message || 'Unknown error occurred'}`);
240307
} finally {
241308
isProcessingFile.value = false;
242-
processingProgress.value = '';
309+
resetProgressState();
243310
}
244311
}
245312

client/src/features/master-recipe/ui/sidebar/MasterElementWindow.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ const initialPackages = computed(() => {
5757
return [
5858
{ name: 'Begin', type: 'recipe_element', recipeElementType: 'Begin' },
5959
{ name: 'End', type: 'recipe_element', recipeElementType: 'End' },
60-
{ name: 'Allocation', type: 'recipe_element', recipeElementType: 'Allocation' },
61-
{ name: 'Condition', type: 'recipe_element', recipeElementType: 'Condition' },
60+
/*{ name: 'Allocation', type: 'recipe_element', recipeElementType: 'Allocation' },*/
61+
{ name: 'Condition', type: 'recipe_element', recipeElementType: 'Condition' }/*,
6262
{ name: 'Begin and end Sequence Selection', type: 'recipe_element', recipeElementType: 'Begin and end Sequence Selection' },
6363
{ name: 'Begin and end Simultaneous Sequence', type: 'recipe_element', recipeElementType: 'Begin and end Simultaneous Sequence' },
6464
{ name: 'Synchronization Point', type: 'recipe_element', recipeElementType: 'Synchronization Point' },
6565
{ name: 'Synchronization Line', type: 'recipe_element', recipeElementType: 'Synchronization Line' },
66-
{ name: 'Synchronization Line indicating material transfer', type: 'recipe_element', recipeElementType: 'Synchronization Line indicating material transfer' }
66+
{ name: 'Synchronization Line indicating material transfer', type: 'recipe_element', recipeElementType: 'Synchronization Line indicating material transfer' }*/
6767
];
6868
case 'Procedures':
6969
return [];

0 commit comments

Comments
 (0)