Merge branch 'css-rework-sidenav' of https://github.com/sendou-ink/sendou.ink into css-rework-sidenav

This commit is contained in:
hfcRed 2026-03-04 19:43:02 +01:00
commit b979914396
114 changed files with 0 additions and 3979 deletions

View File

@ -1,6 +0,0 @@
beans:
path: .beans
prefix: sendou.ink-
id_length: 4
default_status: todo
default_type: task

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@ -1,19 +0,0 @@
---
# sendou.ink-0izp
title: Rename CommandPalette to GlobalSearch
status: todo
type: task
created_at: 2026-01-11T13:51:15Z
updated_at: 2026-01-11T13:51:15Z
parent: sendou.ink-6eko
---
The CommandPalette component is used more as a global search feature than a command palette. Rename it to better reflect its purpose.
## Checklist
- [ ] Rename `CommandPalette.tsx` to `GlobalSearch.tsx`
- [ ] Rename `CommandPalette.module.css` to `GlobalSearch.module.css`
- [ ] Update all CSS class names from `commandPalette` to `globalSearch`
- [ ] Update all imports and references across the codebase
- [ ] Run checks to ensure nothing is broken

View File

@ -1,25 +0,0 @@
---
# sendou.ink-0n0n
title: Search category icons not shown on mobile
status: completed
type: bug
priority: normal
created_at: 2026-01-11T13:53:16Z
updated_at: 2026-01-11T18:43:48Z
parent: sendou.ink-6eko
---
On narrow screens, the search/command palette category tabs (Users, Teams, Organizations, Tournaments) only show text labels without their icons. Icons should be visible on mobile to maintain visual consistency and improve scannability.
## Details
- The tabs currently show text-only on narrow screens
- Icons help users quickly identify categories
- Should match desktop behavior where icons are shown alongside text
## Fix
Added CSS in `CommandPalette.module.css`:
1. Added `flex-shrink: 0` to `picture` and `img` elements inside `.searchTypeRadio` to prevent icons from being compressed/hidden on narrow screens
2. Added `flex-wrap: wrap` to `.searchTypeRadioGroup` to allow tabs to wrap to a new line on very narrow screens instead of overflowing

View File

@ -1,11 +0,0 @@
---
# sendou.ink-0n4h
title: Add any tournament to My Events
status: draft
type: feature
created_at: 2026-01-11T12:55:45Z
updated_at: 2026-01-11T12:55:45Z
parent: sendou.ink-u4ag
---
Allow users to add any tournament to their My Events page, not just tournaments they are organizing or have joined as a participant. This enables spectators to keep track of tournaments they want to follow or watch.

View File

@ -1,43 +0,0 @@
---
# sendou.ink-0q9m
title: My Events page and components
status: todo
type: task
priority: normal
created_at: 2026-01-11T11:45:35Z
updated_at: 2026-01-11T12:50:56Z
parent: sendou.ink-om3i
---
## Summary
Build the `/my-events` page with list view and event card components.
## Details
**Page requirements:**
- Route: `/my-events`
- Login required (redirect if not authenticated)
- List view grouped by day (chronological)
- Empty state with links to /calendar and /scrims
**Event card info:**
- Name (tournament name or scrim opponent)
- Time
- Status
- Check-in window (for tournaments)
- Click navigates directly to tournament/scrim page
**Component sharing:**
- Build components that can be reused by sidebar Events section
- Handle both tournament and scrim event types
## Checklist
- [ ] Add route to routes.ts
- [ ] Create page component with loader
- [ ] Create event list component (grouped by day)
- [ ] Create event card component (handles tournament + scrim)
- [ ] Implement empty state with links
- [ ] Add translations
- [ ] Style with CSS modules

View File

@ -1,56 +0,0 @@
---
# sendou.ink-0sk2
title: Remove trusted users management UI from settings
status: todo
type: task
priority: normal
created_at: 2026-01-13T11:25:10Z
updated_at: 2026-01-13T11:25:15Z
parent: sendou.ink-255r
blocking:
- sendou.ink-kluy
---
## Overview
Delete the "Trusted Users" section from SendouQ settings page. This UI is replaced by the /friends page.
## Files to Modify
### app/features/sendouq-settings/routes/q.settings.tsx
- Remove `<TrustedUsers />` component usage (line ~86)
- Delete `TrustedUsers` function component (lines ~611-680)
### app/features/sendouq-settings/actions/q.settings.server.ts
- Remove `REMOVE_TRUST` case from switch (lines ~44-49)
### app/features/sendouq-settings/q-settings-schemas.server.ts
- Remove `REMOVE_TRUST` schema from union (lines ~68-71)
### app/features/sendouq-settings/loaders/q.settings.server.ts
- Remove `trusted` from loader data
### app/features/sendouq-settings/QSettingsRepository.server.ts
- Remove `findTrustedUsersByGiverId` function
- Remove `deleteTrustedUser` function
- (Keep other functions until full trust deprecation)
## i18n Keys to Remove
From `q` namespace:
- `q:settings.trusted.header`
- `q:settings.trusted.confirm`
- `q:settings.trusted.trustedExplanation`
- `q:settings.trusted.noTrustedExplanation`
- `q:settings.trusted.teamExplanation`
## Checklist
- [ ] Remove TrustedUsers component from q.settings.tsx
- [ ] Remove REMOVE_TRUST action handler
- [ ] Remove REMOVE_TRUST schema
- [ ] Remove trusted from loader
- [ ] Remove repository functions (findTrustedUsersByGiverId, deleteTrustedUser)
- [ ] Remove i18n keys
- [ ] Run `npm run i18n:sync`
- [ ] Verify settings page still works

View File

@ -1,55 +0,0 @@
---
# sendou.ink-0uyd
title: Database schema for friends system
status: completed
type: task
priority: normal
created_at: 2026-01-13T09:31:56Z
updated_at: 2026-01-14T19:14:51Z
parent: sendou.ink-255r
blocking:
- sendou.ink-iulp
---
## Overview
Create database migrations for the friends system tables.
## Tables to Create
### Friendship
```sql
CREATE TABLE Friendship (
id INTEGER PRIMARY KEY,
userOneId INTEGER NOT NULL REFERENCES User(id),
userTwoId INTEGER NOT NULL REFERENCES User(id),
createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
UNIQUE(userOneId, userTwoId)
);
CREATE INDEX idx_friendship_user_one ON Friendship(userOneId);
CREATE INDEX idx_friendship_user_two ON Friendship(userTwoId);
```
Invariant: userOneId < userTwoId to prevent duplicate relationships.
### FriendRequest
```sql
CREATE TABLE FriendRequest (
id INTEGER PRIMARY KEY,
senderId INTEGER NOT NULL REFERENCES User(id),
receiverId INTEGER NOT NULL REFERENCES User(id),
createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
UNIQUE(senderId, receiverId)
);
CREATE INDEX idx_friend_request_receiver ON FriendRequest(receiverId);
```
### SendouQ group changes
We also need to add support for invites in SQ without the user being in the queue. TBD what the table design looks like
## Checklist
- [x] Create migration file
- [x] Add tables to `app/db/tables.ts`
- [x] Run migration and verify schema
- [x] Add to test database

View File

