Add board and stack management for 0.3.0
Some checks are pending
Validate Decky / web-client (push) Waiting to run

This commit is contained in:
Elijah 2026-07-19 19:03:18 -07:00
parent dfb04462f3
commit 6a8c6a1eb6
16 changed files with 364 additions and 92 deletions

View file

@ -23,6 +23,43 @@ describe('DeckApiClient', () => {
expect(client.apiBaseUrl.toString()).toBe('https://cloud.example.test/apps/deck/api/v1.0/');
});
it('creates, updates, and deletes boards with Deck-compatible payloads', async () => {
const fetchImpl = vi.fn<FetchLike>().mockImplementation(async (input, init) => {
if (String(input).endsWith('/boards') && !init?.method) return json([]);
return init?.method === 'DELETE' ? new Response(null, { status: 204 }) : json({ id: 4, title: 'Projects', color: '0082C9' });
});
const client = await DeckApiClient.connect(options, fetchImpl);
await client.createBoard('Projects', '#0082c9');
await client.updateBoard(4, 'Projects 2026', '46BA61');
await client.deleteBoard(4);
expect(String(fetchImpl.mock.calls[1][0])).toMatch(/\/boards$/);
expect(fetchImpl.mock.calls[1][1]?.method).toBe('POST');
expect(JSON.parse(String(fetchImpl.mock.calls[1][1]?.body))).toEqual({ title: 'Projects', color: '0082C9' });
expect(String(fetchImpl.mock.calls[2][0])).toMatch(/\/boards\/4$/);
expect(JSON.parse(String(fetchImpl.mock.calls[2][1]?.body))).toEqual({ title: 'Projects 2026', color: '46BA61', archived: false });
expect(fetchImpl.mock.calls[3][1]?.method).toBe('DELETE');
});
it('creates, renames, and deletes stacks while preserving order', async () => {
const fetchImpl = vi.fn<FetchLike>().mockImplementation(async (input, init) => {
if (String(input).endsWith('/boards') && !init?.method) return json([]);
return init?.method === 'DELETE' ? new Response(null, { status: 204 }) : json({ id: 9, boardId: 4, title: 'Next', order: 3, cards: [] });
});
const client = await DeckApiClient.connect(options, fetchImpl);
await client.createStack(4, 'Next', 3);
await client.updateStack(4, 9, 'Later', 3);
await client.deleteStack(4, 9);
expect(String(fetchImpl.mock.calls[1][0])).toMatch(/\/boards\/4\/stacks$/);
expect(JSON.parse(String(fetchImpl.mock.calls[1][1]?.body))).toEqual({ title: 'Next', order: 3 });
expect(String(fetchImpl.mock.calls[2][0])).toMatch(/\/boards\/4\/stacks\/9$/);
expect(JSON.parse(String(fetchImpl.mock.calls[2][1]?.body))).toEqual({ title: 'Later', order: 3 });
expect(fetchImpl.mock.calls[3][1]?.method).toBe('DELETE');
});
it('preserves complete card fields and normalizes object owners', () => {
const card: Card = {
id: 1,

View file

@ -106,10 +106,35 @@ export class DeckApiClient {
return this.request<Board[]>('boards');
}
createBoard(title: string, color: string): Promise<Board> {
return this.request<Board>('boards', {
method: 'POST',
body: JSON.stringify({ title, color: normalizeColor(color) })
});
}
updateBoard(boardId: number, title: string, color: string, archived = false): Promise<Board> {
return this.request<Board>(`boards/${boardId}`, {
method: 'PUT',
body: JSON.stringify({ title, color: normalizeColor(color), archived })
});
}
deleteBoard(boardId: number): Promise<void> {
return this.request<void>(`boards/${boardId}`, { method: 'DELETE' });
}
getStacks(boardId: number): Promise<Stack[]> {
return this.request<Stack[]>(`boards/${boardId}/stacks`);
}
createStack(boardId: number, title: string, order: number): Promise<Stack> {
return this.request<Stack>(`boards/${boardId}/stacks`, {
method: 'POST',
body: JSON.stringify({ title, order })
});
}
updateStack(boardId: number, stackId: number, title: string, order: number): Promise<Stack> {
return this.request<Stack>(`boards/${boardId}/stacks/${stackId}`, {
method: 'PUT',
@ -117,6 +142,10 @@ export class DeckApiClient {
});
}
deleteStack(boardId: number, stackId: number): Promise<void> {
return this.request<void>(`boards/${boardId}/stacks/${stackId}`, { method: 'DELETE' });
}
async reorderStack(boardId: number, stackId: number, order: number): Promise<Stack[]> {
const server = normalizeServerUrl(this.options.serverUrl);
const url = new URL(`ocs/v2.php/apps/deck/api/v1.0/stacks/${stackId}/reorder`, server);

View file

@ -14,6 +14,12 @@ export interface Board {
archived?: boolean;
lastModified?: number;
deletedAt?: number;
permissions?: {
PERMISSION_READ?: boolean;
PERMISSION_EDIT?: boolean;
PERMISSION_MANAGE?: boolean;
PERMISSION_SHARE?: boolean;
};
}
export interface Stack {