Skip to content

Commit

Permalink
Merge pull request #95 from LCOGT/feature/error-reporting
Browse files Browse the repository at this point in the history
Global Alerts, Handling of failed operations, Wizard Styling changes
  • Loading branch information
LTDakin authored Jul 9, 2024
2 parents cb7bff9 + ec48bee commit 3546cbd
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 117 deletions.
2 changes: 2 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { onMounted, computed, watch } from 'vue'
import { useUserDataStore } from '@/stores/userData'
import { useConfigurationStore } from './stores/configuration'
import NavBar from './components/Global/NavBar.vue'
import AlertToast from './components/Global/AlertToast.vue'
const configurationStore = useConfigurationStore()
const userDataStore = useUserDataStore()
Expand Down Expand Up @@ -39,6 +40,7 @@ watch(() => userDataStore.isColorblindMode, (newVal) => {
<template v-if="loadedConfig">
<template v-if="userDataStore.userIsAuthenticated">
<nav-bar />
<alert-toast />
</template>
<router-view />
</template>
Expand Down
56 changes: 52 additions & 4 deletions src/components/DataSession/DataSession.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { fetchApiCall, handleError } from '../../utils/api'
import { calculateColumnSpan } from '@/utils/common'
import { useConfigurationStore } from '@/stores/configuration'
import ImageGrid from '../Global/ImageGrid'
import OperationWizard from './OperationWizard.vue'
const props = defineProps({
data: {
Expand All @@ -22,6 +23,7 @@ const store = useConfigurationStore()
const emit = defineEmits(['reloadSession'])
const images = ref([...props.data.input_data])
const filteredImages = ref([...images.value])
const showWizardDialog = ref(false)
const dataSessionsUrl = store.datalabApiBaseUrl + 'datasessions/'
const imagesPerRow = 4
Expand Down Expand Up @@ -76,6 +78,11 @@ function selectOperation(operationIndex) {
}
}
function deleteOperation(operationID) {
const url = dataSessionsUrl + props.data.id + '/operations/' + operationID + '/'
fetchApiCall({ url: url, method: 'DELETE', successCallback: () => {emit('reloadSession')}, failCallback: handleError })
}
async function addOperation(operationDefinition) {
const url = dataSessionsUrl + props.data.id + '/operations/'
if ('input_files' in operationDefinition.input_data) {
Expand Down Expand Up @@ -109,12 +116,43 @@ onMounted(() => {

<template>
<v-container class="d-lg-flex ds-container">
<image-grid :images="filteredImages" :column-span="calculateColumnSpan(filteredImages.length, imagesPerRow)" />
<v-col cols="3" justify="center" align="center">
<v-col
cols="3"
justify="center"
align="center"
>
<!-- The operations bar list goes here -->
<operation-pipeline :images="images" :session_id="data.id" :operations="data.operations" :active="props.active"
@add-operation="addOperation" @operation-completed="addCompletedOperation" @select-operation="selectOperation" />
<operation-pipeline
:session_id="data.id"
:operations="data.operations"
:active="props.active"
@operation-completed="addCompletedOperation"
@select-operation="selectOperation"
@delete-operation="deleteOperation"
/>
<v-btn
variant="flat"
class="addop_button"
>
Add Operation
<v-dialog
v-model="showWizardDialog"
activator="parent"
fullscreen
transition="dialog-bottom-transition"
>
<operation-wizard
:images="images"
@close-wizard="showWizardDialog = false"
@add-operation="addOperation"
/>
</v-dialog>
</v-btn>
</v-col>
<image-grid
:images="filteredImages"
:column-span="calculateColumnSpan(filteredImages.length, imagesPerRow)"
/>
</v-container>
</template>

Expand All @@ -123,4 +161,14 @@ onMounted(() => {
background-color: var(--metal);
display: flex;
}
.addop_button {
width: 16rem;
height: 4rem;
font-size: 1.3rem;
align-content: center;
background-color: var(--light-blue);
font-weight: 700;
color: white;
margin-top: 1rem;
}
</style>
89 changes: 24 additions & 65 deletions src/components/DataSession/OperationPipeline.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup>
import { ref, defineEmits, defineProps, watch, onBeforeUnmount } from 'vue'
import { ref, watch, onBeforeUnmount } from 'vue'
import { useConfigurationStore } from '@/stores/configuration'
import { useAlertsStore } from '@/stores/alerts'
import { fetchApiCall, handleError } from '@/utils/api'
import OperationWizard from './OperationWizard.vue'
const store = useConfigurationStore()
const emit = defineEmits(['addOperation', 'operationCompleted', 'selectOperation'])
const alertStore = useAlertsStore()
const emit = defineEmits(['operationCompleted', 'selectOperation', 'deleteOperation'])
const props = defineProps({
operations: {
Expand All @@ -16,18 +17,13 @@ const props = defineProps({
type: Number,
required: true
},
images: {
type: Array,
required: true
},
active: {
type: Boolean,
required: true
}
})
const selectedOperation = ref(-1)
const showWizardDialog = ref(false)
const operationPollingTimers = {}
const operationPercentages = ref({})
const POLL_WAIT_TIME = 2000
Expand All @@ -44,18 +40,8 @@ function selectOperation(index) {
emit('selectOperation', selectedOperation.value)
}
function operationBtnColor(index) {
if (index == selectedOperation.value) {
return 'selected'
}
else {
return 'not-selected'
}
}
async function pollOperationCompletion(operationID) {
const url = store.datalabApiBaseUrl + 'datasessions/' + props.session_id + '/operations/' + operationID + '/'
// Success Callback for checking operation status
const updateOperationStatus = (response) => {
if(response){
const operationStatus = response.status
Expand All @@ -70,10 +56,13 @@ async function pollOperationCompletion(operationID) {
case 'COMPLETED':
operationPercentages.value[operationID] = COMPLETE_PERCENT
emit('operationCompleted', response)
if (operationID in operationPollingTimers) {
clearInterval(operationPollingTimers[operationID])
setTimeout(clearPollingData(operationID), POLL_WAIT_TIME)
}
clearPolling(operationID)
break
case 'FAILED':
alertStore.setAlert('error', response.message ? response.message : 'Failed', 'Operation Error:')
operationPercentages.value[operationID] = COMPLETE_PERCENT
emit('deleteOperation', operationID)
clearPolling(operationID)
break
default:
console.error('Unknown Operation Status:', operationStatus)
Expand All @@ -84,12 +73,16 @@ async function pollOperationCompletion(operationID) {
}
}
const url = store.datalabApiBaseUrl + 'datasessions/' + props.session_id + '/operations/' + operationID + '/'
await fetchApiCall({ url: url, method: 'GET', successCallback: updateOperationStatus, failCallback: handleError })
}
function clearPollingData(operationID) {
delete operationPercentages.value[operationID]
delete operationPollingTimers[operationID]
function clearPolling(operationID) {
if (operationID in operationPollingTimers){
clearInterval(operationPollingTimers[operationID])
delete operationPercentages.value[operationID]
delete operationPollingTimers[operationID]
}
}
watch(() => props.operations, () => {
Expand All @@ -113,8 +106,7 @@ watch(
}
else {
Object.keys(operationPollingTimers).forEach(operationID => {
clearInterval(operationPollingTimers[operationID])
clearPollingData(operationID)
clearPolling(operationID)
})
}
}, { immediate: true }
Expand All @@ -123,8 +115,7 @@ watch(
onBeforeUnmount(() => {
// Clean up Polling Intervals
Object.keys(operationPollingTimers).forEach(operationID => {
clearInterval(operationPollingTimers[operationID])
clearPollingData(operationID)
clearPolling(operationID)
})
})
Expand All @@ -141,7 +132,7 @@ onBeforeUnmount(() => {
class="operation mb-2"
>
<v-btn
:class="operationBtnColor(index)"
:class="{selected: index == selectedOperation}"
variant="outlined"
class="operation_button"
@click="selectOperation(index)"
Expand All @@ -155,24 +146,6 @@ onBeforeUnmount(() => {
:height="5"
/>
</v-row>
<v-btn
variant="flat"
class="addop_button"
>
Add Operation
<v-dialog
v-model="showWizardDialog"
activator="parent"
fullscreen
transition="dialog-bottom-transition"
>
<operation-wizard
:images="images"
@close-wizard="showWizardDialog = false"
@add-operation="emit('addOperation', $event); showWizardDialog = false;"
/>
</v-dialog>
</v-btn>
</template>

<style scoped>
Expand All @@ -186,34 +159,20 @@ onBeforeUnmount(() => {
margin-top: 1rem;
}
.addop_button {
width: 16rem;
height: 4rem;
font-size: 1.3rem;
align-content: center;
background-color: var(--light-blue);
font-weight: 700;
color: white;
margin-top: 1rem;
}
.operation_button {
width: 12rem;
height: 3rem;
font-size: 1.2rem;
font-weight: 600;
border-style: none;
background-color: var(--tan);
color: var(--metal);
}
.selected {
background-color: var(--light-blue)
}
.not-selected {
background-color: var(--light-gray);
color: var(--metal);
}
@media (max-width: 1200px) {
.operations {
font-size: 1.3rem;
Expand Down
48 changes: 3 additions & 45 deletions src/components/DataSession/OperationWizard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function goForward() {
operationDefinition.input_data[inputKey] = selected
}
emit('addOperation', operationDefinition)
emit('closeWizard')
}
}
Expand Down Expand Up @@ -189,7 +190,6 @@ function selectImage(inputKey, imageIndex) {
/>
<div
v-else-if="inputDescription.type == 'file'"
class="images-container"
>
<div
v-if="inputDescription.name"
Expand Down Expand Up @@ -276,23 +276,10 @@ function selectImage(inputKey, imageIndex) {
}
.operation-input {
width: 10vw;
margin-left: 2%;
background-color: var(--metal);
position: fixed;
top: 6%;
margin-top: 2rem;
width: 12rem;
}
.images-container {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
flex-direction: column;
width: 100%;
padding-left: 2rem;
padding-right: 2rem;
margin-top: 6%;
}
.input-images {
font-family: 'Open Sans', sans-serif;
color: var(--tan);
Expand Down Expand Up @@ -325,33 +312,4 @@ function selectImage(inputKey, imageIndex) {
color: var(--light-blue);
font-size: 1.2rem;
}
@media (max-width: 1200px) {
.operation-input {
margin-left: 3%;
}
.images-container {
margin-top: 2%;
}
}
@media (max-width: 900px) {
.selected-operation {
height: 120%;
}
.operation-description {
font-size: 1rem;
}
.operation-input {
margin-left: 4%;
width: 15vw;
}
.images-container {
margin-top: 3%;
}
}
</style>
30 changes: 30 additions & 0 deletions src/components/Global/AlertToast.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup>
import { ref, watch } from 'vue'
import { useAlertsStore } from '../../stores/alerts'
const alertStore = useAlertsStore()
const showAlert = ref(false)
watch(() => alertStore.alertText, () => {showAlert.value = true})
</script>
<template>
<v-fade-transition>
<v-alert
v-model="showAlert"
closable
:type="alertStore.alertType"
:text="alertStore.alertText"
/>
</v-fade-transition>
</template>
<style scoped>
.v-alert {
position: absolute;
bottom: 0;
left: 0;
z-index: 1000;
padding: 10px;
margin: 2rem;
}
</style>
Loading

0 comments on commit 3546cbd

Please sign in to comment.