Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 7 additions & 21 deletions VueApp/src/Effort/components/AddCourseEffortDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,27 +145,12 @@
</q-form>
</q-card-section>

<q-card-actions align="right">
<q-btn
flat
label="Cancel"
@click="handleClose"
/>
<q-btn
color="primary"
label="Add Effort"
:loading="isSaving"
@click="createRecord"
>
<template #loading>
<q-spinner
size="1em"
class="q-mr-sm"
/>
Add Effort
</template>
</q-btn>
</q-card-actions>
<DialogSubmitActions
submit-label="Add Effort"
:is-saving="isSaving"
@cancel="handleClose"
@submit="createRecord"
/>
</q-card>
</q-dialog>
</template>
Expand All @@ -178,6 +163,7 @@ import { courseService } from "../services/course-service"
import { recordService } from "../services/record-service"
import type { CourseInstructorOptionDto, EffortTypeOptionDto, RoleOptionDto } from "../types"
import StatusBanner from "@/components/StatusBanner.vue"
import DialogSubmitActions from "./DialogSubmitActions.vue"
import { filterEffortTypesByCourse } from "../utils/effort-type-filters"
import { effortValueRules, requiredRule, notesMaxHint } from "../validation"
import "../effort-dialogs.css"
Expand Down
137 changes: 137 additions & 0 deletions VueApp/src/Effort/components/AsyncOperationDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<script setup lang="ts">
withDefaults(
defineProps<{
modelValue: boolean
title: string
subtitle?: string
maxWidth?: string
isLoading: boolean
isCommitting: boolean
loadError?: string | null
progress?: number
progressTitle?: string
progressPhase?: string
progressDetail?: string
progressColor?: string
loadingMessage?: string
}>(),
{
subtitle: undefined,
maxWidth: "1000px",
loadError: null,
progress: 0,
progressTitle: "",
progressPhase: "",
progressDetail: "",
progressColor: "primary",
loadingMessage: "Generating preview...",
},
)

defineEmits<{
retry: []
close: []
}>()
</script>

<template>
<q-dialog
:model-value="modelValue"
persistent
maximized-on-mobile
aria-labelledby="async-operation-dialog-title"
@keydown.escape="$emit('close')"
>
<q-card :style="`width: 100%; max-width: ${maxWidth}; position: relative`">
<q-btn
icon="close"
flat
round
dense
class="absolute-top-right q-ma-sm"
style="z-index: 1"
aria-label="Close dialog"
@click="$emit('close')"
/>
<q-card-section class="q-pb-none q-pr-xl">
<div
id="async-operation-dialog-title"
class="text-h6"
>
{{ title }}
</div>
<div
v-if="subtitle"
class="text-caption text-grey-7"
>
{{ subtitle }}
</div>
</q-card-section>

<slot name="before-body" />

<!-- Loading State (Preview) -->
<q-card-section
v-if="isLoading"
class="text-center q-py-xl"
>
<q-spinner-dots
size="50px"
color="primary"
/>
<div class="q-mt-md text-grey-7">{{ loadingMessage }}</div>
</q-card-section>

<!-- Committing State (Progress) -->
<q-card-section
v-else-if="isCommitting"
class="q-py-xl"
>
<div class="text-h6 q-mb-md text-center">{{ progressTitle }}</div>
<q-linear-progress
:value="progress"
size="25px"
:color="progressColor"
class="q-mb-md"
>
<div class="absolute-full flex flex-center">
<q-badge
color="white"
:text-color="progressColor"
:label="`${Math.round(progress * 100)}%`"
/>
</div>
</q-linear-progress>
<div class="text-center text-grey-7">{{ progressPhase }}</div>
<div
v-if="progressDetail"
class="text-center text-caption text-grey-6 q-mt-xs"
>
{{ progressDetail }}
</div>
</q-card-section>

<!-- Error State -->
<q-card-section
v-else-if="loadError"
class="text-center q-py-xl"
>
<q-icon
name="error"
color="negative"
size="48px"
/>
<div class="q-mt-md text-negative">{{ loadError }}</div>
<q-btn
label="Retry"
color="primary"
class="q-mt-md"
@click="$emit('retry')"
/>
</q-card-section>

<!-- Preview content -->
<slot v-else />
</q-card>
</q-dialog>
</template>
Loading