@ -1,56 +0,0 @@
---
# sendou.ink-0ze4
title: Weapons pages
status: draft
type: epic
created_at: 2026-01-11T12:10:47Z
updated_at: 2026-01-11T12:10:47Z
---
## Summary
Individual weapon pages (`/weapons/<weapon-slug>`) with comprehensive weapon data, stats, and links to related content.
## Data Source
Parse weapon parameters from https://github.com/Leanny/splat3 into a format easy to use within sendou.ink.
## Features
### Parameter Comparison Table
- **Rows** = parameters (raw keys from Leanny's data, supports translation that can be filled out over time)
- **Columns** = weapons in same category (e.g., Jr page shows all shooters)
- **Current weapon pinned** in the table for easy comparison
- **Patch history inline**: each cell shows current value + indicator if changed, expandable to see full history (which patch + date)
### Leaderboard & Popularity Stats
- **Top XP holder** for the weapon (name + XP) with link to full leaderboard
- **Popularity** (top 500 appearances) - shown both:
- Overall across all weapons
- Within the weapon's category
- Data already exists in sendou.ink database
### Rich Previews (5 items each)
- **5 recent vods** of the weapon
- **5 popular builds** for the weapon
- **5 art pieces** tagged by the weapon
### Simple Links
- Free agents using this weapon
- Build analyzer link
- Object damage calculator link
## Navigation
- No landing page at `/weapons`
- No category pages
- Users access individual weapon pages directly via menu (weapon mega menu)
## Technical Notes
- Individual weapon pages already exist as placeholders (e.g., `/weapons/splattershot-jr`)
- Leaderboard and popularity data already available in database

View File

@ -1,34 +0,0 @@
---
# sendou.ink-10ai
title: Implement prestige calculation
status: todo
type: feature
created_at: 2026-01-12T09:21:18Z
updated_at: 2026-01-12T09:21:18Z
parent: sendou.ink-ylq5
---
Implement the tournament prestige calculation based on research findings.
## Requirements
- Calculate prestige from registered teams' seeding power
- Available at registration time (not just after results)
- Works for both upcoming and historical tournaments
## Implementation Notes
- Create function in /app/features/tournament/ or /app/features/mmr/
- Query top N teams' avgSeedingSkillOrdinal
- Apply formula from design task
- Return prestige score/tier
## Usage
- Called by stream aggregation service for ranking
- May be displayed on tournament pages (future)
- May be stored for historical tracking (future)
## Dependencies
- Prestige formula design (sendou.ink-XXX)

View File

@ -1,16 +0,0 @@
---
# sendou.ink-18y6
title: Inconsistent notification dot colors
status: completed
type: bug
priority: normal
created_at: 2026-01-11T19:22:01Z
updated_at: 2026-01-12T16:04:11Z
parent: sendou.ink-6eko
---
The notification dot color is inconsistent between:
- The notification bell / user menu tab indicator
- The dots shown in the actual notification popover
These should use consistent styling.

View File

@ -1,43 +0,0 @@
---
# sendou.ink-1kb8
title: Show times on mobile Events panel
status: completed
type: task
priority: normal
tags:
- my-events-epic
created_at: 2026-01-11T09:48:18Z
updated_at: 2026-01-11T13:24:31Z
parent: sendou.ink-6eko
---
## Summary
The mobile Events panel currently only shows tournament names without any time information. Add day headers and event times to help players know when things are happening.
## Current state
Panel shows a flat list of tournaments with just:
- Tournament logo
- Tournament name
## Proposed design
Group events by day with headers, then show time per event:
```
Today
3:00 PM PICNIC #2
7:00 PM In The Zone 22
Tomorrow
2:00 PM Paddling Pool 253
5:00 PM Swim or Sink 101
```
## Checklist
- [x] Add day header groupings (Today, Tomorrow, weekday names, or dates)
- [x] Show event start time next to each entry
- [x] Ensure times display in user's local timezone
- [x] Test with events spanning multiple days

View File

@ -1,34 +0,0 @@
---
# sendou.ink-1l22
title: CSS rework for modern practices and theming accessibility
status: in-progress
type: epic
priority: normal
created_at: 2026-01-11T09:33:26Z
updated_at: 2026-01-11T09:34:23Z
---
## Why
The existing CSS architecture needed modernization to support:
1. **Consistency** - Standardized design tokens and variable naming conventions
2. **Modern CSS practices** - Leveraging newer CSS features for better maintainability
3. **Accessibility for user-defined themes** - Making it easy for users to customize themes while maintaining readability and contrast requirements
## What's been done
- Redefined all styles in `vars.css` with a consistent naming convention
- Established design tokens for colors, spacing, typography, and other properties
- Created a foundation that supports user theme customization while preserving accessibility
## Goals
- All components use CSS variables from `vars.css` consistently
- Theme customization is straightforward and accessible
- Color contrast and other accessibility requirements are maintained across all themes
- Reduced CSS specificity conflicts and improved maintainability
## Completion criteria
All child tickets resolved and the new CSS variable system is fully adopted across the codebase.

View File

@ -1,28 +0,0 @@
---
# sendou.ink-1qfh
title: Fix notification button and indicator dot overlay
status: scrapped
type: bug
priority: normal
created_at: 2026-01-11T09:37:03Z
updated_at: 2026-01-11T12:53:25Z
parent: sendou.ink-6eko
---
## Problem
The notification bell icon and the unread indicator dot are displayed side by side instead of the dot being overlayed on top of the bell icon.
## Screenshot
The blue notification dot appears next to the bell icon rather than positioned as an overlay badge on the icon.
## Expected behavior
The notification indicator dot should be positioned as an overlay on the bell icon (typically top-right corner), following standard notification badge UI patterns.
## Acceptance criteria
- Notification dot is positioned as an overlay on the bell icon
- Dot placement follows common UI conventions (e.g., top-right corner)
- Works correctly on both desktop sidebar and mobile layouts

View File

@ -1,39 +0,0 @@
---
# sendou.ink-24f6
title: Analyze tournament seeding data distribution
status: todo
type: task
priority: normal
created_at: 2026-01-12T09:21:17Z
updated_at: 2026-01-12T09:21:23Z
parent: sendou.ink-ylq5
blocking:
- sendou.ink-h9ht
---
Research task: Analyze existing tournament data to understand seeding skill distributions.
## Goals
- Understand the range and distribution of avgSeedingSkillOrdinal values
- Identify what distinguishes 'stacked' tournaments from casual ones
- Find natural breakpoints for prestige tiers
- Compare team counts vs skill levels
## Data to Analyze
- All finalized tournaments (RANKED and UNRANKED)
- Top 8 teams' average seeding skill ordinal
- Distribution across different tournament sizes
## Output
- Report with findings
- Recommendations for baseline comparison value
- Suggested tier boundaries (if discrete tiers make sense)
## Implementation Notes
- Can use db-prod.sqlite3 for real data
- Write analysis script or SQL queries
- Consider visualizing distributions

View File

@ -1,128 +0,0 @@
---
# sendou.ink-255r
title: Friends feature
status: todo
type: epic
priority: normal
created_at: 2026-01-11T09:27:40Z
updated_at: 2026-01-13T09:31:29Z
---
## Overview
Friends functionality for sendou.ink, replacing the current "Trusted" system. Enables users to see their friends in one place and quickly find what they're looking for (SendouQ group, tournament).
## Core Concepts
### Relationship Model
- **Mutual friendship**: User A sends request → User B accepts → both are friends
- **Friends = Trusted**: Friendship automatically grants all current trust permissions (quick-add to groups, scrim pickups)
- **Soft limit**: ~200 friends maximum per user
### Display Priority Hierarchy
The sidebar and /friends page show users from multiple relationship sources, prioritized as:
1. **Explicit friends** (highest priority) - Users you've mutually friended
2. **Team members** - Members of teams you belong to
3. **Shared association members** (lowest priority) - Members of organizations/associations you share, including Plus server members
Within each tier, users with activity (SendouQ/tournaments) are shown first.
### Activity Visibility
- SendouQ group status (group count like "2/4")
- Tournament registration/participation
- No online/last seen tracking - just activities
- All connections see your activity (no privacy controls for MVP)
### Permission Model
- **Trusted permissions** (quick-add to groups without invite): Friends only
- **Quick invite** (send invite notification): All connections (friends, teammates, associations)
- Teammates and association members receive invite and must accept
### Migration from Trust System
- If A trusts B AND B trusts A → auto-create friendship
- One-way trust relationships → deleted
- TrustRelationship table eventually deprecated
## Features
### Friend Requests
- Send from: user profile page, invite link join flow (checkbox like current trust)
- Notifications: in-app only (notification bell/dropdown)
- States: pending outgoing, pending incoming, accepted, declined
### Sidebar Display
- Mixed list (no grouping by relationship type)
- Quota system: X slots for SendouQ activity, Y slots for tournament activity
- Priority: explicit friends → team members → association members
- Within each tier: active first, then most recent interaction
- Each entry shows: avatar, name, activity subtitle, badge
- "See all" link to /friends page
### /friends Page
- **Grouped sections** with headers (unlike sidebar)
- **Pending requests section**: incoming and outgoing requests
- **Friends section**: explicit friends with activity status
- **Teammates section**: team members (with team name context)
- **Connections section**: association/Plus members
- **Management**: unfriend, search/filter across all sections
- **Mutual friends**: show mutual friends count/list on profiles
### Quick Invite to SendouQ
- "Invite friend" button in SendouQ group lobby
- Can invite any connection (friends, teammates, associations)
- Sends notification to selected user(s)
- User can accept to join group directly
- Only friends can be directly added (trusted); others must accept invite
### Mutual Friends Display
- Show on user profiles
- Count of mutual friends
- Preview of mutual friend avatars/names
## Database Schema
### New Tables
```sql
-- Friendship (mutual relationship)
CREATE TABLE Friendship (
// ...
);
-- Invariant: userOneId < userTwoId to prevent duplicates
-- Friend Request
CREATE TABLE FriendRequest (
// ...
);
### Existing Tables Used
- `TeamMember` - for teammate relationships
- `AssociationMember` - for association members
- `PlusTier` - for Plus server membership
## Future Enhancements (out of scope)
- Private messaging between friends
- Friend activity feed (tournament placements, rank changes)
- Friend categories/groups
- Block list
- LFG status visible to friends
- Email notifications for friend requests
- Privacy controls per connection type
- Tournament team forming (see sendou.ink-5x6m)
## Completion Criteria
- [ ] Database schema and migrations created
- [ ] FriendRepository with all CRUD operations
- [ ] Friend request flow (send, accept, decline, cancel)
- [ ] Sidebar shows real friends/teammates/connections with activity
- [ ] /friends page with grouped sections and management
- [ ] Profile page friend button and mutual friends
- [ ] Quick invite to SendouQ group (all connections)
- [ ] Group invite accept/decline flow for non-friends
- [ ] Migration script for trust → friendship
- [ ] Update all trust-dependent features to use friendship
- [ ] Remove/deprecate TrustRelationship table
- [ ] Tests for friend operations

View File

@ -1,12 +0,0 @@
---
# sendou.ink-2oby
title: Fix handleRadioGroupKeyDown in CommandPalette
status: completed
type: bug
priority: normal
created_at: 2026-01-11T08:58:55Z
updated_at: 2026-01-13T18:49:27Z
parent: sendou.ink-6eko
---
The handleRadioGroupKeyDown function is not working. Location: app/components/layout/CommandPalette.tsx:151

View File

@ -1,16 +0,0 @@
---
# sendou.ink-368p
title: Measure and optimize weapon params JSON performance
status: todo
type: task
created_at: 2026-01-13T15:23:48Z
updated_at: 2026-01-13T15:23:48Z
parent: sendou.ink-0ze4
---
The weapon params are loaded from a large JSON file and calculated at request time. Tasks:
## Checklist
- [ ] Measure the current performance penalty of loading/parsing the big JSON at request time
- [ ] Investigate if caching or pre-computation could improve performance
- [ ] Implement optimizations if the perf impact is significant

View File

@ -1,11 +0,0 @@
---
# sendou.ink-36dp
title: Check if pagination styles can be improved
status: completed
type: task
created_at: 2026-01-11T12:35:22Z
updated_at: 2026-01-11T12:35:22Z
parent: sendou.ink-1l22
---
Review the current pagination component styling and identify potential improvements that align with the CSS rework goals (modern practices, theming, accessibility).

View File

@ -1,29 +0,0 @@
---
# sendou.ink-36pm
title: Add weapon mega menu to mobile design
status: completed
type: task
priority: normal
created_at: 2026-01-11T09:31:09Z
updated_at: 2026-01-11T18:58:39Z
parent: sendou.ink-6eko
---
## Problem
The desktop layout has a weapon mega menu in the top navigation bar that allows users to quickly access weapon-specific pages. This menu is missing from the mobile design, which uses bottom tab navigation instead.
## Context
Mobile users currently lack quick access to weapon-related content that desktop users have via the top menu bar category dropdowns.
## Possible approaches
- Add weapon navigation to the mobile menu modal
- Create a dedicated weapons section accessible from mobile tabs
- Integrate weapon categories into the existing mobile menu structure
## Acceptance criteria
- Mobile users can access the same weapon navigation options as desktop users
- Navigation is intuitive and follows the established mobile design patterns

View File

@ -1,22 +0,0 @@
---
# sendou.ink-3hgl
title: Fix black background on date/time inputs in Chrome
status: todo
type: bug
created_at: 2026-01-11T12:36:28Z
updated_at: 2026-01-11T12:36:28Z
parent: sendou.ink-1l22
---
Some native input types (time, datetime-local, week, month) display with black backgrounds or styling issues in Chrome. Need to investigate the root cause and apply a consistent fix across all affected input types.
## Details
- Affected inputs: time, datetime-local, week, month (and possibly others)
- Browser: Chrome
- The icons and placeholder text appear correctly but there seems to be a color scheme conflict
## Checklist
- [ ] Identify which CSS rules cause the black background
- [ ] Check if it's related to color-scheme property or browser defaults
- [ ] Apply a fix that works for all affected input types at once
- [ ] Test in Chrome and other browsers

View File

@ -1,123 +0,0 @@
---
# sendou.ink-3k3u
title: Friend request API routes
status: todo
type: task
priority: normal
created_at: 2026-01-13T09:32:19Z
updated_at: 2026-01-13T10:18:25Z
parent: sendou.ink-255r
blocking:
- sendou.ink-f1rm
---
## Overview
Implement action handlers for friend request operations following the codebase's action pattern.
## File
`app/features/friends/actions/friends.ts` (same route as /friends page)
## Action Pattern
Uses `_action` field in form submissions with switch case:
```typescript
export const action: ActionFunction = async ({ request }) => {
const user = await requireUser(request);
const data = await parseRequestPayload({ request });
switch (data._action) {
case "SEND_REQUEST": {
// Send friend request
// Validate: not already friends, no pending request, under limit
// Create FriendRequest record
// Create notification for receiver
return json({ success: true });
}
case "ACCEPT_REQUEST": {
// Accept incoming friend request
// Validate: request exists, user is receiver
// Delete FriendRequest, create Friendship
// Create notification for sender
return json({ success: true });
}
case "DECLINE_REQUEST": {
// Decline incoming friend request
// Validate: request exists, user is receiver
// Delete FriendRequest
return json({ success: true });
}
case "CANCEL_REQUEST": {
// Cancel outgoing friend request
// Validate: request exists, user is sender
// Delete FriendRequest
return json({ success: true });
}
case "UNFRIEND": {
// Remove friendship
// Validate: friendship exists
// Delete Friendship record
return json({ success: true });
}
default:
throw new Response("Invalid action", { status: 400 });
}
};
```
## Form Data
```typescript
// SEND_REQUEST
{ _action: "SEND_REQUEST", targetUserId: number }
// ACCEPT_REQUEST
{ _action: "ACCEPT_REQUEST", requestId: number }
// or
{ _action: "ACCEPT_REQUEST", senderId: number }
// DECLINE_REQUEST
{ _action: "DECLINE_REQUEST", requestId: number }
// CANCEL_REQUEST
{ _action: "CANCEL_REQUEST", requestId: number }
// UNFRIEND
{ _action: "UNFRIEND", friendId: number }
```
## Validation
- `SEND_REQUEST`:
- Target user exists
- Not already friends
- No pending request in either direction
- Sender under friend limit (~200)
- `ACCEPT_REQUEST`:
- Request exists
- Current user is the receiver
- Both users under friend limit
- `UNFRIEND`:
- Friendship exists
- Current user is part of the friendship
## Notifications
- `SEND_REQUEST` → notify receiver "X sent you a friend request"
- `ACCEPT_REQUEST` → notify original sender "X accepted your friend request"
## Checklist
- [ ] Add action handler to friends.tsx
- [ ] SEND_REQUEST action with validation
- [ ] ACCEPT_REQUEST action
- [ ] DECLINE_REQUEST action
- [ ] CANCEL_REQUEST action
- [ ] UNFRIEND action
- [ ] Integration with notification system
- [ ] Error handling and user feedback
- [ ] Tests for each action

View File

@ -1,46 +0,0 @@
---
# sendou.ink-3qrp
title: Improve mobile menu grid alignment and prevent label wrapping
status: completed
type: bug
priority: normal
created_at: 2026-01-11T09:42:29Z
updated_at: 2026-01-11T18:31:05Z
parent: sendou.ink-6eko
---
## Problem
On mobile, the menu grid has alignment issues and some page labels are breaking into two or three lines, creating an inconsistent and cluttered appearance.
## Screenshot
Labels breaking to multiple lines:
- "Build Analyzer" (2 lines)
- "User Search" (2 lines)
- "Top Search" (2 lines)
- "Tier List Maker" (3 lines)
- "Plus Server" (2 lines)
- "Leaderboards" wrapping
Meanwhile other labels like "Settings", "SendouQ", "Builds", "Scrims", "LFG" etc. fit on one line.
## Expected behavior
- Menu items should be consistently aligned
- Labels should ideally fit on a single line
- If wrapping is unavoidable, it should be limited to two lines maximum
## Possible solutions
- Reduce number of columns to give more horizontal space per item
- Use shorter label names where possible
- Adjust font size for labels
- Use text truncation with ellipsis for long labels
## Acceptance criteria
- Menu grid is visually balanced and aligned
- No labels break to three lines
- Minimize two-line labels where possible
- Consistent spacing between menu items

View File

@ -1,50 +0,0 @@
---
# sendou.ink-4m0t
title: Add STREAM_CURATOR role to permissions
status: todo
type: task
priority: normal
created_at: 2026-01-12T09:20:30Z
updated_at: 2026-01-12T09:21:32Z
parent: sendou.ink-r6ry
blocking:
- sendou.ink-x535
---
Add new STREAM_CURATOR permission role to allow users to add stream links to calendar events.
## Requirements
- New role in existing permissions system
- Granted by staff (same flow as 'is artist' role)
- Allows adding stream link(s) to calendar events user created
- Scoped to own events only
## Checklist
- [ ] Create migration to add `isStreamCurator` column to User table (Boolean, default 0)
- [ ] Add `"STREAM_CURATOR"` to Role type in `/app/modules/permissions/types.ts`
- [ ] Add mapping in `/app/modules/permissions/mapper.server.ts` userRoles function: `if (user.isStreamCurator) roles.push("STREAM_CURATOR")`
- [ ] Add `makeStreamCuratorByUserId(userId)` function in `/app/features/admin/AdminRepository.server.ts`
- [ ] Add `"STREAM_CURATOR"` action case in `/app/features/admin/actions/admin.server.ts`
- [ ] Add `GiveStreamCurator` component in `/app/features/admin/routes/admin.tsx` (visible to staff)
## Implementation Pattern
Follow existing pattern from `isArtist`:
```typescript
// AdminRepository.server.ts
export function makeStreamCuratorByUserId(userId: number) {
return db
.updateTable("User")
.set({ isStreamCurator: 1 })
.where("User.id", "=", userId)
.execute();
}
```
```typescript
// admin.tsx - add to AdminActions, visible to isStaff
{isStaff ? <GiveStreamCurator /> : null}
```

View File

@ -1,23 +0,0 @@
---
# sendou.ink-5d8q
title: Show notification dot on mobile You tab
status: completed
type: bug
priority: normal
created_at: 2026-01-11T13:06:55Z
updated_at: 2026-01-11T19:13:22Z
parent: 6eko
---
## Problem
The desktop sidebar shows a notification indicator dot when there are unseen notifications, but the mobile "You" tab in the bottom navigation does not have this indicator.
## Expected behavior
The mobile "You" tab should display a notification dot when there are unseen notifications, matching the desktop behavior.
## Location
- Mobile tab bar: `app/components/MobileNav.tsx` (MobileTabBar component)
- Desktop notification indicator for reference: `app/components/layout/index.tsx`

View File

@ -1,25 +0,0 @@
---
# sendou.ink-5x6m
title: Tournament team forming
status: draft
type: epic
created_at: 2026-01-13T06:49:55Z
updated_at: 2026-01-13T06:49:55Z
---
## Overview
Reuse the `SendouQ` class to allow for SendouQ-like UI to create tournaments teams.
## Context
- Depends on: Friends feature (sendou.ink-255r)
## Potential Features
- Can join the "queue" either solo or with a team
- When registering solo, toggle whether they want to stay around as a sub if doesn't have time to form a team before reg closes
## Notes
This is a placeholder epic. Scope to be defined after Friends feature is complete.

View File

@ -1,38 +0,0 @@
---
# sendou.ink-5ysz
title: Add weapon parameter translations to i18n
status: todo
type: task
created_at: 2026-01-11T12:22:15Z
updated_at: 2026-01-11T12:22:15Z
parent: sendou.ink-0ze4
---
## Summary
Add weapon parameter keys to the existing i18n translation system so raw parameter names can be displayed in human-readable form.
## Requirements
- Add new translation namespace or extend existing `weapons` namespace
- Map raw Leanny parameter keys to readable names
- Initially can be partial - untranslated keys display as-is
- Support incremental addition of translations over time
## Example
```json
{
"params": {
"DamageParam_ValueMax": "Max Damage",
"DamageParam_ValueMin": "Min Damage",
"MoveSpeed": "Movement Speed"
}
}
```
## Technical Notes
- Run `npm run i18n:sync` after adding new English translations
- Keys should match exactly what comes from Leanny's data
- We should have a way to know in the table which keys have translation and which don't for conditional rendering

View File

@ -1,41 +0,0 @@
---
# sendou.ink-6eko
title: Desktop sidebar and mobile tabs layout
status: in-progress
type: epic
priority: normal
created_at: 2026-01-11T08:58:29Z
updated_at: 2026-01-11T09:34:23Z
blocking:
- sendou.ink-1l22
---
## Why
The previous layout had several limitations holding back the player experience:
1. **Poor mobile UX** - Navigation wasn't optimized for mobile players
2. **Hard to navigate** - No global search, sidebar only appeared on front page making it difficult to find features
3. **Slow path to matches** - Competitive players had too many clicks to join matches or check their tournament status
4. **No foundation for scheduling** - Future features like team scheduling and league match scheduling need a persistent navigation structure
5. **Streams not visible** - Community streams weren't highlighted, missing opportunity to showcase active players
## Goals
- Make it easy for competitive players to get into matches fast (1-click access to active matches/tournaments)
- Provide persistent navigation across all pages via sidebar (desktop) and bottom tabs (mobile)
- Add global search via command palette to quickly find users, teams, organizations, and tournaments
- Lay foundation for upcoming features: friend group quick-join, team scheduling, league scheduling
- Better highlight community content like streams
## What's been built
- **Desktop**: Fixed sidebar with user profile, tournament calendar, friends list, streams + top menu bar with category dropdowns
- **Mobile**: Bottom tab bar with modal panels for menu, friends, tournaments, and user profile
- **Command palette**: Global search (Cmd/Ctrl+K) for users, teams, organizations, tournaments
- **Breadcrumb navigation**: Context-aware page hierarchy
- **Quick match access**: Tournament and SendouQ match status prominently displayed
## Completion criteria
All child tickets resolved.

View File

@ -1,31 +0,0 @@
---
# sendou.ink-6i41
title: Move Settings to same row as user avatar in mobile user tab
status: completed
type: bug
priority: normal
created_at: 2026-01-11T09:40:48Z
updated_at: 2026-01-11T13:11:56Z
parent: sendou.ink-6eko
---
## Problem
On mobile, in the user tab modal, the Settings option is positioned at the bottom of the panel. It should instead be on the same row as the user avatar and name.
## Current behavior
- User avatar + name (Sendou) on one row
- Notifications section
- Settings at the bottom (separate location)
## Expected behavior
- User avatar + name + Settings on the same row
- Notifications section below
## Acceptance criteria
- Settings button/link is positioned on the same row as the user avatar and name
- Layout is balanced and visually clean
- Settings remains easily accessible

View File

@ -1,33 +0,0 @@
---
# sendou.ink-7omz
title: Try stage banner style for tournament matches
status: todo
type: task
created_at: 2026-01-11T19:25:36Z
updated_at: 2026-01-11T19:25:36Z
parent: sendou.ink-1l22
---
## Description
Experiment with using "stage banner" style images for tournament match displays instead of the regular sized stage images. Stage banner images are available in Lean's splat3 assets repository and might provide a better visual appearance.
## Context
Currently, tournament matches use regular stage images (`stageImageUrl`) as background for the `FancyStageBanner` component (in `app/features/tournament-bracket/components/StartedMatch.tsx`). The banner dimensions are 10rem tall with `background-size: cover`.
Lean's repository may have wider/more panoramic stage banners that could better fit this banner-style layout without as much cropping.
## Tasks
- [ ] Locate stage banner images from Lean's splat3 repository
- [ ] Compare visual appearance with current stage images
- [ ] If they look better, add banner images to static assets
- [ ] Update `stageImageUrl` or create new `stageBannerUrl` utility
- [ ] Update `FancyStageBanner` component to use banner images
## Files
- `app/features/tournament-bracket/components/StartedMatch.tsx:290-292` - stageNameToBannerImageUrl function
- `app/features/tournament-bracket/tournament-bracket.module.css:57-71` - stageBanner styling
- `app/utils/urls.ts:493-494` - stageImageUrl function

View File

@ -1,37 +0,0 @@
---
# sendou.ink-8vei
title: Create weapon stats summary component
status: todo
type: task
created_at: 2026-01-11T12:22:34Z
updated_at: 2026-01-11T12:22:34Z
parent: sendou.ink-0ze4
---
## Summary
Create a React component that displays weapon leaderboard and popularity statistics.
## Features
- **Top XP holder** - Player name + XP value, links to full leaderboard
- **Popularity stats** - Top 500 appearances shown in two contexts:
- Overall rank across all weapons
- Rank within the weapon's category
## Data Sources
- XP data: `XPLeaderboard.server.ts` - `weaponXPLeaderboard(weaponId)`
- Popularity: `XRankPlacement` table queries
## Props
- `topPlayer` - { name, xp, playerId }
- `popularity` - { overallRank, categoryRank, appearances }
- `weaponId` - for leaderboard link
## Technical Notes
- Use existing player link components if available
- Leaderboard link goes to `/leaderboards?type=XP-WEAPON-{weaponId}`
- Caching for performance?

View File

@ -1,72 +0,0 @@
---
# sendou.ink-9cpl
title: Create /streams page route and basic layout
status: todo
type: feature
priority: normal
created_at: 2026-01-12T09:19:55Z
updated_at: 2026-01-12T12:07:16Z
parent: sendou.ink-r6ry
blocking:
- sendou.ink-kph0
- sendou.ink-u6b2
- sendou.ink-ae15
---
Create the new /streams page that will serve as the central hub for all community streams.
## Requirements
- Add route at /streams
- Two-section layout: Live streams and Upcoming streams
- Desktop: Single column (can iterate on two-column later)
- Mobile: Single column stacked layout
- Support query params for filtering
## Query Params
- `?source=sendouq` - Filter to only SendouQ streams
- `?source=tournament` - Filter to only tournament streams
- `?tournament=:id` - Filter to specific tournament (for redirect from `/to/:id/streams`)
## Checklist
- [ ] Add route to `routes.ts`: `route("/streams", "features/streams/routes/streams.tsx")`
- [ ] Create `/app/features/streams/routes/streams.tsx`
- [ ] Create loader that calls aggregation service
- [ ] Create stream card component
- [ ] Implement "Live" section with live streams
- [ ] Implement "Upcoming" section with upcoming calendar events
- [ ] Add empty state for when no streams are live
- [ ] Add filter tabs/dropdown for source filtering
- [ ] Add i18n translations
## Stream Card Design
Each stream card should show:
- Thumbnail/avatar image
- Stream title or streamer name
- Source indicator (SendouQ badge, Tournament name, Event name)
- Subtitle (round name, match info, scheduled time)
- LIVE badge or scheduled time
- Click to expand Twitch embed (if Twitch)
- External link for non-Twitch streams
## Empty State
When no streams are live:
- Show friendly message: "No live streams right now"
- Show upcoming streams section more prominently
- Consider showing recent VODs (future enhancement)
## Implementation Notes
- Create in `/app/features/streams/` folder
- Loader calls aggregation service (sendou.ink-nw8b)
- Twitch streams can embed inline (sendou.ink-r717)
- Non-Twitch streams (YouTube, etc.) open in new tab
## Dependencies
- sendou.ink-nw8b: Stream aggregation service (for data)
- sendou.ink-r717: Twitch embed component (for playback)

View File

@ -1,45 +0,0 @@
---
# sendou.ink-a9gl
title: Add stream links field to CalendarEvent
status: todo
type: task
priority: normal
created_at: 2026-01-12T09:20:30Z
updated_at: 2026-01-12T09:21:32Z
parent: sendou.ink-r6ry
blocking:
- sendou.ink-x535
---
Add database table for storing stream links associated with calendar events.
## Requirements
- Support multiple stream links per event (Twitch, YouTube, etc.)
- Links are just URLs - platform-agnostic
- Only STREAM_CURATOR users can add links to their own events
## Data Model
New join table `CalendarEventStream`:
| Column | Type | Description |
|--------|------|-------------|
| id | number | Primary key |
| calendarEventId | number | FK to CalendarEvent |
| url | string | Full URL to stream (e.g., `https://twitch.tv/username`) |
## Checklist
- [ ] Create migration `0XXX-calendar-event-stream.ts` with CalendarEventStream table
- [ ] Add `CalendarEventStream` interface to `/app/db/tables.ts`
- [ ] Add TypeScript types in calendar feature types file
- [ ] Create repository functions: `addStreamToEvent`, `removeStreamFromEvent`, `getStreamsForEvent`
## Implementation Notes
- Keep it simple: just store the URL as-is
- No validation against external APIs (just basic URL format check)
- Display can extract platform from URL (twitch.tv, youtube.com, etc.) if needed for icon
- First version only support Twitch and YouTube links/embeds, might be expanded later (so store the full stream URL to the DB)
- Max links per event: consider a reasonable limit (e.g., 5)

View File

@ -1,55 +0,0 @@
---
# sendou.ink-ae15
title: Add /streams link to sidebar and mobile menu
status: todo
type: task
created_at: 2026-01-12T09:20:56Z
updated_at: 2026-01-12T09:20:56Z
parent: sendou.ink-r6ry
---
Add navigation link to the new /streams page.
## Requirements
- Add link in sidebar streams section (below the top 3 streams)
- Add to mobile menu in appropriate location
- Link text: "All streams" (or localized equivalent)
## Checklist
- [ ] Add link in desktop sidebar after stream cards (`/app/components/layout/index.tsx`)
- [ ] Add link in mobile menu streams section (`/app/components/MobileNav.tsx`)
- [ ] Add i18n translation key: `common:nav.allStreams` = "All streams"
- [ ] Run `npm run i18n:sync` after adding translation
- [ ] Style link consistently with other section links
## Implementation
Desktop sidebar (in layout/index.tsx):
```tsx
<SideNavHeader icon={<TwitchIcon />}>Streams</SideNavHeader>
{streams.map(stream => <SideNavLink ... />)}
<Link to="/streams" className="...">All streams →</Link>
```
Mobile menu (in MobileNav.tsx):
```tsx
// In MenuOverlay streams section
{streams.map(stream => ...)}
<Link to="/streams">All streams</Link>
```
OR
inline link (the header becomes link) prompt user for resolution (TBD)
## Notes
- Should appear after the stream cards, not before
- Use right arrow or chevron icon to indicate "see more"
- Same pattern will be used for "Events" and "Friends" sections later
## Dependencies
- sendou.ink-9cpl: /streams page must exist first

View File

@ -1,35 +0,0 @@
---
# sendou.ink-aq1e
title: Create weapon art preview component
status: completed
type: task
priority: normal
created_at: 2026-01-11T12:22:54Z
updated_at: 2026-01-14T13:48:27Z
parent: sendou.ink-0ze4
---
## Summary
Create a React component that displays 5 art pieces tagged with the weapon.
## Features
- Show 5 art pieces tagged with the weapon's slug
- Each art shows: thumbnail, artist name
- "View all" link to art page filtered by weapon tag
## Data Source
- `ArtRepository` with tag filter
- Tag = weapon slug (canonical tag, e.g., "splattershot-jr")
## Props
- `artPieces` - array of 5 art objects
- `weaponSlug` - for tag filtering and "view all" link
## Technical Notes
- Art uses existing tag system - weapon slug is the canonical tag
- Reuse existing art display components if available

View File

@ -1,13 +0,0 @@
---
# sendou.ink-bp4w
title: Replace trusted quick invite with friends quick invite
status: todo
type: task
created_at: 2026-01-13T09:33:09Z
updated_at: 2026-01-13T09:33:09Z
parent: sendou.ink-255r
---
## Overview
Anywhere where user can currently quick invite their trusted users, now same can be done for friends (SendouQ, tournaments)

View File

@ -1,32 +0,0 @@
---
# sendou.ink-bti2
title: Fix duplicate Notifications header in mobile user tab
status: completed
type: bug
priority: normal
created_at: 2026-01-11T09:39:53Z
updated_at: 2026-01-11T13:02:44Z
parent: sendou.ink-6eko
---
## Problem
On mobile, the user tab modal shows the "Notifications" header twice in a row - once as a section header and again immediately below it with a refresh button.
## Screenshot
The "You" modal displays:
- User profile (Sendou)
- 🔔 Notifications (first instance)
- 🔔 Notifications + refresh button (second instance)
- Notification list items
## Expected behavior
The Notifications header should only appear once.
## Acceptance criteria
- Only one Notifications header is displayed in the mobile user tab
- Refresh functionality is preserved
- Layout remains clean and properly structured

View File

@ -1,11 +0,0 @@
---
# sendou.ink-bxxe
title: Improve expanded row border alignment style
status: todo
type: task
created_at: 2026-01-13T15:23:48Z
updated_at: 2026-01-13T15:23:48Z
parent: sendou.ink-0ze4
---
Review the expanded row styling in the weapons page params table and see if the border alignment can be improved for a cleaner visual appearance.

View File

@ -1,11 +0,0 @@
---
# sendou.ink-cml6
title: Investigate visual regression test failures
status: completed
type: task
priority: normal
created_at: 2026-01-11T15:06:10Z
updated_at: 2026-01-11T15:09:14Z
---
Investigate visual regression test failures and update fixtures if they seem expected based on the current css-rework-sidenav branch changes.

View File

@ -1,43 +0,0 @@
---
# sendou.ink-d6vh
title: My Events data layer
status: todo
type: task
priority: normal
created_at: 2026-01-11T11:45:35Z
updated_at: 2026-01-11T12:51:07Z
parent: sendou.ink-om3i
---
## Summary
Create the data fetching layer for the My Events page that combines tournaments and scrims into a unified event list.
## Details
**Data sources to combine:**
- Tournaments user is registered for
- Tournaments user is organizing
- Scrims with scheduled matches
- Scrims in looking-for-match state
**Output:**
- Single sorted list of events (chronological)
- Each event needs: name, time, status, type (tournament/scrim)
- Tournament events need: check-in window info, handle two-day tournaments
- Filter to upcoming events only (no past)
## Technical approach
- Create loader at `/my-events` route
- Repository functions to fetch user tournaments and scrims
- Merge and sort by start time
- Design data structure that works for both page and sidebar (shared)
## Checklist
- [ ] Create repository function for user's upcoming tournaments
- [ ] Create repository function for user's upcoming scrims
- [ ] Create unified event type/interface
- [ ] Create loader that combines and sorts events
- [ ] Verify data structure works for sidebar consumption

View File

@ -1,21 +0,0 @@
---
# sendou.ink-eb17
title: Add ad slots to new layout
status: draft
type: task
created_at: 2026-01-11T09:29:25Z
updated_at: 2026-01-11T09:29:25Z
parent: sendou.ink-6eko
---
## Task
Define and implement ad slot placements for the new sidebar/mobile layout.
## To Define
- Where should ads appear? (sidebar, content area, mobile panels?)
- Ad sizes and formats
- How do ads interact with sidebar collapse?
- Mobile-specific ad placements
- Integration with existing ad provider

View File

@ -1,11 +0,0 @@
---
# sendou.ink-ekv0
title: Collapse sidenav button flashes on mobile page load
status: completed
type: bug
priority: normal
created_at: 2026-01-15T19:16:35Z
updated_at: 2026-01-17T06:04:49Z
---
The collapse sidenav button should not be visible on mobile at all, but it currently flashes briefly during page load before being hidden. It should not show even for a fraction of a second.

View File

@ -1,87 +0,0 @@
---
# sendou.ink-f1rm
title: Notifications with action buttons
status: todo
type: task
created_at: 2026-01-13T10:13:18Z
updated_at: 2026-01-13T10:13:18Z
parent: sendou.ink-255r
---
## Overview
Extend the notification system to support inline action buttons. First use case: accept/decline friend requests directly from notification dropdown.
## Current State
Notifications are display-only - users must navigate to another page to take action.
## Requirements
### Generic Action Support
Notifications should support optional action buttons:
- Primary action (e.g., "Accept")
- Secondary action (e.g., "Decline")
- Actions trigger API calls without page navigation
- Notification updates/dismisses after action
### Friend Request Actions
First implementation:
- Friend request notification shows "Accept" and "Decline" buttons
- Accept → creates friendship, dismisses notification
- Decline → deletes request, dismisses notification
## Database Changes
Extend notification schema (if needed):
```sql
-- Option A: Store action metadata in notification
ALTER TABLE Notification ADD COLUMN actionType TEXT;
ALTER TABLE Notification ADD COLUMN actionData TEXT; -- JSON
-- Option B: Derive actions from notification type
-- No schema change, handle in code based on notification type
```
## UI Components
### NotificationItem
- Conditionally render action buttons based on notification type
- Loading state during action
- Success/error feedback
- Optimistic UI update
### Action Types (extensible)
```typescript
type NotificationAction = {
type: "FRIEND_REQUEST";
actions: ["accept", "decline"];
} |
// not part of this task, but for future can also be group_invite etc.
{
type: "GROUP_INVITE";
actions: ["accept", "decline"];
} // ... future types
```
## API
New /notifications actions route.
## Future Use Cases
- Group invites (accept/decline)
- Tournament team invites
- Match ready check
- Scrim requests
## Checklist
- [ ] Design notification action data model
- [ ] Update NotificationItem component with action buttons
- [ ] Action API endpoint or reuse existing endpoints
- [ ] Loading/success/error states
- [ ] Implement for friend requests
- [ ] Update notification after action (dismiss or mark complete)
- [ ] Mobile-friendly action buttons
- [ ] Tests for notification actions

View File

@ -1,19 +0,0 @@
---
# sendou.ink-g01q
title: Explore more comfortable way to close mobile tab menu panels
status: todo
type: task
created_at: 2026-01-15T19:19:30Z
updated_at: 2026-01-15T19:19:30Z
---
The X button to close mobile tab menu panels is positioned far out of reach, making it uncomfortable to use.
## Potential solution
React Aria's Sheet component might be a good approach: https://react-aria.adobe.com/examples/sheet
This would allow swipe-to-dismiss gestures which are more ergonomic on mobile.
## Trade-offs
- Pro: Better UX with swipe gestures
- Con: Requires pulling in a new dependency (Motion)

View File

@ -1,50 +0,0 @@
---
# sendou.ink-gdrp
title: Update trust-dependent features to use friendship
status: todo
type: task
created_at: 2026-01-13T09:33:32Z
updated_at: 2026-01-13T09:33:32Z
parent: sendou.ink-255r
---
## Overview
Update all code that uses TrustRelationship to use Friendship instead.
## Files to Update
### SendouQ Group Adding
- `app/features/sendouq/actions/q.preparing.server.ts` - ADD_TRUSTED action
- `app/features/sendouq/components/MemberAdder.tsx` - Uses useTrusted hook
- `app/hooks/swr.ts` - useTrusted() hook → useFriends()
- `app/features/sendouq/routes/trusters.ts` - Trusters API → Friends API
### Tournament Join
- `app/features/tournament/actions/to.$id.join.server.ts` - Trust checkbox on join
- `app/features/tournament/loaders/to.$id.register.server.ts` - trusterPlayers query
- Change "trust" checkbox to "add as friend" checkbox
### Scrim Pickups
- `app/features/scrims/actions/scrims.new.server.ts` - Validates trusted for pickup
- Update to check friendship instead
### Settings
- `app/features/sendouq-settings/routes/q.settings.tsx` - Trusted users list
- `app/features/sendouq-settings/QSettingsRepository.server.ts` - Trust queries
- Replace with friends list/management
### Cleanup Routine
- `app/routines/deleteOldTrusts.ts` - Delete after migration complete
## Checklist
- [ ] Update SendouQ ADD_TRUSTED to use friends
- [ ] Update MemberAdder component
- [ ] Replace useTrusted hook with useFriends
- [ ] Update tournament join flow
- [ ] Update scrim pickup validation
- [ ] Update Q settings page
- [ ] Update or remove trust cleanup routine
- [ ] Search codebase for remaining TrustRelationship references
- [ ] E2E tests for updated flows

View File

@ -1,34 +0,0 @@
---
# sendou.ink-gjie
title: Create parameter comparison table component
status: completed
type: task
priority: normal
created_at: 2026-01-11T12:22:15Z
updated_at: 2026-01-13T12:21:54Z
parent: sendou.ink-0ze4
---
## Summary
Create a React component that displays weapon parameters in a comparison table format. Use `Table` component
## Design
- **Rows** = parameters (raw keys, translated via i18n if available)
- **Columns** = weapons in same category (e.g., all shooters)
- **Current weapon pinned** - always visible, visually highlighted
- **Patch history inline** - cells show current value + indicator if changed, expandable to see full history (patch + date)
## Props
- `weaponId` - current weapon
- `categoryWeapons` - list of weapons in same category
- `params` - parameter data with patch history
## Technical Notes
- Use CSS modules for styling
- Consider horizontal scroll for many weapons
- Expandable rows for patch history (click to expand)
- Changed values should have visual indicator (icon or color)

View File

@ -1,11 +0,0 @@
---
# sendou.ink-gk34
title: Improve tournament search relevance
status: todo
type: task
created_at: 2026-01-11T08:58:56Z
updated_at: 2026-01-11T08:58:56Z
parent: sendou.ink-6eko
---
For tournament search results, when there are equally good matches, show tournaments closest to current date first. Example: searching 'In The Zone' should show newest tournament first. Location: app/features/search/routes/search.ts:86

View File

@ -1,11 +0,0 @@
---
# sendou.ink-govi
title: Load test sidenav
status: todo
type: task
created_at: 2026-01-11T12:28:34Z
updated_at: 2026-01-11T12:28:34Z
parent: sendou.ink-6eko
---
Perform load tests to ensure the sidenav performs well under various conditions and with different data volumes.

View File

@ -1,11 +0,0 @@
---
# sendou.ink-h8l0
title: Fix MobileNav padding on Android
status: todo
type: bug
created_at: 2026-01-11T08:58:54Z
updated_at: 2026-01-11T08:58:54Z
parent: sendou.ink-6eko
---
The mobile navigation lacks proper padding on Android devices. iOS displays correctly. Location: app/components/MobileNav.tsx:35

View File

@ -1,42 +0,0 @@
---
# sendou.ink-h9ht
title: Design and test prestige formula
status: todo
type: task
priority: normal
created_at: 2026-01-12T09:21:17Z
updated_at: 2026-01-12T09:21:23Z
parent: sendou.ink-ylq5
blocking:
- sendou.ink-10ai
---
Experiment with different prestige calculation formulas using real tournament data.
## Algorithm Concept (from epic)
Calculate prestige based on average seeding power of top 8 teams compared to a baseline.
## Variables to Experiment With
- Which teams to consider (top 8? top 4? all teams?)
- Baseline value (median of all tournaments? fixed threshold?)
- Team count weighting (16-team vs 128-team handling)
- Continuous score vs discrete tiers
## Test Cases
Run formula against known tournaments and verify results match intuition:
- Major community tournaments (should be high prestige)
- Regular weeklies (should be medium)
- Casual/new tournaments (should be lower)
## Output
- Documented formula with rationale
- Test results showing formula produces expected rankings
- Configuration constants for tuning
## Dependencies
- Seeding data analysis (sendou.ink-XXX) for understanding data distribution

View File

@ -1,69 +0,0 @@
---
# sendou.ink-hw3x
title: Update sidebar to use real stream data
status: todo
type: task
created_at: 2026-01-12T09:20:56Z
updated_at: 2026-01-12T09:20:56Z
parent: sendou.ink-r6ry
---
Replace mock stream data in sidebar with real aggregated streams.
## Current State
- `/app/features/sidebar/routes/sidebar.ts` has `getMockStreams()`
- Returns hardcoded test data
- Sidebar already displays streams in correct format
## Requirements
- Call stream aggregation service
- Apply scoring algorithm to get top 3
- Return in SideNavLink format: `{ id, name, imageUrl, subtitle, badge }`
## Checklist
- [ ] Import aggregation service in sidebar.ts
- [ ] Replace `getMockStreams()` with call to `aggregateStreams()`
- [ ] Apply `rankStreams()` to get scored/shuffled order
- [ ] Take top 3 streams
- [ ] Map to SideNavLink format
- [ ] Add error handling (return empty array if service fails)
- [ ] Test with real Twitch data
## Implementation
```typescript
// sidebar.ts loader
import { aggregateStreams, rankStreams } from "~/features/streams/streams.server";
async function getStreams() {
try {
const allStreams = await aggregateStreams();
const ranked = rankStreams(allStreams);
return ranked.slice(0, 3).map(stream => ({
id: stream.id,
name: stream.name,
imageUrl: stream.imageUrl,
subtitle: stream.subtitle,
badge: stream.badge,
url: stream.url,
}));
} catch (error) {
console.error("Failed to fetch streams:", error);
return [];
}
}
```
## Error Handling
- If aggregation service fails, return empty array (don't break sidebar)
- Log error for debugging
- Sidebar gracefully hides streams section when empty
## Dependencies
- sendou.ink-nw8b: Stream aggregation service
- sendou.ink-js3r: Sidebar scoring algorithm

View File

@ -1,22 +0,0 @@
---
# sendou.ink-i6lm
title: Radio buttons and similar components get squished on mobile
status: todo
type: bug
created_at: 2026-01-15T19:29:55Z
updated_at: 2026-01-15T19:29:55Z
---
Some components like radio buttons get squished when there is no horizontal space available on mobile devices. This affects usability and visual appearance on smaller screens.
## Screenshots
![alt text](image.png)
![alt text](image-1.png)
## Checklist
- [ ] Identify affected components (radio buttons, checkboxes, etc.)
- [ ] Investigate root cause of squishing behavior
- [ ] Implement fix to ensure components maintain minimum size
- [ ] Test on various mobile viewport widths

View File

@ -1,11 +0,0 @@
---
# sendou.ink-iam7
title: Replace placeholder logo
status: todo
type: task
created_at: 2026-01-11T08:58:55Z
updated_at: 2026-01-11T08:58:55Z
parent: sendou.ink-6eko
---
Replace the placeholder logo with final logo asset when ready. Location: app/components/layout/index.tsx:259

View File

@ -1,44 +0,0 @@
---
# sendou.ink-iulp
title: FriendRepository implementation
status: in-progress
type: task
priority: normal
created_at: 2026-01-13T09:32:05Z
updated_at: 2026-01-16T07:59:43Z
parent: sendou.ink-255r
---
## Overview
Create repository for friend-related database operations.
## File
`app/features/friends/FriendRepository.server.ts`
## Functions
### Friendship CRUD
- `insert(userOneId, userTwoId)` - Create mutual friendship (ensure userOneId < userTwoId)
- `delete(userId, friendId)` - Remove friendship
- `findByUserId(userId)` - Get all friends for a user
- `findMutualFriends(userIdA, userIdB)` - Get mutual friends between two users
### Friend Requests
- `insert(senderId, receiverId)` - Send friend request
- `delete(senderId, receiverId)` - Cancel/decline request
- `findPendingForUser(userId)` - Get incoming requests
- `findOutgoingForUser(userId)` - Get outgoing requests
- `hasPendingRequest(senderId, receiverId)` - Check if request exists
### Connections (aggregated)
- `findAllConnectionsWithActivity(userId)` - Get friends, teammates, associations with SendouQ/tournament activity
## Checklist
- [x] Create FriendRepository.server.ts
- [x] Implement `findByUserIdWithActivity(userId)` for sidebar
- [ ] Implement friendship CRUD
- [ ] Implement friend request operations
- [ ] Unit tests for repository functions

View File

@ -1,42 +0,0 @@
---
# sendou.ink-ivaf
title: Friends section in sidebar with quick invite
status: todo
type: feature
created_at: 2026-01-16T10:17:54Z
updated_at: 2026-01-16T10:17:54Z
parent: sendou.ink-6eko
---
Integrate friends into the sidebar, enabling quick invites and friend status viewing.
**Related:** sendou.ink-255r (Friends feature epic)
## Features
### Quick Invite
- Users can send a "quick invite" to friends who have a partial SendouQ group that is currently looking for members
- The invite should be fast/easy to send directly from the sidebar
### Expandable Friend Details
When a user expands a friend entry in the sidebar, display:
- User avatar
- User name
- Weapons (their current weapon pool or equipped weapons)
- Current group info (if they're in a SendouQ group)
- Any custom status text if available
For each of the friends groups' members
### Pending Requests Indicator
- Show pending requests somewhere visible in the UI
## Checklist
- [ ] Design UI for friends list in sidebar
- [ ] Add expand/collapse functionality for friend entries
- [ ] Display friend details (avatar, name, weapons, status text)
- [ ] Show current SendouQ group info for friends who are looking
- [ ] Implement quick invite button for friends with partial groups
- [ ] Add pending friend requests indicator
- [ ] Handle loading and empty states

View File

@ -1,47 +0,0 @@
---
# sendou.ink-j4zp
title: Remove and redirect /u page
status: completed
type: task
priority: normal
created_at: 2026-01-11T08:58:56Z
updated_at: 2026-01-11T14:08:47Z
parent: sendou.ink-6eko
---
## Summary
Deprecate and remove the /u page. The /u page is a user search page that should be replaced by the CommandPalette search modal.
## Approach
Add URL search params to CommandPalette to allow opening it via a direct link:
- `?search=open` - opens the CommandPalette modal
- `?q=<query>` - pre-fills the search query
- `?type=users|teams|organizations|tournaments` - sets the search type
Then redirect /u to /?search=open&type=users (preserving any existing `q` param).
## Files involved
- `app/components/layout/CommandPalette.tsx` - add search param support
- `app/features/user-search/routes/u.tsx` - replace with redirect
- `app/features/user-search/loaders/u.server.ts` - can be removed
- `routes.ts` - update route to use redirect
- navIconUrl (png and avif files)
## Checklist
- [x] Add search param support to CommandPalette (`search`, `q`, `type` params)
- [x] Update /u route to redirect to /?search=open&type=users
- [x] Remove unused loader and route components
- [x] Remove any reference to u navIcon including breadcrumbs
- [x] Remove from nav (was not in nav)
- [ ] Test the redirect works correctly
- [x] Run checks to ensure everything passes
## Additional changes
- Updated `UserSearch` component to use `/search` API instead of `/u`
- Removed `USER_SEARCH_PAGE` constant from urls.ts
- Removed "u" nav item from `nav-items.ts` and `TopNavMenus.tsx`

View File

@ -1,14 +0,0 @@
---
# sendou.ink-ja9h
title: Show all notifications link doesn't close menu on mobile
status: completed
type: bug
priority: normal
created_at: 2026-01-11T19:22:01Z
updated_at: 2026-01-12T15:32:45Z
parent: sendou.ink-6eko
---
On mobile, clicking the 'Show all notifications' link navigates to the notifications page in the background but doesn't close the menu/popover.
The menu should close when the page navigates.

View File

@ -1,18 +0,0 @@
---
# sendou.ink-jesq
title: Move front page data back to front page loader
status: todo
type: task
created_at: 2026-01-25T12:05:14Z
updated_at: 2026-01-25T12:05:14Z
parent: sendou.ink-6eko
---
The commit 131fa699f moved front page specific data loading (ShowcaseTournaments.frontPageTournamentsByUserId, changelog, leaderboards) into root.tsx loader as part of the sidenav rework.
This data should be moved back to the front page loader (_index route) since it's only needed there, not on every page load.
## Context
- Commit 131fa699f added this to root loader
- Data includes: tournaments, changelog, leaderboards
- Currently loaded via Promise.all in root loader

View File

@ -1,69 +0,0 @@
---
# sendou.ink-jkq9
title: Create mock Twitch streams service for development
status: todo
type: task
created_at: 2026-01-12T12:21:36Z
updated_at: 2026-01-12T12:21:36Z
parent: sendou.ink-r6ry
---
Create a mock streams service that simulates Twitch API responses for local development without requiring Twitch credentials.
## Why
- Develop stream features without Twitch API credentials
- Faster iteration (no network calls)
- Predictable test data
- Works offline
## Requirements
- Same interface as real Twitch service (`getStreams()`)
- Returns realistic mock data (usernames, thumbnails, viewer counts)
- Configurable: number of streams, online/offline states
- Easy toggle between mock and real service
## Options to Consider
1. **Simple mock function** - Just return hardcoded data in dev mode
2. **JSON file** - Load mock data from a JSON file
3. **json-server** - Separate mock server (may be overkill)
4. **MSW (Mock Service Worker)** - Intercepts fetch calls (good for tests too)
## Recommendation
Start simple: environment variable toggle + mock function that returns static data.
```typescript
// /app/modules/twitch/streams.ts
export async function getStreams() {
if (process.env.NODE_ENV === "development" && !process.env.TWITCH_CLIENT_ID) {
return getMockStreams();
}
return getRealStreams();
}
function getMockStreams(): MappedStream[] {
return [
{ twitchUserName: "sendou", viewerCount: 150, thumbnailUrl: "..." },
{ twitchUserName: "teststreamer1", viewerCount: 89, thumbnailUrl: "..." },
// ... more mock data
];
}
```
## Mock Data Should Include
- 5-10 mock streamers with realistic usernames
- Variety of viewer counts (10 - 500 range)
- Placeholder thumbnail URLs (or local images)
- Some usernames matching seeded dev database users (if applicable)
## Checklist
- [ ] Add mock toggle logic to `/app/modules/twitch/streams.ts`
- [ ] Create mock data with realistic stream info
- [ ] Ensure mock works when TWITCH_CLIENT_ID is not set
- [ ] Document in README or .env.example
- [ ] Consider: sync mock usernames with seed data users

View File

@ -1,35 +0,0 @@
---
# sendou.ink-jopf
title: 'Decide on front page content: proper feed or restore old layout'
status: todo
type: task
created_at: 2026-01-11T09:47:05Z
updated_at: 2026-01-11T09:47:05Z
parent: sendou.ink-6eko
---
## Summary
The front page needs attention - either build out a proper activity feed or restore the previous front page content for now.
## Options
### Option A: Build proper feed
- Design and implement a real activity feed with relevant content
- Would include things like: recent tournament results, friend activity, team updates, community highlights
- More work but provides value
### Option B: Restore old front page
- Bring back the previous front page content/layout
- Quick solution to have something meaningful on the homepage
- Can revisit feed implementation later
## Decision needed
Decide which approach to take based on timeline and priorities.
## Checklist
- [ ] Decide on approach (feed vs restore old)
- [ ] Implement chosen solution
- [ ] Test on desktop and mobile

View File

@ -1,95 +0,0 @@
---
# sendou.ink-js3r
title: Implement sidebar scoring algorithm
status: todo
type: feature
priority: normal
created_at: 2026-01-12T09:20:16Z
updated_at: 2026-01-12T09:21:32Z
parent: sendou.ink-r6ry
blocking:
- sendou.ink-hw3x
---
Create the scoring algorithm that determines which streams appear in the sidebar (top 3).
## Scoring Principles
- Skill-based ranking only (NO viewer count)
- Configurable weights for easy tuning
## Scoring by Source
### Tournament streams
- Base: Tournament prestige score (from prestige epic)
- Bonus: Round progression (later rounds score higher)
- Fallback until prestige system: Simple heuristics (team count tiers)
### SendouQ streams
- Score based on tier/peak XP
- Existing tier logic in /app/features/sendouq-streams/
### Calendar event streams
- Fixed score (e.g., 500 - tune based on testing)
- Simple approach for now, can add event-based scoring later
### Non-match streamers
- Score by peak XP from linked X rank account
## Daily Shuffle
- Seeded random by date (consistent order for the day)
- Higher skill weighted more heavily
- Lower skill still has chance to appear
Example algorithm:
```typescript
// Seed based on date for consistency
const seed = dateFns.startOfDay(new Date()).getTime();
const rng = seedrandom(seed.toString());
// Weighted shuffle: multiply score by random factor
const shuffled = streams.map(s => ({
...s,
sortScore: s.score * (0.5 + rng() * 0.5) // 50-100% of score
}));
shuffled.sort((a, b) => b.sortScore - a.sortScore);
```
## Checklist
- [ ] Create `scoreStream()` function for individual stream scoring
- [ ] Create `rankStreams()` function that applies daily shuffle
- [ ] Define score constants at top of file (easy to tune)
- [ ] Add tournament fallback scoring (team count tiers until prestige exists)
- [ ] Add round progression bonus mapping
- [ ] Test with real data to verify reasonable distribution
## Score Constants (starting point)
```typescript
const SCORES = {
// Tournament base by team count
TOURNAMENT_16_PLUS: 1000,
TOURNAMENT_8_PLUS: 800,
TOURNAMENT_SMALL: 600,
// Round bonuses
ROUND_FINALS: 200,
ROUND_SEMIS: 150,
ROUND_QUARTERS: 100,
// Calendar event (fixed)
CALENDAR_EVENT: 500,
// SendouQ by tier (use existing tier values)
// Non-match: use peak XP directly (already ~1500-3000 range)
};
```
## Implementation Notes
- Make weights configurable (constants at top of file)
- Document the scoring formula clearly
- Lives in `/app/features/streams/scoring.server.ts`
- Consider A/B testing different weight configurations later

View File

@ -1,38 +0,0 @@
---
# sendou.ink-kluy
title: Deprecate TrustRelationship table
status: todo
type: task
created_at: 2026-01-13T09:33:39Z
updated_at: 2026-01-13T09:33:39Z
parent: sendou.ink-255r
---
## Overview
Final cleanup: remove TrustRelationship table after migration is stable.
## Prerequisites
- Migration complete (sendou.ink-vd9w)
- All trust-dependent features updated (sendou.ink-gdrp)
- Stable in production for at least 1 week
## Steps
1. Remove TrustRelationship from `app/db/tables.ts`
2. Create migration to drop table
3. Remove `app/features/tournament/queries/giveTrust.server.ts`
4. Remove `app/routines/deleteOldTrusts.ts`
5. Remove any remaining trust-related code
6. Update tests
## Checklist
- [ ] Confirm migration stable in production
- [ ] Remove table definition from tables.ts
- [ ] Create DROP TABLE migration
- [ ] Remove giveTrust.server.ts
- [ ] Remove deleteOldTrusts.ts routine
- [ ] Search and remove remaining trust code
- [ ] Update/remove trust-related tests

View File

@ -1,21 +0,0 @@
---
# sendou.ink-kp16
title: Map pool picker checkmark styling broken & tiebreak text hard to read
status: todo
type: bug
created_at: 2026-01-13T15:13:02Z
updated_at: 2026-01-13T15:13:02Z
parent: sendou.ink-1l22
---
## Description
The map pool picker component has two styling issues:
1. **Checkmark styling is broken** - The checkmarks on selected maps (e.g., Inkblot Art Academy, MakoMart) appear to have incorrect styling
2. **Tiebreak text is hard to read** - The "TIEBREAK" text overlay has poor contrast/readability against the map image background
## Expected behavior
- Checkmarks should have consistent, visible styling that matches the design system
- Tiebreak text should have sufficient contrast to be easily readable (similar to the "BANNED" text which appears more visible)

View File

@ -1,28 +0,0 @@
---
# sendou.ink-kph0
title: Deprecate /q/streams with redirect
status: todo
type: task
created_at: 2026-01-12T09:20:46Z
updated_at: 2026-01-12T09:20:46Z
parent: sendou.ink-r6ry
---
Remove the old /q/streams page and redirect to unified /streams page.
## Requirements
- Redirect /q/streams to /streams?source=sendouq
- Remove link from /q/looking page
- Keep redirect permanent (301)
## Files to Update
- /app/features/sendouq-streams/routes/q.streams.tsx - convert to redirect
- /app/features/sendouq/routes/q.looking.tsx - remove streams link
- routes.ts - may need route changes
## Notes
- Should be done in same release as /streams page launch
- Preserve any bookmarked URLs via redirect

View File

@ -1,17 +0,0 @@
---
# sendou.ink-kvrh
title: Weapon art 'View all' link should support multiple tag variations
status: todo
type: task
created_at: 2026-01-14T16:01:57Z
updated_at: 2026-01-14T16:01:57Z
---
The WeaponArtPreview component's 'View all' link currently only links to one tag (the slug with hyphens, e.g. `/art?tag=luna-blaster`). However, the art query searches for multiple tag variations (hyphens, spaces, underscores).
Consider:
- Updating the art page to support multiple tag filters
- Or showing all matching tag variations in the link
- Or consolidating duplicate tags in the database
Related: WeaponArtPreview component in app/features/weapons/components/WeaponArtPreview.tsx

View File

@ -1,36 +0,0 @@
---
# sendou.ink-kw6u
title: Show scrims in Events
status: completed
type: task
priority: normal
tags:
- my-events-epic
created_at: 2026-01-11T09:44:26Z
updated_at: 2026-01-13T16:21:09Z
parent: sendou.ink-6eko
---
## Summary
Display scrims (both scheduled and looking-for-match) in the sidebar's "Events" section.
## Details
The Events section currently shows tournament calendar entries. It should also include:
1. **Scheduled scrims** - Scrims where the user's team has a confirmed match scheduled
2. **Looking-for-match scrims** - Active scrim postings where user's team is still searching for opponents
This gives competitive players visibility into their upcoming scrim commitments directly from the sidebar, reducing the need to navigate to a separate scrims page.
## Checklist
- [x] Identify where scrim data is fetched/stored
- [x] Add `findUserScrims` repository function
- [x] Update sidebar loader to fetch and merge scrims
- [x] Update Layout component to render scrims with badges
- [x] Add badge variant to SideNav component
- [x] Add CSS for "looking" badge variant
- [x] Add translation keys
- [x] Test with various scrim states

View File

@ -1,39 +0,0 @@
---
# sendou.ink-lk5i
title: Profile page friend button
status: todo
type: task
created_at: 2026-01-13T09:32:51Z
updated_at: 2026-01-13T09:32:51Z
parent: sendou.ink-255r
---
## Overview
Add friend request button to user profile pages. IMPORTANT need to be added both to the old user profile and new user profile (widget based).
## Location
`app/features/user-page/routes/u.$identifier.tsx` (or similar profile route)
## States
Button shows different states based on relationship:
1. **Not friends, no request**: "Add Friend" button → sends request
2. **Outgoing request pending**: "Request Sent" (disabled or cancel option)
3. **Incoming request pending**: "Accept Request" / "Decline" buttons
4. **Already friends**: "Friends ✓" with unfriend option in dropdown
## Implementation
- Check friendship status in loader
- Check pending requests in both directions
- Action handler for send/accept/decline/unfriend
## Checklist
- [ ] Add friendship status to profile loader
- [ ] Friend button component with all states
- [ ] Action handlers for friend operations
- [ ] Loading states during actions
- [ ] Success/error feedback

View File

@ -1,46 +0,0 @@
---
# sendou.ink-m9lp
title: Friend checkbox on invite link join
status: todo
type: task
created_at: 2026-01-13T09:33:50Z
updated_at: 2026-01-13T09:33:50Z
parent: sendou.ink-255r
---
## Overview
Update the invite link join flow to offer friending instead of trusting.
## Current Flow
When joining SendouQ group or tournament team via invite link:
- Checkbox: "Trust [owner name]"
- If checked, creates TrustRelationship
## New Flow
- Checkbox: "Send friend request to [owner name]"
- If checked, creates FriendRequest (not immediate friendship)
- Or if they already sent you a request, accepts it (creates friendship)
## Files
- `app/features/sendouq/actions/q.server.ts` - JOIN_TEAM_WITH_TRUST action
- `app/features/tournament/actions/to.$id.join.server.ts` - Trust checkbox handling
- Related UI components for the join forms
## Behavior
1. If not friends and no pending request: create FriendRequest from joiner → owner
2. If pending request from owner → joiner: accept request, create Friendship
3. If already friends: no-op
4. Notification sent to owner
## Checklist
- [ ] Update SendouQ join action
- [ ] Update tournament join action
- [ ] Update checkbox labels (i18n)
- [ ] Handle mutual request → friendship case
- [ ] Tests for join flow

View File

@ -1,31 +0,0 @@
---
# sendou.ink-n3wk
title: Rename mobile menu tab from Calendar to avoid confusion
status: completed
type: bug
priority: normal
created_at: 2026-01-11T09:38:36Z
updated_at: 2026-01-11T12:48:03Z
parent: sendou.ink-6eko
---
## Problem
On mobile, the bottom tab bar has a calendar icon labeled "Calendar" that opens the menu/navigation modal. This is confusing because there is an actual Calendar page in the app, and users may expect tapping "Calendar" to navigate there.
## Screenshot
The bottom tab shows a calendar icon with "Calendar" label, but it functions as the menu button.
## Expected behavior
The menu tab should have a label that clearly indicates it opens navigation/menu, not the Calendar page.
## Possible solutions
- Rename to "My Calendar" (check if fits)
## Acceptance criteria
- Mobile menu tab label clearly communicates its function
- No confusion with the actual Calendar page

View File

@ -1,39 +0,0 @@
---
# sendou.ink-nucy
title: Consistent avatar border radius in sidebar
status: draft
type: feature
created_at: 2026-01-16T09:23:10Z
updated_at: 2026-01-16T09:23:10Z
---
Currently sidebar avatars have inconsistent border radius styles:
- Some are fully rounded (circle)
- Some are slightly rounded
- Some have no background/different treatment
## Idea
Add a theme option for avatar border radius that applies globally to all avatars:
- **round** - fully circular (border-radius: 50%)
- **rounded** - slightly rounded corners
- **square** - no border radius
This would provide visual consistency and give users control over their preferred avatar style.
## Alternative
Choose one and apply everywhere.
## Areas to check
- Tournament/event avatars in sidebar
- Friend list avatars
- User avatars throughout the app
- Team avatars
## Checklist
- [ ] Audit current avatar border radius usage across codebase
- [ ] Design CSS variable approach for avatar border radius
- [ ] Add theme option to settings
- [ ] Apply consistent border radius to all Avatar components
- [ ] Test across different contexts (sidebar, profiles, etc.)

View File

@ -1,79 +0,0 @@
---
# sendou.ink-nw8b
title: Implement stream aggregation service
status: todo
type: feature
priority: normal
created_at: 2026-01-12T09:20:06Z
updated_at: 2026-01-12T09:21:32Z
parent: sendou.ink-r6ry
blocking:
- sendou.ink-hw3x
---
Create a unified service that combines streams from all sources into a single sorted list.
## Stream Sources
### 1. SendouQ matches
- Players in active matches who are streaming
- Existing: `/app/features/sendouq-streams/core/streams.server.ts`
- Reuse `streamedMatches()` function
### 2. Tournament matches
- Players/casters streaming tournament matches
- Existing: `/app/features/tournament/core/streams.server.ts`
- Reuse `streamsByTournamentId()` function
- Include round name (e.g., 'Top 8', 'Grand Finals')
- Group streams of the same match together
### 3. Calendar event streams (new)
- External events with curator-added stream links
- Query CalendarEventStream table for upcoming events
- These are just URLs - may not be live (display "Upcoming" not "LIVE")
- Soft dependency on sendou.ink-a9gl (can skip if not done yet)
### 4. Non-match streamers (new)
- Players streaming Splatoon 3 but NOT in SQ/tournament matches
- **Must have X rank account linked** (SplatoonPlayer with userId set)
- Sorted by peak XP from X rank data
- Filter out anyone already appearing in sources 1-2
## Output Type
```typescript
interface AggregatedStream {
id: string; // Unique identifier
name: string; // Display name
imageUrl: string; // Thumbnail or avatar
subtitle: string; // Round name, scheduled time, etc.
badge?: "LIVE" | "UPCOMING"; // Status badge
source: "sendouq" | "tournament" | "calendar" | "general";
score: number; // For sorting (from scoring algorithm)
url: string; // Link to stream
twitchUsername?: string; // For Twitch embeds
}
```
## Checklist
- [ ] Create `/app/features/streams/` folder structure
- [ ] Create `aggregateStreams()` main function in `streams.server.ts`
- [ ] Implement SendouQ source adapter
- [ ] Implement Tournament source adapter
- [ ] Implement Calendar event source adapter (can be empty initially)
- [ ] Implement non-match streamers source (users with X rank + Twitch, currently streaming Splatoon 3)
- [ ] Add type definitions
- [ ] Add caching strategy (reuse Twitch's 2min cache pattern)
## Implementation Notes
- Create in `/app/features/streams/` (new feature folder)
- Reuse existing Twitch caching via `/app/modules/twitch/`
- Call existing stream functions, don't duplicate Twitch API logic
- Non-match streamers query pattern:
1. Get all Splatoon 3 streams from Twitch API
2. Get users with `twitchUsername` AND linked `SplatoonPlayer` (X rank data)
3. Match streams to users
4. Filter out those already in SQ/tournament streams
5. Sort by peak XP

View File

@ -1,20 +0,0 @@
---
# sendou.ink-o3l0
title: Improve notifications popover top bar styling
status: todo
type: task
priority: normal
created_at: 2026-01-11T11:51:53Z
updated_at: 2026-01-11T11:51:53Z
parent: sendou.ink-1l22
---
The notifications popover top bar has styling issues:
1. **Lacks padding** - The header area needs proper padding for visual balance
2. **Refresh icon too large** - The refresh/reload icon appears oversized compared to the title text
## Checklist
- [ ] Add appropriate padding to the top bar header
- [ ] Reduce the size of the refresh icon to match the title text better

View File

@ -1,32 +0,0 @@
---
# sendou.ink-oiw2
title: My Events navigation links
status: todo
type: task
priority: normal
created_at: 2026-01-11T11:45:36Z
updated_at: 2026-01-11T12:50:48Z
parent: sendou.ink-om3i
---
## Summary
Add links to My Events page in sidebar (desktop) and mobile menu.
## Details
**Desktop sidebar:**
- Add "My Events" link
- Placement TBD (near existing Calendar link?)
- Only show when logged in
**Mobile menu:**
- Add "My Events" link in appropriate location
- Only show when logged in
## Checklist
- [ ] Add My Events link to desktop sidebar
- [ ] Add My Events link to mobile menu
- [ ] Ensure links only appear when authenticated
- [ ] Add translations for link text

View File

@ -1,67 +0,0 @@
---
# sendou.ink-om3i
title: My Events page
status: todo
type: epic
priority: normal
created_at: 2026-01-11T09:45:51Z
updated_at: 2026-01-11T12:55:40Z
blocking:
- sendou.ink-u4ag
---
## Summary
A dedicated page (`/my-events`) for viewing the user's personal events with all their upcoming commitments in one centralized view.
## Why
Users struggle to see all their commitments (tournaments, scrims) in one place. While the sidebar shows a quick glance, players need a full page view to see their complete schedule.
## Scope (Initial)
**Must have:**
- Tournaments user is registered for
- Tournaments user is organizing
- Scrims (scheduled matches)
- Scrims (looking-for-match posts)
**Future consideration:**
- SendouQ sessions
- Team practice times
- External calendar integration (Google, etc.)
## Design
**View format:** Simple list view, grouped by day (chronological)
**Event card info:**
- Name (tournament name or scrim opponent)
- Time
- Status
- Check-in window (for tournaments, e.g. "Check-in opens in 2 hours")
- Handle two-day tournaments appropriately
**Time range:** All upcoming events only, no past events
**Interaction:** Clicking an event navigates directly to the tournament/scrim page
**URL:** `/my-events` (login required)
**Empty state:** Simple message ("No upcoming events") with links to /calendar and /scrims
**iCal export:** Support .ics file export for subscribing in external calendars (similar to existing /calendar feature)
## Technical Notes
- Share components and data fetching logic with sidebar "Events" section
- Related tasks: `sendou.ink-kw6u` (scrims in sidebar), `sendou.ink-1kb8` (times on mobile panel)
## Navigation
- Add link to sidebar (desktop)
- Add link to mobile menu
## Completion Criteria
All child tasks resolved.

View File

@ -1,43 +0,0 @@
---
# sendou.ink-p9v1
title: Create npm script to sync weapon params from Leanny/splat3
status: completed
type: task
priority: normal
created_at: 2026-01-11T12:22:15Z
updated_at: 2026-01-13T12:04:46Z
parent: sendou.ink-0ze4
---
## Summary
Create an npm script (`npm run sync-weapon-params`) that fetches weapon parameter data from https://github.com/Leanny/splat3 and transforms it into sendou.ink's format.
## Output Format
JSON files stored in the repo. For parameters that changed across patches, use patch-keyed format:
```json
{
"damage": 36,
"damage@5.0.0": 32,
"damage@4.0.0": 30,
"range": 50
}
```
- Plain key = current value
- `key@patch` = historical value from that patch
## Requirements
- Fetch data from Leanny's repo (all weapon categories)
- Track patch history by comparing versions
- Output one JSON file per weapon or per category (TBD based on Leanny's structure)
- Store in appropriate location (e.g., `app/features/weapons/data/`)
## Technical Notes
- Examine Leanny's repo structure to understand data format
- Script should be idempotent - running multiple times produces same result
- Consider storing patch metadata (dates) separately

View File

@ -1,11 +0,0 @@
---
# sendou.ink-pspr
title: Unit test sidenav data loading
status: todo
type: task
created_at: 2026-01-11T12:28:34Z
updated_at: 2026-01-11T12:28:34Z
parent: sendou.ink-6eko
---
Write Vitest unit tests for sidenav data loading logic including loader functions and data transformations.

View File

@ -1,24 +0,0 @@
---
# sendou.ink-qu4s
title: Show team and association members in friends section
status: todo
type: feature
created_at: 2026-01-16T10:20:57Z
updated_at: 2026-01-16T10:20:57Z
parent: sendou.ink-255r
---
Extend the friends section in the sidebar to also display:
- Team members (from the user's team)
- Association members (from the user's association)
## Sorting priority
Friends should appear first in the list, followed by team members, then association members.
## Checklist
- [ ] Query team members for the current user
- [ ] Query association members for the current user
- [ ] Combine friends, team members, and association members into a single list
- [ ] Implement sorting logic: friends first, then team, then association
- [ ] Handle duplicate entries (e.g., a friend who is also a team member should only appear once, as a friend)
- [ ] Update sidebar UI to display the combined list

View File

@ -1,19 +0,0 @@
---
# sendou.ink-qwz1
title: Fix admin page input widths
status: draft
type: task
created_at: 2026-01-11T12:59:32Z
updated_at: 2026-01-11T12:59:32Z
parent: sendou.ink-1l22
---
The admin page has inconsistent input widths - some inputs extend too wide while others are narrow (like the User dropdowns).
**Note:** With the incoming form rework, we might do a bigger overhaul of the admin UI anyway. This task may become part of that larger effort.
## Context
- "Impersonate user" text input stretches full width
- "Link player" User dropdown is very narrow compared to the Player ID input
- "Add as artist" and "Give video adder" User dropdowns are also narrow
- Overall form layout could be more consistent

View File

@ -1,160 +0,0 @@
---
# sendou.ink-r6ry
title: Streams integration in sidebar
status: todo
type: epic
priority: normal
created_at: 2026-01-11T09:18:00Z
updated_at: 2026-01-12T08:49:38Z
---
Unified streams experience across sendou.ink - displaying live and upcoming Splatoon community streams in the sidebar, mobile menu, and a dedicated /streams page.
## Goals
1. Surface live community streams prominently in the sidebar/mobile menu
2. Create a central `/streams` page for browsing all current and upcoming streams
3. Unify SendouQ, tournament, and community event streams under one system
4. Deprecate fragmented stream pages (`/q/streams`, `/to/:id/streams`)
## Stream Sources
### 1. SendouQ Matches
- Players in active SendouQ matches who are streaming
- Scoring based on tier/peak XP
- Already implemented in `/app/features/sendouq-streams/`
### 2. Tournament Matches
- Players/casters streaming tournament matches
- Scoring based on tournament prestige (separate epic) + round progression
- Round names should be visible (e.g., "Top 8", "Grand Finals")
- Streams of the same match should be visually grouped
- Already partially implemented in `/app/features/tournament/core/streams.server.ts`
### 3. Calendar Event Streams (New)
- External events (e.g., LANs) not hosted on sendou.ink
- Requires new `STREAM_CURATOR` role (granted by staff)
- Curators can only add stream links to events they created
- Stream links are platform-agnostic URLs (Twitch, YouTube, etc.)
- New CalendarEventStream join table for multiple links per event
### 4. Non-Match Streamers
- Players streaming Splatoon but not in SQ/tournament
- Must have X rank account linked
- Sorted by peak XP
## Sidebar & Mobile Menu
### Display
- Show top 3 streams (constant, easy to change later)
- Use existing SideNavLink format:
- Image (thumbnail/logo)
- Name (tournament/streamer name)
- Subtitle (round name, or scheduled time for upcoming)
- Badge ("LIVE" indicator)
- Link to `/streams` page
### Scoring Algorithm
- Skill-based ranking only (no viewer count)
- Configurable weights for easy tuning:
- Tournament: prestige score + round progression bonus (fallback: team count tiers)
- SendouQ: tier/peak XP
- Calendar events: Fixed score (simple for now)
- Non-match streamers: peak XP
- Daily shuffle with bias toward higher score
- Seeded random by date so order is consistent for the day
- Higher scores weighted more heavily but lower scores still have chance to appear
### Update Strategy
- Separate task exists for defining sidebar data update strategy (sendou.ink-wn5o)
## /streams Page
### Layout
- Two sections: Current/Live streams and Upcoming streams
- Desktop: potentially two-column layout (TBD in design phase)
- Option to view merged list or grouped by source (SQ/Tournaments/Events)
### Current/Live Section
- All active streams from all sources
- Tournament streams sorted by round, grouped by match
- Multiple tournaments interleaved by prestige/round score
### Upcoming Section
- Calendar events with attached stream info
- Tournaments with known start times
- Future: scheduled league matches
### Twitch Embeds
- Clicking a stream expands inline embed (accordion style)
- Multiple embeds can be open simultaneously
- Optional chat toggle alongside video
## Deprecations (Same Release)
### /q/streams
- Redirect to `/streams?source=sendouq`
- Remove link from `/q/looking` page
### /to/:id/streams
- Redirect to `/streams?tournament=:id` (or similar filter)
- Tournament bracket LIVE popovers retain current behavior (no change)
## New Permissions
### STREAM_CURATOR Role
- New role in existing permissions system
- Granted by staff (same flow as "is artist" role)
- Allows adding stream links to calendar events user created
- Scoped to own events only
- Database: `isStreamCurator` boolean column on User table
## Dependencies
- **sendou.ink-ylq5 - Tournament Prestige System**: Algorithm for calculating tournament prestige based on registered players' seeding power. Streams epic can start with simple heuristics (e.g., team count tiers) until prestige system is built. (soft dependency - can proceed without)
## Data Model Changes
### New Table: CalendarEventStream
- Join table for calendar event stream links
- Columns: id, calendarEventId, url
- Supports multiple stream links per event (Twitch, YouTube, etc.)
### User Table
- Add `isStreamCurator` boolean column (like `isArtist`)
## Technical Notes
### Existing Infrastructure
- Twitch API integration exists (`/app/modules/twitch/`)
- Stream caching with 2-min cache, 10-min stale-while-revalidate
- SendouQ streams logic in `/app/features/sendouq-streams/`
- Tournament streams logic in `/app/features/tournament/core/streams.server.ts`
### Sidebar Integration
- Current mock data in `/app/features/sidebar/routes/sidebar.ts`
- Replace `getMockStreams()` with real stream aggregation
## Open Questions (To Resolve During Implementation)
1. ~~Calendar event stream scoring weight~~ → Fixed score (resolved)
2. Exact layout for /streams page (start simple, iterate)
3. Visual design for tournament match grouping
4. Round ordering: progression order (R1→Finals) or reverse (Finals first)?
5. Cross-tournament interleaving vs grouping by tournament
## Child Tasks
All child tasks have been created as separate tickets under this epic:
- sendou.ink-9cpl: Create /streams page route and basic layout
- sendou.ink-nw8b: Implement stream aggregation service
- sendou.ink-js3r: Implement sidebar scoring algorithm
- sendou.ink-4m0t: Add STREAM_CURATOR role to permissions
- sendou.ink-a9gl: Add Twitch channel field to CalendarEvent
- sendou.ink-x535: Add stream curator UI for calendar events
- sendou.ink-r717: Implement Twitch embed component with chat toggle
- sendou.ink-kph0: Deprecate /q/streams with redirect
- sendou.ink-u6b2: Deprecate /to/:id/streams with redirect
- sendou.ink-hw3x: Update sidebar to use real stream data
- sendou.ink-ae15: Add /streams link to sidebar and mobile menu

View File

@ -1,90 +0,0 @@
---
# sendou.ink-r717
title: Implement Twitch embed component with chat toggle
status: todo
type: feature
created_at: 2026-01-12T09:20:45Z
updated_at: 2026-01-12T09:20:45Z
parent: sendou.ink-r6ry
---
Create reusable Twitch embed component for the /streams page.
## Requirements
- Clicking a stream expands inline embed (accordion style)
- Multiple embeds can be open simultaneously
- Optional chat toggle alongside video
- Responsive sizing (16:9 aspect ratio)
## Checklist
- [ ] Create `/app/components/TwitchEmbed.tsx` component
- [ ] Implement video-only embed mode
- [ ] Implement video + chat side-by-side mode
- [ ] Add chat toggle button
- [ ] Save chat preference to localStorage (`twitch-chat-enabled`)
- [ ] Handle parent domain from `VITE_SITE_DOMAIN` env var
- [ ] Add loading state while iframe loads
- [ ] Test on mobile (responsive width)
## Component API
```tsx
interface TwitchEmbedProps {
channel: string; // Twitch username
showChat?: boolean; // Default from localStorage
onChatToggle?: (show: boolean) => void;
}
// Usage
<TwitchEmbed channel="sendou" />
```
## Twitch Embed URL Format
```
Video only:
https://player.twitch.tv/?channel={channel}&parent={domain}&muted=false
Chat only:
https://www.twitch.tv/embed/{channel}/chat?parent={domain}
```
## Parent Domain
Twitch requires the `parent` parameter for embeds to work. Use:
- `import.meta.env.VITE_SITE_DOMAIN` (strips protocol)
- Parse hostname: `new URL(VITE_SITE_DOMAIN).hostname`
- For localhost: `localhost`
- For production: `sendou.ink`
## Layout
```
Desktop with chat:
┌──────────────────────┬──────────┐
│ │ │
│ Video (16:9) │ Chat │
│ │ (300px) │
│ │ │
└──────────────────────┴──────────┘
Mobile (video only, full width):
┌──────────────────────────────────┐
│ │
│ Video (16:9) │
│ │
└──────────────────────────────────┘
```
## Performance Notes
- Use lazy loading: only load iframe when expanded
- Consider max 3 simultaneous embeds (optional limit)
- iframes are heavy - show placeholder/thumbnail when collapsed
## References
- Twitch embed docs: https://dev.twitch.tv/docs/embed/video-and-clips/
- Chat embed docs: https://dev.twitch.tv/docs/embed/chat/

View File

@ -1,35 +0,0 @@
---
# sendou.ink-rbyg
title: Mutual friends display
status: todo
type: task
created_at: 2026-01-13T09:32:58Z
updated_at: 2026-01-13T09:32:58Z
parent: sendou.ink-255r
---
## Overview
Show mutual friends on user profile pages.
## Display
On profile page, show:
- "X mutual friends" count
- Preview of 3-5 mutual friend avatars
- Click to expand/see full list
## Implementation
- `FriendRepository.findMutualFriends(viewerId, profileUserId)`
- Returns users who are friends with both
- Count and limited preview in loader
- Full list modal or expandable section
## Checklist
- [ ] Add mutual friends query to repository
- [ ] Add to profile page loader
- [ ] Mutual friends preview component
- [ ] Full list modal/expansion
- [ ] Handle zero mutual friends

View File

@ -1,28 +0,0 @@
---
# sendou.ink-reks
title: Define sidebar loading behavior
status: completed
type: task
priority: normal
created_at: 2026-01-11T09:27:48Z
updated_at: 2026-01-25T12:08:55Z
parent: sendou.ink-6eko
---
## Problem
Currently the sidebar shows empty/nothing while data loads, making the page appear broken briefly.
## Task
Define how the sidebar should behave when loading data:
- Should we show skeleton loaders?
- Should we show the previous cached data while fetching fresh data?
- Should we show a loading spinner?
- What about error states if the fetch fails?
## Current Implementation
- Sidebar uses a fetcher to load data on mount (`app/components/layout/index.tsx`)
- No loading or error states are shown
- If fetcher fails, error is silently ignored

View File

@ -1,16 +0,0 @@
---
# sendou.ink-s23a
title: Implement hover menu switching
status: draft
type: task
priority: normal
created_at: 2026-01-11T08:58:55Z
updated_at: 2026-01-11T19:06:55Z
parent: sendou.ink-6eko
---
After clicking to open a menu, moving cursor to another menu item should show that menu's items. Menu should close on outside click or Escape key. Location: app/components/layout/TopNavMenus.tsx:14
## Blocked
React Aria Components doesn't make this easy currently. See: https://github.com/adobe/react-spectrum/issues/8905

View File

@ -1,22 +0,0 @@
---
# sendou.ink-s83f
title: Restore Sendou blue+pink and white+pink themes
status: todo
type: task
created_at: 2026-01-11T09:49:34Z
updated_at: 2026-01-11T09:49:34Z
parent: sendou.ink-1l22
---
## Summary
Re-add the classic Sendou themes for the CSS rework:
1. **Blue + Pink** - The signature Sendou.ink dark theme
2. **White + Pink** - Light theme variant
## Checklist
- [ ] Define theme CSS variables for blue+pink theme
- [ ] Define theme CSS variables for white+pink theme
- [ ] Verify accessibility/contrast requirements are met

View File

@ -1,33 +0,0 @@
---
# sendou.ink-sb5p
title: Create recent vods preview component
status: todo
type: task
created_at: 2026-01-11T12:22:34Z
updated_at: 2026-01-11T12:22:34Z
parent: sendou.ink-0ze4
---
## Summary
Create a React component that displays 5 recent VODs for a weapon.
## Features
- Show 5 most recent VODs featuring the weapon
- Each VOD shows: thumbnail, title, date, player(s)
- "View all" link to `/vods?weapon={weaponId}`
## Data Source
- `VodRepository.findVods()` with weapon filter
- Includes alt skins/kits via `weaponIdToArrayWithAlts()`
## Props
- `vods` - array of 5 vod objects
- `weaponId` - for "view all" link
## Technical Notes
- Reuse existing VOD display components if available (`VodListing`)

View File

@ -1,11 +0,0 @@
---
# sendou.ink-sonu
title: E2E test desktop sidenav
status: todo
type: task
created_at: 2026-01-11T12:28:33Z
updated_at: 2026-01-11T12:28:33Z
parent: sendou.ink-6eko
---
Write Playwright E2E tests for the desktop sidenav functionality including navigation, collapsing, and user interactions.

View File

@ -1,12 +0,0 @@
---
# sendou.ink-svz2
title: Inline function in NotificationPopover
status: completed
type: task
priority: normal
created_at: 2026-01-11T08:58:55Z
updated_at: 2026-01-11T12:41:51Z
parent: sendou.ink-6eko
---
Refactor NotificationPopover by inlining the separate function that makes no sense as standalone. Location: app/components/layout/NotificationPopover.tsx:40

View File

@ -1,20 +0,0 @@
---
# sendou.ink-takt
title: Mobile menu doesn't block background scrolling
status: todo
type: bug
created_at: 2026-01-11T18:39:55Z
updated_at: 2026-01-11T18:39:55Z
parent: sendou.ink-1l22
---
The mobile menu doesn't actually block scrolling the container when open. This might be a bug with our react-aria components scroll lock workaround.
## Context
- Mobile menu should prevent background content from scrolling when open
- Current react-aria scroll lock implementation may not be working correctly
## Investigation needed
- Check the current scroll lock workaround implementation
- Verify if this is a react-aria issue or our custom code
- Test on different mobile browsers

View File

@ -1,37 +0,0 @@
---
# sendou.ink-tnjk
title: Rewrite weapon page UI
status: todo
type: task
created_at: 2026-01-11T12:23:05Z
updated_at: 2026-01-11T12:23:05Z
parent: sendou.ink-0ze4
---
## Summary
Replace the existing placeholder weapon page with the new design using all created components.
## Components to Integrate
- Weapon header (image, name)
- Stats summary (top XP holder, popularity)
- Quick links
- Parameter comparison table
- Recent vods preview (5)
- Popular builds preview (5)
- Art preview (5)
## Requirements
- Replace existing `/app/features/weapons/routes/weapons.$slug.tsx`
- Well-componentized - each section in its own component for easy reordering
- Use CSS modules for styling
- Responsive design (desktop + mobile)
## Technical Notes
- Import data from rewritten loader
- Each section component receives props from loader data
- Handle empty states gracefully (e.g., "No art tagged for this weapon yet")
- Order of sections can be adjusted later - keep components decoupled

View File

@ -1,27 +0,0 @@
---
# sendou.ink-tq5i
title: Fix bracket lines and match timer color contrast
status: todo
type: bug
created_at: 2026-01-11T09:36:11Z
updated_at: 2026-01-11T09:36:11Z
parent: sendou.ink-1l22
---
## Problem
On the tournament bracket page, the bracket connector lines and match timer badge blend together because they use the same or very similar colors. This makes it difficult to visually distinguish the timer from the bracket structure.
## Screenshot
The timer showing "0m" and the bracket lines are both a similar gray/muted color, reducing visual clarity.
## Expected behavior
The bracket lines and match timer should have distinct colors so they're easily distinguishable at a glance.
## Acceptance criteria
- Bracket connector lines and match timer have sufficient color contrast between them
- Both elements remain accessible and readable
- Fits within the new CSS variable system in `vars.css`

View File

@ -1,10 +0,0 @@
---
# sendou.ink-u4ag
title: My events future work
status: draft
type: epic
created_at: 2026-01-11T12:55:35Z
updated_at: 2026-01-11T12:55:35Z
---
Future enhancements for the My Events page after initial implementation is complete.

View File

@ -1,29 +0,0 @@
---
# sendou.ink-u6b2
title: Deprecate /to/:id/streams with redirect
status: todo
type: task
priority: normal
created_at: 2026-01-12T09:20:46Z
updated_at: 2026-01-12T12:07:11Z
parent: sendou.ink-r6ry
---
Remove the old tournament streams page and redirect to unified /streams page.
## Requirements
- Redirect /to/:id/streams to /streams?tournament=:id (or similar filter)
- Tournament bracket LIVE popovers retain current behavior (no change)
- Keep redirect permanent (301)
## Files to Update
- /app/features/tournament/routes/to.$id.streams.tsx - convert to redirect
- routes.ts - may need route changes
## Notes
- Should be done in same release as /streams page launch
- Preserve any bookmarked URLs via redirect
- Bracket popover behavior is separate and unchanged

View File

@ -1,27 +0,0 @@
---
# sendou.ink-utog
title: Update user-pickable themes to use new CSS vars
status: todo
type: task
created_at: 2026-01-11T09:49:34Z
updated_at: 2026-01-11T09:49:34Z
parent: sendou.ink-1l22
---
## Summary
User profile pages and team pages allow custom theme colors. These need to be updated to work with the new CSS variable system from the CSS rework.
## Affected areas
- **User pages** - Profile customization with user-selected colors
- **Team pages** - Team branding colors
## Checklist
- [ ] Audit current user theme implementation
- [ ] Map old theme values to new CSS variable system
- [ ] Update user page theme application logic
- [ ] Update team page theme application logic
- [ ] Test custom themes don't break with new CSS structure
- [ ] Ensure theme picker UI works correctly

View File

@ -1,35 +0,0 @@
---
# sendou.ink-uvge
title: Create weapon quick links component
status: todo
type: task
created_at: 2026-01-11T12:22:54Z
updated_at: 2026-01-11T12:22:54Z
parent: sendou.ink-0ze4
---
## Summary
Create a React component that displays quick links to related pages.
## Links to Include
- **Builds** - `/builds/{weaponSlug}`
- **Popular Builds** - `/builds/{weaponSlug}/popular`
- **Ability Stats** - `/builds/{weaponSlug}/stats`
- **VODs** - `/vods?weapon={weaponId}`
- **Build Analyzer** - `/analyzer` (with weapon pre-selected if possible)
- **Object Damage Calculator** - `/object-damage-calculator`
- **Free Agents** - Link to SendouQ looking page filtered by weapon
- **Leaderboard** - `/leaderboards?type=XP-WEAPON-{weaponId}` (shown with top XP holder)
## Props
- `weaponSlug`
- `weaponId`
## Technical Notes
- Use existing `LinkButton` component
- Appropriate icons for each link (existing icons in codebase)
- Some links may be grouped with related features (e.g., leaderboard with stats summary)

View File

@ -1,20 +0,0 @@
---
# sendou.ink-v42i
title: Check brackets UI design
status: todo
type: task
created_at: 2026-01-11T12:34:21Z
updated_at: 2026-01-11T12:34:21Z
parent: sendou.ink-6eko
---
## Description
Review the brackets UI and decide on design questions:
## Checklist
- [ ] Should tabs (Main/Underground) be "attached" to brackets outline?
- [ ] Should "Add sub" link to a different page?
- [ ] Compactify button restyle needed?
- [ ] Do we need "Go to match" button when match info is already in the sidebar?

View File

@ -1,36 +0,0 @@
---
# sendou.ink-vbbi
title: Rewrite weapon page loader
status: todo
type: task
created_at: 2026-01-11T12:22:54Z
updated_at: 2026-01-11T12:22:54Z
parent: sendou.ink-0ze4
---
## Summary
Rewrite the loader for `/weapons/:slug` to fetch all data needed for the new weapon page.
## Data to Fetch
All in a single loader:
1. **Weapon info** - ID, name, slug, category (existing)
2. **Weapon params** - From JSON files, including patch history
3. **Category weapons** - All weapons in same category for comparison table
4. **Top XP holder** - From `weaponXPLeaderboard(weaponId)`
5. **Popularity stats** - Overall rank + category rank from XRankPlacement
6. **Recent vods** - 5 most recent from `VodRepository.findVods()`
7. **Popular builds** - 5 most popular from BuildRepository
8. **Art by tag** - 5 art pieces tagged with weapon slug
## Location
`/app/features/weapons/loaders/weapons.$slug.server.ts`
## Technical Notes
- Use Promise.all for parallel DB queries where possible
- Consider caching for expensive queries
- Handle missing data gracefully (e.g., no art tagged yet)

View File

@ -1,58 +0,0 @@
---
# sendou.ink-vd9w
title: Migration from trust to friendship
status: todo
type: task
created_at: 2026-01-13T09:33:20Z
updated_at: 2026-01-13T09:33:20Z
parent: sendou.ink-255r
---
## Overview
Migrate existing TrustRelationship data to new Friendship system.
## Migration Logic
```sql
-- Find mutual trusts (A trusts B AND B trusts A)
-- Convert to friendships
INSERT INTO Friendship (userOneId, userTwoId, createdAt)
SELECT
CASE WHEN t1.trustGiverUserId < t1.trustReceiverUserId
THEN t1.trustGiverUserId
ELSE t1.trustReceiverUserId END,
CASE WHEN t1.trustGiverUserId < t1.trustReceiverUserId
THEN t1.trustReceiverUserId
ELSE t1.trustGiverUserId END,
MIN(t1.lastUsedAt)
FROM TrustRelationship t1
JOIN TrustRelationship t2
ON t1.trustGiverUserId = t2.trustReceiverUserId
AND t1.trustReceiverUserId = t2.trustGiverUserId
WHERE t1.trustGiverUserId < t1.trustReceiverUserId
GROUP BY 1, 2;
-- Delete all trust relationships (mutual converted, one-way discarded)
DELETE FROM TrustRelationship;
```
## Approach
1. Create migration script (not DB migration - one-time data migration)
2. Run on staging first
3. Verify friendship counts match expected
4. Run on production
5. Keep TrustRelationship table temporarily for rollback
6. Remove table in later migration after confirming stability
## Checklist
- [ ] Create migration script
- [ ] Test on development database
- [ ] Count mutual trusts before migration
- [ ] Verify friendship count after migration
- [ ] Test on staging
- [ ] Run on production
- [ ] Monitor for issues

View File

@ -1,12 +0,0 @@
---
# sendou.ink-vj1n
title: Fix organizations icon in CommandPalette
status: completed
type: bug
priority: normal
created_at: 2026-01-11T08:58:55Z
updated_at: 2026-01-11T13:17:39Z
parent: sendou.ink-6eko
---
The organizations category uses wrong icon. Should use the same icon as AnythingAdder uses for organizations. Location: app/components/layout/CommandPalette.tsx:42

View File

@ -1,11 +0,0 @@
---
# sendou.ink-vmid
title: Define navigation menu categories
status: todo
type: task
created_at: 2026-01-11T08:58:54Z
updated_at: 2026-01-11T08:58:54Z
parent: sendou.ink-6eko
---
Replace placeholder categories in TopNavMenus with actual navigation structure. Location: app/components/layout/TopNavMenus.tsx:13

Some files were not shown because too many files have changed in this diff Show More