- **`FeaturedEventsAPI` now returns `event_type_name` string** — `model_to_dict()` serialises the `event_type` FK as an integer ID; the hero slider frontend reads `ev.event_type_name` to display the category badge, which was always `null`
- Added `data_dict['event_type_name'] = e.event_type.event_type if e.event_type else ''` after `model_to_dict(e)` to resolve the FK to its human-readable name (e.g. `"Festivals"`)
- No frontend changes required — `fetchHeroSlides()` already falls back to `ev.event_type_name`
- **`FeaturedEventsAPI` now works without authentication** — `POST /api/events/featured-events/` had `AllowAny` permission but still called `validate_token_and_get_user()`, causing the endpoint to return HTTP 200 + `{"status":"error","message":"token and username required"}` for unauthenticated requests (e.g. the desktop hero slider)
- Removed the `validate_token_and_get_user()` call entirely — the endpoint is public by design and requires no token
- Also tightened the queryset to `event_status='published'` (was `is_featured=True` only) to match `ConsumerFeaturedEventsView` behaviour and avoid returning draft/cancelled events
- Root cause: host Nginx routes `/api/` → `eventify-backend` container (port 3001), not `eventify-django` (port 8085); the `validate_token_and_get_user` gate in this container was silently blocking all hero slider requests
-`district_changed_at` DateTimeField on User model (migration `0013_user_district_changed_at`) — nullable, no backfill; NULL means "eligible to change immediately"
-`VALID_DISTRICTS` constant (14 Kerala districts) in `accounts/models.py` for server-side validation
-`WebRegisterForm` now accepts optional `district` field; stamps `district_changed_at` on valid selection during signup
-`UpdateProfileView` enforces 183-day (~6 months) cooldown — rejects district changes within the window with a human-readable "Next change: {date}" error
-`district_changed_at` included in all relevant API responses: `LoginView`, `WebRegisterView`, `StatusView`, `UpdateProfileView`
-`StatusView` now also returns `district` field (was previously missing)
- **Internal exceptions no longer exposed to API callers** — all 15 `except Exception as e` blocks across `mobile_api/views/user.py` and `mobile_api/views/events.py` now log the real error via `eventify_logger` and return a generic `"An unexpected server error occurred."` to the caller
-`StatusView` and `UpdateProfileView` were also missing `log(...)` calls entirely — added
-`from eventify_logger.services import log` import added to `events.py` (was absent)
---
## [1.6.1] — 2026-04-03
### Added
- **`eventify_id` in `StatusView` response** (`/api/user/status/`) — consumer app uses this to refresh the Eventify ID badge (`EVT-XXXXXXXX`) for sessions that pre-date the `eventify_id` login field
- **`accounts` migration `0012_user_eventify_id` deployed to production containers** — backfilled all existing users with unique Eventify IDs; previously the migration existed locally but had not been applied in production
- **Unique Eventify ID system** (`EVT-XXXXXXXX` format)
- New `eventify_id` field on `User` model — `CharField(max_length=12, unique=True, editable=False, db_index=True)`
- Charset `ABCDEFGHJKLMNPQRSTUVWXYZ23456789` (no ambiguous characters I/O/0/1) giving ~1.78T combinations
- Auto-generated on first `save()` via a 10-attempt retry loop using `secrets.choice()`
- Migration `0012_user_eventify_id`: add nullable → backfill all existing users → make non-null
-`eventify_id` exposed in `accounts/api.py` → `_partner_user_to_dict()` fields list
-`eventify_id` exposed in `partner/api.py` → `_user_to_dict()` fields list
-`eventify_id` exposed in `mobile_api/views/user.py` → `LoginView` response (populates `localStorage.event_user.eventify_id`)
-`eventifyId` exposed in `admin_api/views.py` → `_serialize_user()` (camelCase for direct TypeScript compatibility)
- Server-side search in `UserListView` now also filters on `eventify_id__icontains`
- Synced migration `0011_user_allowed_modules_alter_user_id` (pulled from server, was missing from local repo)
### Changed
-`accounts/models.py`: merged `allowed_modules` field + `get_allowed_modules()` + `ALL_MODULES` constant from server (previously only existed on server)
---
## [1.5.0] — 2026-03-31
### Added
-`allowed_modules` TextField on `User` model — comma-separated module slug access control
-`get_allowed_modules()` method on `User` — returns list of accessible modules based on role or explicit list
-`ALL_MODULES` class constant listing all platform module slugs