# 23-Mar-2026 — add committee documents row type, index-based navigation, and draggable swiper [ref #162](https://github.com/nbarrett/ngx-ramblers/issues/162)

## [build 547](https://github.com/nbarrett/ngx-ramblers/actions/runs/23466309751) — [commit d793171](https://github.com/nbarrett/ngx-ramblers/commit/d7931711549df6174c5cc95c6414f46e01f28d96)

_____

## End-User Changes
### Committee Documents as CMS Content
- Committee file listings can now be placed on any CMS page as a row type
- Each year's committee files (AGM minutes, financial returns, etc.) appear
on their own CMS-managed page at `committee/{year}`
- The root `/committee` page auto-populates with the most recent year's files
- Images are configurable per year page via the Image Actions editor
(edit/replace/remove) — the same editor available on text rows
- The Index automatically picks up each year page's image for its cards
### Committee Years Navigation
- Year navigation cards are now generated by an Index row instead of
hand-maintained action buttons — new years appear automatically
- The shared fragment `fragments/committee-years` holds the index
configuration so all pages share the same navigation
- Edit fragment button opens the fragment in a new tab to avoid losing
site edit context
- Fragment inline preview hides markdown edit buttons to reduce clutter
### Draggable Swiper for Action Buttons
- Action button carousels can now be dragged/swiped on both desktop (mouse)
and mobile (touch) to browse cards
- Cards track the cursor/finger position in real-time during drag
- Releasing the drag snaps to the nearest grid position with smooth animation
- Multi-card drag: dragging across multiple card widths advances that many
positions instead of just one
- Native browser image drag is suppressed within the swiper
- Click events are blocked during drag — only a clean tap/click navigates
- Arrow buttons continue to work alongside drag
### Fragment Margin Support
- Margin Top/Bottom settings on shared fragment rows now apply correctly
in the page view
## Technical Changes
### Migration (`20260319000000-convert-committee-page-to-cms`)
- Fragment path changed from `committee-committee-years` to
`fragments/committee-years` (required prefix for FragmentService)
- Fragment content converted from action-button rows to an Index row
(`ALBUM_INDEX`) with `STARTS_WITH` match on `committee/`, sorted DESC
- Index configured with `maxColumns: 3`, `showSwiper: true`,
`indexMarkdown: "## Committee Years"`, `autoTitle: false`
- Hardcoded "EKWG" intro text replaced with dynamic lookup from
system config `area.shortName`
- `new Date()` replaced with `dateTimeFromMillis()` from `dates.ts`
- Each year page's `committeeDocuments.imageSource` populated from the
existing fragment action-button column images
### Image Discovery for Index
- `findFirstImageInPage()` in both `IndexService` and
`LocationExtractionService` now checks `row.committeeDocuments?.imageSource`
before scanning columns — enables Index cards to display committee doc images
### Committee Documents Site Edit
- Image Actions dropdown (edit/replace/remove) with image cropper added
to committee documents row editing
- Image controls hidden when "Auto Populate" is checked (image comes
from child page)
### Auto-Populate Improvements
- Replaced fragile `siteLinks`-based child page lookup with direct
`$regex` database query for reliability
- Page title derived from child page path instead of URL fragments
### View Component Changes
- `hideEditToggle` input threaded through `DynamicContentView` →
`DynamicContentViewTextRow` → `MarkdownEditorComponent` to suppress
inline edit buttons in fragment previews
- `contentDescription` fallback to fragment path prevents
"Edit content for undefined" tooltip
### Swiper Implementation (action-buttons.ts)
- Continuous flex strip with `overflow: hidden` viewport replaces
slice-based `viewableColumns()` for swiper mode
- `gap: 24px` on flex container with `calc()`-based flex-basis matches
Bootstrap grid spacing
- Pixel-based `translateX` for both slide position and drag offset
- Capture-phase click listener blocks navigation during drag
- `pointer-events: none` and `-webkit-user-drag: none` on images
prevents native drag interference
### Route changes
- Removed hardcoded `/committee` route — committee pages now served
as CMS dynamic content via `hasDynamicPath` matcher
- Added `hasSendNotificationPath` URL matcher for `<area>/send-notification/<id>`
- Added `PathSegment` and `RouteParam` enums replacing hardcoded strings
- Consolidated `CommitteeEditFileModal` into single-file component
### Removed
- `CommitteeHomeComponent`, `CommitteeYearComponent` and templates
- `CommitteeDocumentsYearFilter` enum, `BuiltInAnchor.COMMITTEE_YEARS`,
`BuiltInPath.COMMITTEE`, `PageContentPath.COMMITTEE_ACTION_BUTTONS_YEARS`
- `CommitteeRoutingModule`
### Bug fixes
- Fixed `.reverse()` mutation bug in `RouterHistoryService`
### Backup/restore resilience
- Added timeout and retry logic for mongorestore operations