diff --git a/web/src/api/types/events.ts b/web/src/api/types/events.ts index 5fb174fdd..b5f567856 100644 --- a/web/src/api/types/events.ts +++ b/web/src/api/types/events.ts @@ -152,6 +152,16 @@ export interface EventStreamMessage { error?: string, } +export interface EventStreamMessageWithError extends EventStreamMessage { + error: string; +} + +export function isErrorEventStreamMessage( + msg: EventStreamMessage +): msg is EventStreamMessageWithError { + return msg.error !== undefined; +} + export type OnMessageEventSourceCallback = (msg: T) => void; export function isEventStreamMessage(o: object): o is EventStreamMessage { diff --git a/web/src/components/publishProcess/PublishStep.vue b/web/src/components/publishProcess/PublishStep.vue index c4a61282e..6e56d3c89 100644 --- a/web/src/components/publishProcess/PublishStep.vue +++ b/web/src/components/publishProcess/PublishStep.vue @@ -5,8 +5,12 @@ :name="name" title="This is a step" :icon="icon" - :active-icon="icon" + :active-icon="hasError ? 'warning' : icon" + :active-color="hasError ? 'red' : undefined" :header-nav="true" + :error="hasError" + error-icon="warning" + error-color="red" >
- {{ log }} + + @@ -30,18 +39,21 @@ diff --git a/web/src/components/publishProcess/steps/CreateBundle.vue b/web/src/components/publishProcess/steps/CreateBundle.vue index 93acac429..7e3630794 100644 --- a/web/src/components/publishProcess/steps/CreateBundle.vue +++ b/web/src/components/publishProcess/steps/CreateBundle.vue @@ -7,7 +7,7 @@ icon="compress" summary="Collecting and bundling up the files included in your project, so that they can be uploaded to the server within a bundle." :done="done" - :logs="messages" + :messages="messages" /> @@ -16,6 +16,7 @@ import { onBeforeUnmount, ref } from 'vue'; import PublishStep from 'src/components/publishProcess/PublishStep.vue'; import { useEventStream } from 'src/plugins/eventStream'; +import { EventStreamMessage } from 'src/api/types/events'; defineProps({ name: { type: [String, Number], required: true }, @@ -25,25 +26,29 @@ const emit = defineEmits(['start', 'done']); const $eventStream = useEventStream(); const done = ref(false); -const messages = ref([]); +const messages = ref([]); const startCb = $eventStream.addEventMonitorCallback('publish/createBundle/start', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); emit('start'); }); const logCb = $eventStream.addEventMonitorCallback('publish/createBundle/log', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); }); const successCb = $eventStream.addEventMonitorCallback('publish/createBundle/success', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); done.value = true; emit('done'); }); +const failureCb = $eventStream.addEventMonitorCallback('publish/createBundle/failure', (msg) => { + messages.value.push(msg); +}); onBeforeUnmount(() => { $eventStream.delEventFilterCallback(startCb); $eventStream.delEventFilterCallback(logCb); $eventStream.delEventFilterCallback(successCb); + $eventStream.delEventFilterCallback(failureCb); }); diff --git a/web/src/components/publishProcess/steps/CreateDeployment.vue b/web/src/components/publishProcess/steps/CreateDeployment.vue index 6fd3ecc8c..a0cb00658 100644 --- a/web/src/components/publishProcess/steps/CreateDeployment.vue +++ b/web/src/components/publishProcess/steps/CreateDeployment.vue @@ -7,7 +7,7 @@ icon="create_new_folder" summary="Registering the deployment object with the Posit Connect Server." :done="done" - :logs="messages" + :messages="messages" /> @@ -16,6 +16,7 @@ import { onBeforeUnmount, ref } from 'vue'; import PublishStep from 'src/components/publishProcess/PublishStep.vue'; import { useEventStream } from 'src/plugins/eventStream'; +import { EventStreamMessage } from 'src/api/types/events'; defineProps({ name: { type: [String, Number], required: true }, @@ -25,20 +26,24 @@ const emit = defineEmits(['start', 'done']); const $eventStream = useEventStream(); const done = ref(false); -const messages = ref([]); +const messages = ref([]); const startCb = $eventStream.addEventMonitorCallback('publish/createDeployment/start', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); emit('start'); }); const successCb = $eventStream.addEventMonitorCallback('publish/createDeployment/success', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); done.value = true; emit('done'); }); +const errorCb = $eventStream.addEventMonitorCallback('publish/createDeployment/failure', (msg) => { + messages.value.push(msg); +}); onBeforeUnmount(() => { $eventStream.delEventFilterCallback(startCb); $eventStream.delEventFilterCallback(successCb); + $eventStream.delEventFilterCallback(errorCb); }); diff --git a/web/src/components/publishProcess/steps/DeployBundle.vue b/web/src/components/publishProcess/steps/DeployBundle.vue index ad13e818e..72c946346 100644 --- a/web/src/components/publishProcess/steps/DeployBundle.vue +++ b/web/src/components/publishProcess/steps/DeployBundle.vue @@ -7,7 +7,7 @@ icon="publish" summary="Associating the uploaded bundle with the deployment object." :done="done" - :logs="messages" + :messages="messages" /> @@ -16,6 +16,7 @@ import { onBeforeUnmount, ref } from 'vue'; import PublishStep from 'src/components/publishProcess/PublishStep.vue'; import { useEventStream } from 'src/plugins/eventStream'; +import { EventStreamMessage } from 'src/api/types/events'; defineProps({ name: { type: [String, Number], required: true }, @@ -25,21 +26,25 @@ const emit = defineEmits(['start', 'done']); const $eventStream = useEventStream(); const done = ref(false); -const messages = ref([]); +const messages = ref([]); const startCb = $eventStream.addEventMonitorCallback('publish/deployBundle/start', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); emit('start'); }); const successCb = $eventStream.addEventMonitorCallback('publish/deployBundle/success', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); done.value = true; emit('done'); }); +const failureCb = $eventStream.addEventMonitorCallback('publish/deployBundle/failure', (msg) => { + messages.value.push(msg); +}); onBeforeUnmount(() => { $eventStream.delEventFilterCallback(startCb); $eventStream.delEventFilterCallback(successCb); + $eventStream.delEventFilterCallback(failureCb); }); diff --git a/web/src/components/publishProcess/steps/RestorePythonEnvironment.vue b/web/src/components/publishProcess/steps/RestorePythonEnvironment.vue index d8d9c3345..9f14b0841 100644 --- a/web/src/components/publishProcess/steps/RestorePythonEnvironment.vue +++ b/web/src/components/publishProcess/steps/RestorePythonEnvironment.vue @@ -7,7 +7,7 @@ icon="move_down" summary="Installing the dependent python packages on the server in order to reproduce your runtime environment." :done="done" - :logs="messages" + :messages="messages" /> @@ -16,6 +16,7 @@ import { onBeforeUnmount, ref } from 'vue'; import PublishStep from 'src/components/publishProcess/PublishStep.vue'; import { useEventStream } from 'src/plugins/eventStream'; +import { EventStreamMessage } from 'src/api/types/events'; defineProps({ name: { type: [String, Number], required: true }, @@ -25,29 +26,33 @@ const emit = defineEmits(['start', 'done']); const $eventStream = useEventStream(); const done = ref(false); -const messages = ref([]); +const messages = ref([]); const startCb = $eventStream.addEventMonitorCallback('publish/restorePythonEnv/start', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); emit('start'); }); const logCb = $eventStream.addEventMonitorCallback('publish/restorePythonEnv/log', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); }); const progressCb = $eventStream.addEventMonitorCallback('publish/restorePythonEnv/progress', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); }); const successCb = $eventStream.addEventMonitorCallback('publish/restorePythonEnv/success', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); done.value = true; emit('start'); }); +const failureCb = $eventStream.addEventMonitorCallback('publish/restorePythonEnv/failure', (msg) => { + messages.value.push(msg); +}); onBeforeUnmount(() => { $eventStream.delEventFilterCallback(startCb); $eventStream.delEventFilterCallback(logCb); $eventStream.delEventFilterCallback(progressCb); $eventStream.delEventFilterCallback(successCb); + $eventStream.delEventFilterCallback(failureCb); }); diff --git a/web/src/components/publishProcess/steps/RunContent.vue b/web/src/components/publishProcess/steps/RunContent.vue index 02952634f..242d4fad7 100644 --- a/web/src/components/publishProcess/steps/RunContent.vue +++ b/web/src/components/publishProcess/steps/RunContent.vue @@ -7,7 +7,7 @@ icon="sync" summary="Performing execution checks ahead of applying settings." :done="done" - :logs="messages" + :messages="messages" /> @@ -16,6 +16,7 @@ import { onBeforeUnmount, ref } from 'vue'; import PublishStep from 'src/components/publishProcess/PublishStep.vue'; import { useEventStream } from 'src/plugins/eventStream'; +import { EventStreamMessage } from 'src/api/types/events'; defineProps({ name: { type: [String, Number], required: true }, @@ -25,25 +26,29 @@ const emit = defineEmits(['start', 'done']); const $eventStream = useEventStream(); const done = ref(false); -const messages = ref([]); +const messages = ref([]); const startCb = $eventStream.addEventMonitorCallback('publish/runContent/start', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); emit('start'); }); const logCb = $eventStream.addEventMonitorCallback('publish/runContent/log', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); }); const successCb = $eventStream.addEventMonitorCallback('publish/runContent/success', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); done.value = true; emit('done'); }); +const failureCb = $eventStream.addEventMonitorCallback('publish/runContent/failure', (msg) => { + messages.value.push(msg); +}); onBeforeUnmount(() => { $eventStream.delEventFilterCallback(startCb); $eventStream.delEventFilterCallback(logCb); $eventStream.delEventFilterCallback(successCb); + $eventStream.delEventFilterCallback(failureCb); }); diff --git a/web/src/components/publishProcess/steps/UploadBundle.vue b/web/src/components/publishProcess/steps/UploadBundle.vue index 4c58df4c9..4ce7d032e 100644 --- a/web/src/components/publishProcess/steps/UploadBundle.vue +++ b/web/src/components/publishProcess/steps/UploadBundle.vue @@ -7,7 +7,7 @@ icon="login" summary="Transferring the files from your local workstation to the server." :done="done" - :logs="messages" + :messages="messages" /> @@ -16,6 +16,7 @@ import { onBeforeUnmount, ref } from 'vue'; import PublishStep from 'src/components/publishProcess/PublishStep.vue'; import { useEventStream } from 'src/plugins/eventStream'; +import { EventStreamMessage } from 'src/api/types/events'; defineProps({ name: { type: [String, Number], required: true }, @@ -25,21 +26,25 @@ const emit = defineEmits(['start', 'done']); const $eventStream = useEventStream(); const done = ref(false); -const messages = ref([]); +const messages = ref([]); const startCb = $eventStream.addEventMonitorCallback('publish/uploadBundle/start', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); emit('start'); }); const successCb = $eventStream.addEventMonitorCallback('publish/uploadBundle/success', (msg) => { - messages.value.push(msg.data.message); + messages.value.push(msg); done.value = true; emit('done'); }); +const failureCb = $eventStream.addEventMonitorCallback('publish/uploadBundle/failure', (msg) => { + messages.value.push(msg); +}); onBeforeUnmount(() => { $eventStream.delEventFilterCallback(startCb); $eventStream.delEventFilterCallback(successCb); + $eventStream.delEventFilterCallback(failureCb); }); diff --git a/web/src/stores/color.ts b/web/src/stores/color.ts index 284b850af..fa0dfe370 100644 --- a/web/src/stores/color.ts +++ b/web/src/stores/color.ts @@ -18,6 +18,7 @@ export const useColorStore = defineStore('color', () => { textInput: { active: 'grey-1', }, + textError: 'red', outline: 'grey-6', icon: { fill: 'grey-4', @@ -90,6 +91,7 @@ export const useColorStore = defineStore('color', () => { textInput: { active: 'grey-10', }, + textError: 'red', outline: 'grey-10', icon: { fill: 'grey-8',