diff --git a/docs/DEVELOPMENT_SESSION_2025-11-19_Refresh_Button_Spinner.md b/docs/DEVELOPMENT_SESSION_2025-11-19_Refresh_Button_Spinner.md new file mode 100644 index 0000000..0009b8e --- /dev/null +++ b/docs/DEVELOPMENT_SESSION_2025-11-19_Refresh_Button_Spinner.md @@ -0,0 +1,153 @@ +# Development Session - 2025-11-19 + +## Session Overview + +**Started**: 18:05 +**Branch**: `feature/refresh-button-spinner` +**Focus**: Add loading spinners to refresh buttons and improve Logger admin panel UX + +## Major Accomplishments + +### Phase 1: Button Order and Spinner Implementation (18:05-18:30) + +- **Goal**: Swap button order on cards and add loading spinners to refresh buttons +- **Result**: Successfully implemented across all components +- **Code Changes**: + - `BaseCard.vue`: Swapped button order (Details left, Refresh right), added spinner + - `AutomaticGroupsAdmin.vue`: Added spinner, fixed loading state with `isFetching` + - `TagsAdmin.vue`: Added spinner to refresh button + - `ExpiringAppointmentsAdmin.vue`: Added spinner to refresh button + - `LoggerSummaryAdmin.vue`: Added spinner to refresh button + - `LoggerSummaryAdminBulk.vue`: Added spinner and fixed button styling + +### Phase 2: Logger Component Cleanup (18:55-19:00) + +- **Goal**: Remove unused legacy LoggerSummaryAdmin component +- **Result**: Cleaned up codebase, renamed Bulk version to standard name +- **Code Changes**: + - Removed `LoggerSummaryAdmin.vue` (legacy, 806 lines) + - Renamed `LoggerSummaryAdminBulk.vue` to `LoggerSummaryAdmin.vue` + - Updated imports in `App.vue` + - Removed "(Bulk Cache)" from component title + +### Phase 3: Logger Admin Panel UX Improvements (19:14-19:42) + +- **Goal**: Fix table display issues and improve user experience +- **Result**: Comprehensive UX overhaul with better filtering and display +- **Code Changes**: + - Fixed table title to show correct filtered count (not just paginated 50) + - Added dynamic title showing active filters: "Logger System - Letzter Tag - Fehler" + - Removed custom pagination in favor of AdminTable's built-in features + - Fixed search functionality (removed duplicate search fields) + - Changed default from 3 days to 1 day for both Card and Admin + - Increased Card log limit from 1000 to 1500 entries + - Added "1500+" display when Card hits log limit + +## Technical Decisions + +### Decision: Use isFetching for Loading State (18:15) + +**Context**: `isLoading` only true on initial load, not on refetch +**Decision**: Use `computed(() => isLoading.value || isFetching.value)` for better UX +**Impact**: Spinner now shows during manual refresh operations + +### Decision: Remove Custom Pagination (19:30) + +**Context**: Custom pagination conflicted with AdminTable's search functionality +**Decision**: Remove custom pagination, use AdminTable's built-in features +**Impact**: Simpler code, better search experience, all filtered logs searchable + +### Decision: Increase Card Limit to 1500 (19:39) + +**Context**: Card showed "1000 Einträge" but Admin showed more +**Decision**: Increase limit to 1500, show "1500+" when limited +**Impact**: Better consistency between Card and Admin, clearer when data is truncated + +### Decision: Remove Legacy LoggerSummaryAdmin (18:55) + +**Context**: Two versions existed (legacy and bulk), only bulk was used +**Decision**: Remove legacy version, rename bulk to standard name +**Impact**: Cleaner codebase, less confusion, 806 lines removed + +## Challenges and Solutions + +### Challenge: Duplicate Search Fields (19:25) + +**Problem**: AdminTable had its own search field, plus custom search field in actions +**Solution**: Removed custom search, disabled AdminTable search initially, then re-enabled AdminTable search and removed custom implementation +**Lesson**: Use framework features instead of duplicating functionality + +### Challenge: goToPage Undefined Error (19:31) + +**Problem**: `applyFilters()` called `goToPage(1)` after removing pagination +**Solution**: Made `applyFilters()` empty since filters are reactive +**Lesson**: Clean up all references when removing features + +### Challenge: Table Showing Wrong Count (19:14) + +**Problem**: Table title showed "50 Einträge" (paginated count) instead of total filtered +**Solution**: Pass `filteredLogs` to AdminTable instead of `paginatedLogs`, remove custom pagination +**Lesson**: Let table components handle their own pagination + +## Code Quality + +- All changes passed `npm run lint` (type-check + format-check) +- Consistent spinner styling across all components +- Proper use of TanStack Query's `isFetching` for loading states +- Clean separation of concerns (filtering vs pagination) + +## Commits + +1. `feat: add loading spinner to refresh buttons` - Initial spinner implementation +2. `feat: add refresh button with spinner to logger admin panels` - Logger components +3. `refactor: remove unused LoggerSummaryAdmin component` - Cleanup legacy code +4. `refactor: rename LoggerSummaryAdminBulk to LoggerSummaryAdmin` - Simplify naming +5. `feat: improve logger admin panel UX and fix table display` - UX improvements + +## Next Steps + +- [ ] Monitor user feedback on new button order +- [ ] Consider adding spinner to other loading operations +- [ ] Evaluate if 1500 log limit is sufficient for Card +- [ ] Consider adding pagination info to AdminTable footer + +## Lessons Learned + +### 1. Loading State Management with TanStack Query + +**Problem**: `isLoading` only true on initial load, not on refetch +**Solution**: Combine `isLoading` and `isFetching` in computed property +**Application**: Use for any manual refresh operations with TanStack Query + +### 2. Avoid Duplicating Framework Features + +**Problem**: Created custom search field when AdminTable already had one +**Solution**: Use AdminTable's built-in search functionality +**Application**: Always check if framework/component already provides needed feature + +### 3. Clean Up All References When Removing Features + +**Problem**: Removed pagination but left `goToPage()` call +**Solution**: Search for all references before removing features +**Application**: Use grep/search to find all usages before deletion + +### 4. Consistent Spinner Styling + +**Problem**: Different components might have different spinner implementations +**Solution**: Use consistent CSS classes and animations across all components +**Application**: Define common UI patterns in shared styles or components + +## Files Modified + +- `src/components/common/BaseCard.vue` +- `src/components/automatic-groups/AutomaticGroupsAdmin.vue` +- `src/components/tags/TagsAdmin.vue` +- `src/components/expiring-appointments/ExpiringAppointmentsAdmin.vue` +- `src/components/loggerSummary/LoggerSummaryAdmin.vue` (renamed from Bulk) +- `src/components/loggerSummary/LoggerSummaryCard.vue` +- `src/composables/useLoggerSummaryQuery.ts` +- `src/App.vue` + +## Files Deleted + +- `src/components/loggerSummary/LoggerSummaryAdmin.vue` (legacy version) diff --git a/package-lock.json b/package-lock.json index fd32534..7ee71db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "churchtools-dashboard", - "version": "1.0.4", + "version": "1.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "churchtools-dashboard", - "version": "1.0.4", + "version": "1.0.7", "dependencies": { "@fortawesome/fontawesome-svg-core": "^7.0.1", "@fortawesome/free-solid-svg-icons": "^7.0.1", diff --git a/package.json b/package.json index 3fc5e79..3b4bc78 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "churchtools-dashboard", "private": true, - "version": "1.0.7", + "version": "1.0.8", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.vue b/src/App.vue index e796336..039c15e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -56,7 +56,6 @@ import BeispielCard from './components/beispiel/BeispielCard.vue' import ColorPickerExample from './components/common/ColorPickerExample.vue' import LoggerSummaryCard from './components/loggerSummary/LoggerSummaryCard.vue' import LoggerSummaryAdmin from './components/loggerSummary/LoggerSummaryAdmin.vue' -import LoggerSummaryAdminBulk from './components/loggerSummary/LoggerSummaryAdminBulk.vue' import Toast from './components/common/Toast.vue' import { useToast } from './composables/useToast' @@ -93,7 +92,7 @@ const modules: DashboardModule[] = [ icon: '📋', description: 'Überwachung und Verwaltung von Log-Einträgen', cardComponent: LoggerSummaryCard, - adminComponent: LoggerSummaryAdminBulk, + adminComponent: LoggerSummaryAdmin, }, ] diff --git a/src/components/automatic-groups/AutomaticGroupsAdmin.vue b/src/components/automatic-groups/AutomaticGroupsAdmin.vue index 43a384e..5c7dc1a 100644 --- a/src/components/automatic-groups/AutomaticGroupsAdmin.vue +++ b/src/components/automatic-groups/AutomaticGroupsAdmin.vue @@ -23,6 +23,7 @@ Suche zurücksetzen @@ -75,7 +76,8 @@ defineProps<{ }>() // Use TanStack Query composable for data management -const { data: groups, isLoading: loading, error, refetch } = useAutomaticGroups() +const { data: groups, isLoading, isFetching, error, refetch } = useAutomaticGroups() +const loading = computed(() => isLoading.value || isFetching.value) // AdminTable reference const adminTableRef = ref() @@ -193,8 +195,8 @@ const formatDate = (dateString: string | null) => { // URL generation is now handled by the churchtools service // Data loading -const refreshGroups = () => { - refetch() +const refreshGroups = async () => { + await refetch() } const clearSearch = () => { @@ -304,4 +306,24 @@ const clearSearch = () => { padding: 0.5rem 1rem; font-size: 0.85rem; } + +.btn-spinner { + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-top-color: white; + border-radius: 50%; + animation: spin 0.6s linear infinite; + margin-right: 0.5rem; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/common/BaseCard.vue b/src/components/common/BaseCard.vue index 0d2158f..d4a0528 100644 --- a/src/components/common/BaseCard.vue +++ b/src/components/common/BaseCard.vue @@ -81,14 +81,6 @@
@@ -415,6 +416,17 @@ const getStatusClass = (type?: string) => { cursor: not-allowed; } +.btn-spinner { + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-top-color: white; + border-radius: 50%; + animation: spin 0.6s linear infinite; + margin-right: 0.5rem; +} + .ct-btn-icon { background: none; border: none; diff --git a/src/components/expiring-appointments/ExpiringAppointmentsAdmin.vue b/src/components/expiring-appointments/ExpiringAppointmentsAdmin.vue index 360321d..ef2702c 100644 --- a/src/components/expiring-appointments/ExpiringAppointmentsAdmin.vue +++ b/src/components/expiring-appointments/ExpiringAppointmentsAdmin.vue @@ -70,6 +70,7 @@ Filter löschen @@ -548,4 +549,24 @@ onMounted(() => { border-color: var(--ct-primary, #3498db); box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.1); } + +.btn-spinner { + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-top-color: white; + border-radius: 50%; + animation: spin 0.6s linear infinite; + margin-right: 0.5rem; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/loggerSummary/LoggerSummaryAdmin.vue b/src/components/loggerSummary/LoggerSummaryAdmin.vue index 519c2b6..d0e9e88 100644 --- a/src/components/loggerSummary/LoggerSummaryAdmin.vue +++ b/src/components/loggerSummary/LoggerSummaryAdmin.vue @@ -2,21 +2,21 @@{{ item.source }}
-
-
-
-