-
Notifications
You must be signed in to change notification settings - Fork 164
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Desktop] Startup maintenance screen (#2253)
Co-authored-by: github-actions <[email protected]>
- Loading branch information
1 parent
8257e84
commit 0b69d3c
Showing
20 changed files
with
1,176 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<!-- | ||
A refresh button that disables and shows a progress spinner whilst active. | ||
|
||
Usage: | ||
```vue | ||
<RefreshButton | ||
v-model="isRefreshing" | ||
:outlined="false" | ||
@refresh="refresh" | ||
/> | ||
``` | ||
--> | ||
<template> | ||
<Button | ||
class="relative p-button-icon-only" | ||
:outlined="props.outlined" | ||
:severity="props.severity" | ||
:disabled="active || props.disabled" | ||
@click="(event) => $emit('refresh', event)" | ||
> | ||
<span | ||
class="p-button-icon pi pi-refresh transition-all" | ||
:class="{ 'opacity-0': active }" | ||
data-pc-section="icon" | ||
></span> | ||
<span class="p-button-label" data-pc-section="label"> </span> | ||
<ProgressSpinner v-show="active" class="absolute w-1/2 h-1/2" /> | ||
</Button> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import Button from 'primevue/button' | ||
import ProgressSpinner from 'primevue/progressspinner' | ||
|
||
import { VueSeverity } from '@/types/primeVueTypes' | ||
|
||
// Properties | ||
interface Props { | ||
outlined?: boolean | ||
disabled?: boolean | ||
severity?: VueSeverity | ||
} | ||
const props = withDefaults(defineProps<Props>(), { | ||
outlined: true, | ||
severity: 'secondary' | ||
}) | ||
|
||
// Model | ||
const active = defineModel<boolean>({ required: true }) | ||
|
||
// Emits | ||
defineEmits(['refresh']) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<template> | ||
<Tag :icon :severity :value /> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { PrimeIcons, type PrimeIconsOptions } from '@primevue/core/api' | ||
import Tag, { TagProps } from 'primevue/tag' | ||
import { ref, watch } from 'vue' | ||
import { t } from '@/i18n' | ||
// Properties | ||
const props = defineProps<{ | ||
error: boolean | ||
refreshing?: boolean | ||
}>() | ||
// Bindings | ||
const icon = ref<string>(null) | ||
const severity = ref<TagProps['severity']>(null) | ||
const value = ref<PrimeIconsOptions[keyof PrimeIconsOptions]>(null) | ||
const updateBindings = () => { | ||
if (props.refreshing) { | ||
icon.value = PrimeIcons.QUESTION | ||
severity.value = 'info' | ||
value.value = t('maintenance.refreshing') | ||
} else if (props.error) { | ||
icon.value = PrimeIcons.TIMES | ||
severity.value = 'danger' | ||
value.value = t('g.error') | ||
} else { | ||
icon.value = PrimeIcons.CHECK | ||
severity.value = 'success' | ||
value.value = t('maintenance.OK') | ||
} | ||
} | ||
watch(props, updateBindings, { deep: true }) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
<template> | ||
<div | ||
class="task-div max-w-48 min-h-52 grid relative" | ||
:class="{ 'opacity-75': isLoading }" | ||
> | ||
<Card | ||
class="max-w-48 relative h-full overflow-hidden" | ||
:class="{ 'opacity-65': state.state !== 'error' }" | ||
v-bind="(({ onClick, ...rest }) => rest)($attrs)" | ||
> | ||
<template #header> | ||
<i | ||
v-if="state.state === 'error'" | ||
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15" | ||
style="font-size: 10rem" | ||
/> | ||
<img | ||
v-if="task.headerImg" | ||
:src="task.headerImg" | ||
class="object-contain w-full h-full opacity-25 pt-4 px-4" | ||
/> | ||
</template> | ||
<template #title>{{ task.name }}</template> | ||
<template #content>{{ description }}</template> | ||
<template #footer> | ||
<div class="flex gap-4 mt-1"> | ||
<Button | ||
:icon="task.button?.icon" | ||
:label="task.button?.text" | ||
class="w-full" | ||
raised | ||
icon-pos="right" | ||
@click="(event) => $emit('execute', event)" | ||
:loading="isExecuting" | ||
/> | ||
</div> | ||
</template> | ||
</Card> | ||
|
||
<i | ||
v-if="!isLoading && state.state === 'OK'" | ||
class="task-card-ok pi pi-check" | ||
/> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import Button from 'primevue/button' | ||
import Card from 'primevue/card' | ||
import { computed } from 'vue' | ||
import { useMaintenanceTaskStore } from '@/stores/maintenanceTaskStore' | ||
import type { MaintenanceTask } from '@/types/desktop/maintenanceTypes' | ||
import { useMinLoadingDurationRef } from '@/utils/refUtil' | ||
const taskStore = useMaintenanceTaskStore() | ||
const state = computed(() => taskStore.getState(props.task)) | ||
// Properties | ||
const props = defineProps<{ | ||
task: MaintenanceTask | ||
}>() | ||
// Events | ||
defineEmits<{ | ||
execute: [event: MouseEvent] | ||
}>() | ||
// Bindings | ||
const description = computed(() => | ||
state.value.state === 'error' | ||
? props.task.errorDescription ?? props.task.shortDescription | ||
: props.task.shortDescription | ||
) | ||
// Use a minimum run time to ensure tasks "feel" like they have run | ||
const reactiveLoading = computed(() => state.value.refreshing) | ||
const reactiveExecuting = computed(() => state.value.executing) | ||
const isLoading = useMinLoadingDurationRef(reactiveLoading, 250) | ||
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250) | ||
</script> | ||
|
||
<style scoped> | ||
.task-card-ok { | ||
@apply text-green-500 absolute -right-4 -bottom-4 opacity-100 row-span-full col-span-full transition-opacity; | ||
font-size: 4rem; | ||
text-shadow: 0.25rem 0 0.5rem black; | ||
z-index: 10; | ||
} | ||
.p-card { | ||
@apply transition-opacity; | ||
--p-card-background: var(--p-button-secondary-background); | ||
opacity: 0.9; | ||
&.opacity-65 { | ||
opacity: 0.4; | ||
} | ||
&:hover { | ||
opacity: 1; | ||
} | ||
} | ||
:deep(.p-card-header) { | ||
z-index: 0; | ||
} | ||
:deep(.p-card-body) { | ||
z-index: 1; | ||
flex-grow: 1; | ||
justify-content: space-between; | ||
} | ||
.task-div { | ||
> i { | ||
pointer-events: none; | ||
} | ||
&:hover > i { | ||
opacity: 0.2; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<template> | ||
<tr | ||
class="border-neutral-700 border-solid border-y" | ||
:class="{ | ||
'opacity-50': state.state === 'resolved', | ||
'opacity-75': isLoading && state.state !== 'resolved' | ||
}" | ||
> | ||
<td class="text-center w-16"> | ||
<TaskListStatusIcon :state="state.state" :loading="isLoading" /> | ||
</td> | ||
<td> | ||
<p class="inline-block">{{ task.name }}</p> | ||
<Button | ||
class="inline-block mx-2" | ||
type="button" | ||
:icon="PrimeIcons.INFO_CIRCLE" | ||
severity="secondary" | ||
:text="true" | ||
@click="toggle" | ||
/> | ||
|
||
<Popover ref="infoPopover" class="block m-1 max-w-64 min-w-32"> | ||
<span class="whitespace-pre-line">{{ task.description }}</span> | ||
</Popover> | ||
</td> | ||
<td class="text-right px-4"> | ||
<Button | ||
:icon="task.button?.icon" | ||
:label="task.button?.text" | ||
:severity | ||
icon-pos="right" | ||
@click="(event) => $emit('execute', event)" | ||
:loading="isExecuting" | ||
/> | ||
</td> | ||
</tr> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { PrimeIcons } from '@primevue/core/api' | ||
import Button from 'primevue/button' | ||
import Popover from 'primevue/popover' | ||
import { computed, ref } from 'vue' | ||
import { useMaintenanceTaskStore } from '@/stores/maintenanceTaskStore' | ||
import type { MaintenanceTask } from '@/types/desktop/maintenanceTypes' | ||
import { VueSeverity } from '@/types/primeVueTypes' | ||
import { useMinLoadingDurationRef } from '@/utils/refUtil' | ||
import TaskListStatusIcon from './TaskListStatusIcon.vue' | ||
const taskStore = useMaintenanceTaskStore() | ||
const state = computed(() => taskStore.getState(props.task)) | ||
// Properties | ||
const props = defineProps<{ | ||
task: MaintenanceTask | ||
}>() | ||
// Events | ||
defineEmits<{ | ||
execute: [event: MouseEvent] | ||
}>() | ||
// Binding | ||
const severity = computed<VueSeverity>(() => | ||
state.value.state === 'error' || state.value.state === 'warning' | ||
? 'primary' | ||
: 'secondary' | ||
) | ||
// Use a minimum run time to ensure tasks "feel" like they have run | ||
const reactiveLoading = computed(() => state.value.refreshing) | ||
const reactiveExecuting = computed(() => state.value.executing) | ||
const isLoading = useMinLoadingDurationRef(reactiveLoading, 250) | ||
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250) | ||
// Popover | ||
const infoPopover = ref() | ||
const toggle = (event: Event) => { | ||
infoPopover.value.toggle(event) | ||
} | ||
</script> |
Oops, something went wrong.