Bug Reports

analytics exports are 404ing
## Enhanced Ticket Type: Bug Priority Recommendation: High - Export is a core feature for Team tier users --- ### Description Analytics export functionality returns 404 errors because the frontend ExportButton component uses a direct fetch() call instead of the generated hey-api client, causing requests to go to the Next.js server instead of the Django backend. --- ### Root Cause Analysis The ExportButton component at /apps/frontend/src/components/analytics/export-button.tsx (lines 68-73) makes a direct fetch call: ```typescript const response = await fetch( /api/v1/analytics/export/?${params.toString()} , { method: "GET", headers: { "Content-Type": "application/json", }, }); ``` Problems: Missing Base URL : The relative URL /api/v1/analytics/export/ resolves to the Next.js server (port 3100) instead of the Django backend (port 8100) Missing Authorization Header : Unlike the hey-api client which automatically injects the JWT token via the auth configuration in /apps/frontend/src/lib/api/client.ts , this direct fetch has no authentication Ignores Generated Client : A properly configured analyticsExportRetrieve function exists in /apps/frontend/src/lib/api/generated/sdk.gen.ts but isn't being used --- ### Technical Context Backend endpoint is correctly implemented: Location: /apps/backend/apps/analytics/views/analytics.py (lines 747-815) URL: /api/v1/analytics/export/ Supports format (csv/json) and type (overview/matches/games/etc.) query params Returns proper file download responses Frontend has generated client available: analyticsExportRetrieve in /apps/frontend/src/lib/api/generated/sdk.gen.ts Automatically handles auth and base URL Should be used for all API calls per project conventions --- ### Affected Files | File | Change Required | |------|-----------------| | /apps/frontend/src/components/analytics/export-button.tsx | Replace direct fetch() with analyticsExportRetrieve from hey-api client | --- ### Implementation Notes Import the generated SDK function: ```typescript import { analyticsExportRetrieve } from "@/lib/api/generated"; ``` Replace the fetch call with the hey-api client function which handles: - Base URL resolution - JWT authentication header injection - Proper error handling Handle blob response - The hey-api client may need special handling for file downloads. Consider using response.blob() or implementing a custom fetch wrapper that preserves the file download functionality. Alternative approach - If hey-api doesn't support blob responses well, update the fetch to use proper base URL and auth: ```typescript const baseUrl = process.env.NEXT _PUBLIC_API_URL ?? " http://localhost:8100 "; const token = localStorage.getItem("nexlog_access_token"); const response = await fetch( ${baseUrl}/api/v1/analytics/export/?${params} , { headers: { Authorization: Bearer ${token} , }, }); ``` --- ### Acceptance Criteria [ ] Clicking "Export as CSV" downloads a CSV file with analytics data [ ] Clicking "Export as JSON" downloads a JSON file with analytics data [ ] Export respects current filter selections (date range, users, decks, etc.) [ ] Export shows loading state during download [ ] Export shows error toast if download fails [ ] Subscription tier gating still works (Team tier required for CSV export) [ ] No 404 errors in browser console or network tab --- ### Testing Requirements Replicate the bug: - Navigate to Analytics page - Click Export button - Select "Export as CSV" or "Export as JSON" - Observe 404 error in Network tab Verify fix: - Same steps should now trigger successful download - Check Network tab shows request to correct backend URL (port 8100) - Check request includes Authorization header - Verify downloaded file contains expected data Edge cases to test: - Export with various filter combinations - Export when not logged in (should fail gracefully) - Export for users without Team tier (should show upgrade prompt) --- ### Questions for Stakeholder Should the export include a timestamp in the filename (currently does: analytics_export_2024-01-15.csv )? For large datasets, should we consider async export with email delivery instead of direct download? --- ### Estimated Complexity S (Small) - Single file change, straightforward fix using existing patterns
2
·
complete
Analytics Archetype Filter Not Finding Matches With Direct Archetype (No Deck)
## Description When filtering analytics by archetype (for matches the user has played), it shows no data. This occurs because the archetype filter only checks deck.archetype and ignores matches recorded with a direct archetype field (without selecting a specific deck). ## Root Cause Analysis The Match model supports TWO ways to specify the player's archetype: Via Deck ( match.deck.archetype ) - When a specific deck is selected Direct Archetype ( match.archetype ) - When only an archetype is specified (no deck) The Bug: The analytics filter in BaseAnalyticsService._apply_match_filters() ONLY checks deck__archetype_id : ```python Current code (BUGGY): if self.filters.archetype_ids: queryset = queryset.filter(deck__archetype_id__in=self.filters.archetype_ids) Matches recorded with just an `archetype` (without a `deck`) are NOT found by the filter. ## Affected Files **`/apps/backend/apps/analytics/services/base.py`** - Lines 101-102 and 125-128 The `_apply_match_filters()` and `_apply_game_filters()` methods need to use an OR condition: ```python # FIXED: from django.db.models import Q if self.filters.archetype_ids: queryset = queryset.filter( Q(deck__archetype_id__in=self.filters.archetype_ids) | Q(archetype_id__in=self.filters.archetype_ids) ) Same pattern for _apply_game_filters() using match__deck__archetype_id and match__archetype_id . Also update select_related() calls to include the direct archetype path. ## Acceptance Criteria [ ] Analytics archetype filter finds matches recorded with direct archetype (no deck) [ ] Analytics archetype filter continues to find matches recorded with deck.archetype [ ] Both match-level and game-level filters are updated [ ] select_related includes direct archetype path for query optimization [ ] Unit tests added for both scenarios (deck.archetype and direct archetype) ## Test-Driven Development Write failing tests first in apps/backend/apps/analytics/tests/test_archetype_filter.py : test_filter_finds_match_with_deck_archetype - Should pass now test_filter_finds_match_with_direct_archetype - Will fail, proving the bug test_filter_finds_both_match_types - Will fail until fixed ## Estimated Complexity S (Small) - Single file change with clear fix pattern. Main effort is in writing comprehensive tests.
2
·
complete
RSVP Attendees Not Displaying on Event Detail Page
## Description The RSVP attendees list is not showing team users' RSVP statuses on the event detail page. When users RSVP to an event (Yes/No/Maybe), their responses should appear in the "Attendees" card at the bottom of the event detail page, but currently the list appears empty or fails to load. ## Original Report > RSVP not showing RSVP statuses of team users in event detail page > > Context (auto-captured) > - Team: Team Flexslot > - Subscription: team > - Page: https://www.nexlog.gg/events/247c9324-7d03-42a3-b18b-e9d3dd4bab9e > - Browser: Chrome 144.0.0.0 (Windows) ## Steps to Reproduce Navigate to /events and select an event Have multiple team members RSVP to the event using the "Your Response" buttons View the event detail page Observe the "Attendees" card at the bottom of the page Expected : See list of RSVPs grouped by response (Going, Maybe, Not Going) Actual : Attendees list is empty or not loading ## Technical Context ### Frontend Components Event Detail Page : /apps/frontend/src/app/(app)/events/[id]/event-detail-content.tsx - Fetches event via tournamentsRetrieveOptions({ path: { id } }) - Fetches RSVP summary via tournamentsRsvpsSummaryRetrieveOptions({ path: { event_pk: id } }) - Renders RSVPSummary (line 173) - shows compact badge with counts - Renders RSVPButton (line 184-188) - allows user to set their RSVP - Renders RSVPAttendees (line 419) - shows full attendee list grouped by response RSVPAttendees Component : /apps/frontend/src/components/events/rsvp-attendees.tsx - Fetches attendees via tournamentsRsvpsListOptions({ path: { event_pk: eventId }, query: { page_size: 100 } }) - Groups RSVPs by response type (yes/maybe/no) - Displays avatar, name, and optional note for each attendee RSVPSummary Component : /apps/frontend/src/components/events/rsvp-summary.tsx - Shows counts (X going, +Y maybe) in a badge - Tooltip shows breakdown ### Backend API ViewSet : /apps/backend/apps/tournaments/views/event_rsvp.py - list action: Lists all RSVPs for event, uses list_rsvps_for_event() selector - summary action: Returns counts + current user's RSVP Selector : /apps/backend/apps/tournaments/selectors/event_rsvp.py - list_rsvps_for_event() : Filters by event_id, uses select_related("user") - get_rsvp_counts_for_event() : Aggregates counts with Django Count/Q Serializer : /apps/backend/apps/tournaments/serializers/event_rsvp.py - EventRSVPSerializer : Returns user details (id, first_name, last_name, display_name, avatar_url) ### API Endpoints GET /api/v1/tournaments/{event_pk}/rsvps/ - List all RSVPs for event GET /api/v1/tournaments/{event_pk}/rsvps/summary/ - Get counts + my RSVP POST /api/v1/tournaments/{event_pk}/rsvps/set/ - Set/update RSVP DELETE /api/v1/tournaments/{event_pk}/rsvps/remove/ - Remove RSVP ## Potential Root Causes API returning empty results - The /rsvps/ endpoint may not be returning RSVPs despite them existing in the database Multi-tenancy filtering - EventRSVP model uses BaseModel (not TenantModel), but the event itself is team-scoped; ensure RSVPs are correctly filtered by event Missing pagination handling - Frontend requests page_size=100 but API may be returning paginated response differently Response serialization issue - User data may not be properly nested in the response ## Investigation Steps Check browser Network tab for the /rsvps/ API response Verify RSVPs exist in database for the specific event (ID: 247c9324-7d03-42a3-b18b-e9d3dd4bab9e) Test the endpoint directly: GET /api/v1/tournaments/247c9324-7d03-42a3-b18b-e9d3dd4bab9e/rsvps/ Check if RSVPSummary counts are showing correctly (if yes, data exists) Inspect component render - check if data?.results is being accessed correctly ## Acceptance Criteria [ ] RSVPs are correctly fetched from the API when viewing event detail page [ ] Attendees card displays all team members who have RSVPed [ ] RSVPs are grouped correctly by response (Going, Maybe, Not Going) [ ] User avatars and display names render correctly [ ] RSVP notes display if provided [ ] Empty state shows "No responses yet" when no RSVPs exist ## Questions for Stakeholder Is the RSVP summary badge (showing "X going +Y maybe") working correctly? This would help narrow down frontend vs backend issue. Does this affect all events or only specific ones? Are the RSVP buttons (Yes/No/Maybe) working to set your own RSVP? ## Estimated Complexity S (Small) - Likely a data fetching or serialization issue that can be fixed with targeted debugging
2
·
complete
Add Formats Admin Page with CRUD Operations
## Description Add a new "Formats" management page to the admin section ( /admin/formats ) that allows team administrators to create, edit, activate/deactivate, and delete game formats for their team. This should mirror the existing Archetypes admin page ( /admin/archetypes ) in terms of functionality and UI patterns. Formats are team-scoped entities that define different ways to play a game (e.g., Standard, Modern, Legacy for MTG). Currently, formats can be managed via the API but lack a dedicated admin UI. ## Technical Context Backend (Already Complete): The Format model exists at /apps/backend/apps/games/models/format.py Full CRUD API is available via FormatViewSet at /apps/backend/apps/games/views/format.py API endpoints: GET/POST /api/v1/formats/ , GET/PUT/PATCH/DELETE /api/v1/formats/{id}/ Format model fields: name , slug (auto-generated), description , game (FK), is_active The model inherits from TenantModel (team-scoped) and ActivatableMixin Missing: The backend ViewSet does NOT have activate and deactivate custom actions (unlike ArchetypeViewSet) Frontend (To Be Created): Need new page at /apps/frontend/src/app/(app)/admin/formats/ Follow the established pattern from /apps/frontend/src/app/(app)/admin/archetypes/ ## Acceptance Criteria [ ] Backend: FormatViewSet has activate and deactivate POST actions [ ] Frontend: /admin/formats page renders with SSR prefetch [ ] Frontend: DataTable displays formats with name, game, description, status columns [ ] Frontend: "Create Format" dialog allows adding new formats [ ] Frontend: "Edit Format" dialog allows updating name and description [ ] Frontend: Activate/Deactivate toggle works with optimistic updates [ ] Frontend: Delete confirmation dialog works [ ] Frontend: Status filter (Active/Inactive/All) works [ ] Navigation: "Formats" tab appears in admin sub-nav [ ] Design System: Page follows gaming-inspired design (sharp corners, borders, neon colors) ## Questions for Stakeholder Permissions: Should formats management require a new formats.manage permission? Delete behavior: Should deleting a format be allowed if decks/archetypes are associated? Ordering in nav: Where should "Formats" appear in the admin sub-nav? ## Estimated Complexity M (Medium) - Follows established archetype pattern
2
·
complete
User Onboarding Flow
## Description Implement a user onboarding checklist that guides new users through essential first steps in Nexlog. ## Problem New users land on an empty dashboard with no guidance on what to do first. ## Onboarding Checklist (6 Steps) Complete your profile - Add display name and avatar Add your first format - Set up a game format (e.g., Modern, Standard) Add your first archetype - Create a deck archetype (e.g., Burn, Control) Create your first deck - Set up a deck to track matches Record your first match - Log a match result View your analytics - Check out your performance stats ## Why This Order? Steps follow data dependency: Profile, then Format and Archetype (needed for decks), then Deck (needed for matches), then Match (generates analytics data), then Analytics. ## Acceptance Criteria New users see onboarding checklist at top of dashboard Checklist shows 6 steps with titles and descriptions Progress bar shows completion percentage Users can dismiss the checklist Dismissed checklist can be re-enabled in Settings Steps show Skip button if team already has that data Clicking a step navigates to the relevant page Mobile: checklist is collapsible Progress persists across sessions ## Complexity Large - Backend model, signal handlers, frontend components, Settings integration ## Technical Implementation Backend Model (OnboardingProgress): user (OneToOne) is_dismissed (bool) profile_completed, profile_completed_at format_completed, format_completed_at archetype_completed, archetype_completed_at deck_completed, deck_completed_at match_completed, match_completed_at analytics_completed, analytics_completed_at completed_at (all steps done) API Endpoints: GET /api/v1/users/onboarding/me/ POST /api/v1/users/onboarding/complete-profile/ POST /api/v1/users/onboarding/complete-format/ POST /api/v1/users/onboarding/complete-archetype/ POST /api/v1/users/onboarding/complete-deck/ POST /api/v1/users/onboarding/complete-match/ POST /api/v1/users/onboarding/complete-analytics/ POST /api/v1/users/onboarding/skip-format/ POST /api/v1/users/onboarding/skip-archetype/ POST /api/v1/users/onboarding/dismiss/ POST /api/v1/users/onboarding/reset/ Signal Handlers: User save -> check profile completion Format create -> mark format step complete Archetype create -> mark archetype step complete Deck create -> mark deck step complete Match create -> mark match step complete Frontend Components: OnboardingChecklist (dashboard widget) OnboardingStepItem (individual step) useOnboarding hook (state management) Settings toggle for re-show ## Design Decisions (Finalized) Placement: Top of dashboard as full-width card Auto-complete: Steps show Skip button if team already has that data type Re-show after dismiss: Toggle in user Settings page Email reminders: None Celebration on completion: Tabled for future Achievements system: Tabled for future Mobile: Collapsible card, tap to expand Step click action: Navigate to relevant page Step content: Title plus short description Analytics completion: Auto-completes on page visit Welcome banner: Skip, checklist is sufficient Empty states: Standard, no onboarding-specific messaging Timestamps: Not visible to user, stored for analytics only
3
·
complete
Investigate Riftbound Data Support
## Description Investigate what additional Riftbound-specific data and features need to be supported in Nexlog. The attached screenshot shows some Riftbound-related data or UI that may need implementation. ## Current Riftbound Support Nexlog already has partial Riftbound support: Battlefield tracking on Game model ( our_battlefield , opponent_battlefield fields) Card autocomplete via Flexslot.gg API for Riftbound cards Conditional UI in match recording for Riftbound-specific fields ## Investigation Needed Review the attached screenshot to understand what specific data/feature is being requested Assess current gaps in Riftbound support Determine scope of work needed ## Potential Areas for Enhancement Based on Riftbound game mechanics, potential areas that may need work: Legend/Champion tracking Battlefield effects and interactions Riftbound-specific win conditions Format-specific deck validation rules Riftbound card metadata (if different from MTG) ## Next Steps Review the screenshot attachment Clarify requirements with stakeholder Create specific implementation tickets based on findings ## Type Spike/Investigation - This ticket requires research before implementation can be scoped ## Questions for Stakeholder What does the attached screenshot show? What specific feature is being requested? Are there Riftbound-specific statistics that need tracking? Do we need to support Riftbound-specific formats? Is the battlefield field currently being used in match recording UI? ## Estimated Complexity TBD - Depends on investigation findings. Could range from S (small UI tweak) to L (new game-specific features)
1
·
complete