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
10 changes: 10 additions & 0 deletions VueApp/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ export default [
},
},
rules: {
// Quasar components are auto-imported by the vite plugin — flag explicit imports for cleanup
"no-restricted-imports": ["warn", {
patterns: [{
group: ["quasar"],
importNamePattern: "^Q[A-Z]",
allowTypeImports: true,
message: "Quasar components are auto-imported — remove explicit imports (QBtn, QTable, etc.). Utilities like setCssVar, useQuasar, Notify are fine.",
}],
}],

// Disable the base rule and enable TypeScript-aware version
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", {
Expand Down
29 changes: 7 additions & 22 deletions VueApp/src/Effort/components/AddCourseEffortDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -122,36 +122,20 @@
/>

<!-- Warning Message -->
<q-banner
<StatusBanner
v-if="warningMessage"
class="bg-warning"
rounded
role="alert"
type="warning"
>
<template #avatar>
<q-icon
name="warning"
color="dark"
/>
</template>
{{ warningMessage }}
</q-banner>
</StatusBanner>

<!-- Error Message -->
<q-banner
<StatusBanner
v-if="errorMessage"
class="bg-negative text-white"
rounded
role="alert"
type="error"
>
<template #avatar>
<q-icon
name="error"
color="white"
/>
</template>
{{ errorMessage }}
</q-banner>
</StatusBanner>
</q-form>
</q-card-section>

Expand Down Expand Up @@ -187,6 +171,7 @@ import { useUnsavedChanges } from "@/composables/use-unsaved-changes"
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 { filterEffortTypesByCourse } from "../utils/effort-type-filters"
import { effortValueRules, requiredRule, notesMaxHint } from "../validation"
import "../effort-dialogs.css"
Expand Down
69 changes: 20 additions & 49 deletions VueApp/src/Effort/components/ClinicalImportDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@
<q-linear-progress
:value="importProgress"
size="25px"
color="teal-8"
color="info"
class="q-mb-md"
>
<div class="absolute-full flex flex-center">
<q-badge
color="white"
text-color="teal-8"
text-color="info"
:label="`${Math.round(importProgress * 100)}%`"
/>
</div>
Expand Down Expand Up @@ -90,17 +90,14 @@
<q-option-group
v-model="selectedMode"
:options="modeOptions"
color="teal-8"
color="info"
:inline="$q.screen.gt.xs"
@update:model-value="onModeChange"
/>
</div>

<!-- Summary Banner -->
<q-banner
class="bg-blue-1 q-mb-md"
rounded
>
<StatusBanner type="info">
<div class="row q-col-gutter-md">
<div class="col-6 col-sm-3 text-center">
<div class="text-h5 text-positive">{{ preview.addCount }}</div>
Expand All @@ -127,21 +124,14 @@
<div class="text-caption text-grey-7 q-mt-sm text-center">
Data as of {{ formatPreviewDateTime(preview.previewGeneratedAt) }}
</div>
</q-banner>
</StatusBanner>

<!-- Warnings Section -->
<q-banner
<StatusBanner
v-if="preview.warnings.length > 0"
class="bg-orange-1 q-mb-md"
rounded
type="warning"
>
<div class="row items-center q-mb-xs">
<q-icon
name="warning"
color="orange"
size="sm"
class="q-mr-sm"
/>
<span class="text-weight-medium">
{{ preview.warnings.length }} {{ inflect("Warning", preview.warnings.length) }}
</span>
Expand All @@ -154,21 +144,15 @@
{{ warning }}
</li>
</ul>
</q-banner>
</StatusBanner>

<!-- Delete Warning for Sync Mode with Empty Source -->
<q-banner
<StatusBanner
v-if="selectedMode === 'Sync' && preview.addCount === 0 && preview.deleteCount > 0"
class="bg-red-1 q-mb-md"
rounded
type="error"
icon="dangerous"
>
<div class="row items-center q-mb-xs">
<q-icon
name="dangerous"
color="negative"
size="sm"
class="q-mr-sm"
/>
<span class="text-weight-medium text-negative"
>Sync will delete ALL clinical effort records</span
>
Expand All @@ -177,21 +161,14 @@
The source returned 0 records. Syncing will delete all {{ preview.deleteCount }} existing
clinical effort records for this term.
</div>
</q-banner>
</StatusBanner>

<!-- Delete Warning Banner -->
<q-banner
<StatusBanner
v-else-if="preview.deleteCount > 0"
class="bg-red-1 q-mb-md"
rounded
type="error"
>
<div class="row items-center q-mb-xs">
<q-icon
name="warning"
color="negative"
size="sm"
class="q-mr-sm"
/>
<span class="text-weight-medium text-negative">
{{ preview.deleteCount }} {{ inflect("record", preview.deleteCount) }} will be deleted
</span>
Expand All @@ -204,27 +181,20 @@
Sync mode will remove clinical records that no longer exist in the source.
</template>
</div>
</q-banner>
</StatusBanner>

<!-- Nothing to Import Warning -->
<q-banner
<StatusBanner
v-if="totalChanges === 0"
class="bg-orange-1 q-mb-md"
rounded
type="warning"
>
<div class="row items-center q-mb-xs">
<q-icon
name="info"
color="orange"
size="sm"
class="q-mr-sm"
/>
<span class="text-weight-medium">Nothing to import</span>
</div>
<div class="text-caption text-grey-7">
There are no changes to make with the selected import mode.
</div>
</q-banner>
</StatusBanner>

<!-- Preview Table -->
<ClinicalEffortPreviewTable
Expand All @@ -248,7 +218,7 @@
/>
<q-btn
label="Confirm Import"
color="teal-8"
color="info"
:disable="totalChanges === 0 || isCommitting"
@click="confirmImport"
/>
Expand All @@ -264,6 +234,7 @@ import { useQuasar } from "quasar"
import { clinicalService } from "../services/clinical-service"
import type { ClinicalImportPreviewDto, ClinicalImportMode } from "../types"
import { inflect } from "inflection"
import StatusBanner from "@/components/StatusBanner.vue"
import ClinicalEffortPreviewTable from "./ClinicalEffortPreviewTable.vue"

const props = defineProps<{
Expand Down
43 changes: 32 additions & 11 deletions VueApp/src/Effort/components/CourseEffortTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,12 @@
</div>

<!-- Error state -->
<q-banner
<StatusBanner
v-else-if="loadError"
class="bg-negative text-white q-mb-md"
rounded
role="alert"
type="error"
>
<template #avatar>
<q-icon
name="error"
color="white"
/>
</template>
{{ loadError }}
</q-banner>
</StatusBanner>

<template v-else-if="records.length > 0">
<!-- Mobile Card View -->
Expand Down Expand Up @@ -84,7 +76,21 @@
<div class="text-body2 q-mb-xs">
{{ record.effortTypeDescription }} ({{ record.effortTypeId }}) &bull;
<span :class="{ 'zero-effort-text': record.effortValue === 0 }">
<q-icon
v-if="record.effortValue === 0"
name="report_problem"
color="amber-8"
size="1rem"
class="q-mr-xs"
aria-hidden="true"
/>
Comment on lines +79 to +86
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The zero-effort icon uses amber-8, which reintroduces a hard-coded Quasar palette color in a PR that is moving components to semantic theme tokens (warning/info). Prefer color="warning" here so the icon color is controlled by the app theme (and remains consistent with the other warning indicators added in this PR).

Copilot uses AI. Check for mistakes.
{{ record.effortValue }} {{ record.effortLabel }}
<span
v-if="record.effortValue === 0"
class="sr-only"
>
(zero effort)
</span>
</span>
</div>
<div
Expand Down Expand Up @@ -217,8 +223,22 @@

<!-- Effort cell with unit label -->
<template v-else-if="col.name === 'effort'">
<q-icon
v-if="slotProps.row.effortValue === 0"
name="report_problem"
color="amber-8"
size="1rem"
class="q-mr-xs"
aria-hidden="true"
/>
Comment on lines +226 to +233
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This second zero-effort indicator also uses the hard-coded amber-8 palette color. For consistent theming (and to avoid contrast regressions if the theme changes), use the semantic warning color token here too.

Copilot uses AI. Check for mistakes.
{{ slotProps.row.effortValue }}
{{ slotProps.row.effortLabel }}
<span
v-if="slotProps.row.effortValue === 0"
class="sr-only"
>
(zero effort)
</span>
</template>

<!-- Actions cell -->
Expand Down Expand Up @@ -279,6 +299,7 @@
<script setup lang="ts">
import { computed, ref } from "vue"
import type { QTableColumn } from "quasar"
import StatusBanner from "@/components/StatusBanner.vue"
import type { CourseEffortRecordDto } from "../types"
import { useEffortPermissions } from "../composables/use-effort-permissions"
import "../effort-record-table.css"
Expand Down
Loading