fix: polish UX error handling across views (FINAL-01)
- AppDetails: replace alert() with dismissible toast, add error feedback for start/stop/restart/uninstall actions - GoalDetail: add error toast for install failures instead of silent catch - Apps: add loading skeleton when WebSocket data hasn't arrived yet - Add appDetails.noLaunchUrl i18n key Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
2d0ac12a6a
commit
6cea156df6
@@ -137,6 +137,16 @@
|
||||
</RouterLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Action error toast -->
|
||||
<Transition name="fade">
|
||||
<div v-if="actionError" class="fixed bottom-20 left-1/2 -translate-x-1/2 z-50 max-w-md w-full px-4" role="alert" aria-live="assertive">
|
||||
<div class="bg-red-500/20 border border-red-500/40 backdrop-blur-sm rounded-lg px-4 py-3 text-red-200 text-sm flex items-center justify-between gap-3">
|
||||
<span>{{ actionError }}</span>
|
||||
<button @click="actionError = ''" class="text-red-300 hover:text-white shrink-0">×</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -159,6 +169,14 @@ const goalId = computed(() => route.params.goalId as string)
|
||||
const goal = computed(() => getGoalById(goalId.value))
|
||||
|
||||
const isInstalling = ref(false)
|
||||
const actionError = ref('')
|
||||
let errorTimer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
function showActionError(msg: string) {
|
||||
actionError.value = msg
|
||||
if (errorTimer) clearTimeout(errorTimer)
|
||||
errorTimer = setTimeout(() => { actionError.value = '' }, 5000)
|
||||
}
|
||||
|
||||
const overallStatus = computed(() => goalStore.getGoalStatus(goalId.value))
|
||||
|
||||
@@ -252,7 +270,7 @@ async function installApp(step: GoalStep) {
|
||||
await appStore.installPackage(step.appId, '', 'latest')
|
||||
goalStore.completeStep(goalId.value, step.id)
|
||||
} catch (err) {
|
||||
if (import.meta.env.DEV) console.error('[GoalDetail] Install failed:', err)
|
||||
showActionError(`Install failed: ${err instanceof Error ? err.message : 'Unknown error'}`)
|
||||
} finally {
|
||||
isInstalling.value = false
|
||||
}
|
||||
@@ -278,3 +296,14 @@ function goBack() {
|
||||
router.push('/dashboard')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user