Public Access
feat(library): backlog status, rating, notes and priority in the UI
Detail page gets an edit card. The library gets status badges, status filter chips and a debounced name search, both kept in the URL.
This commit is contained in:
1 parent
7b676f0a1e
commit
f94e0aff15
9 files changed
+394
-13
No files matched your search
@@ -0,0 +1,21 @@
|
||||
export const GAME_STATUSES = ['BACKLOG', 'PLAYING', 'FINISHED', 'COMPLETED', 'DROPPED'];
|
||||
export const GAME_PRIORITIES = ['HIGH', 'MEDIUM', 'LOW'];
|
||||
|
||||
// Radix Select has no empty value, so "no priority" needs its own value in the form.
|
||||
export const NO_PRIORITY = 'NONE';
|
||||
|
||||
export const parseStatus = (value) => (GAME_STATUSES.includes(value) ? value : null);
|
||||
|
||||
export const toBacklogRequest = ({status, rating, notes, priority}) => ({
|
||||
status,
|
||||
rating: rating || null,
|
||||
notes: notes?.trim() ? notes : null,
|
||||
priority: priority === NO_PRIORITY ? null : priority,
|
||||
});
|
||||
|
||||
export const libraryQuery = ({page, size, sort, status, q}) => {
|
||||
const params = new URLSearchParams({page: String(page), size: String(size), sort});
|
||||
if (status) params.set('status', status);
|
||||
if (q?.trim()) params.set('q', q.trim());
|
||||
return params.toString();
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import {describe, expect, it} from 'vitest';
|
||||
import {libraryQuery, NO_PRIORITY, parseStatus, toBacklogRequest} from './backlog.js';
|
||||
|
||||
describe('toBacklogRequest', () => {
|
||||
it('sends every field', () => {
|
||||
expect(toBacklogRequest({status: 'FINISHED', rating: 4, notes: 'Great', priority: 'HIGH'}))
|
||||
.toEqual({status: 'FINISHED', rating: 4, notes: 'Great', priority: 'HIGH'});
|
||||
});
|
||||
|
||||
it('sends null for cleared fields', () => {
|
||||
expect(toBacklogRequest({status: 'BACKLOG', rating: 0, notes: ' ', priority: NO_PRIORITY}))
|
||||
.toEqual({status: 'BACKLOG', rating: null, notes: null, priority: null});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseStatus', () => {
|
||||
it('accepts a known status only', () => {
|
||||
expect(parseStatus('PLAYING')).toBe('PLAYING');
|
||||
expect(parseStatus('playing')).toBeNull();
|
||||
expect(parseStatus(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('libraryQuery', () => {
|
||||
it('leaves out empty filters', () => {
|
||||
expect(libraryQuery({page: 0, size: 20, sort: 'playtimeMinutes,desc', status: null, q: ' '}))
|
||||
.toBe('page=0&size=20&sort=playtimeMinutes%2Cdesc');
|
||||
});
|
||||
|
||||
it('adds status and the trimmed, encoded search', () => {
|
||||
expect(libraryQuery({page: 1, size: 40, sort: 'appid,asc', status: 'DROPPED', q: ' 100% & more '}))
|
||||
.toBe('page=1&size=40&sort=appid%2Casc&status=DROPPED&q=100%25+%26+more');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user