init
This commit is contained in:
+14
-7
@@ -1,6 +1,6 @@
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { env } from "$env/dynamic/public";
|
||||
|
||||
const BASE = env.PUBLIC_API_BASE || 'http://localhost:8080';
|
||||
const BASE = env.PUBLIC_API_BASE || "http://localhost:8080";
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
@@ -12,9 +12,9 @@ export class ApiError extends Error {
|
||||
|
||||
async function request<T>(path: string, opts: RequestInit = {}): Promise<T> {
|
||||
const res = await fetch(`${BASE}/api${path}`, {
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json', ...(opts.headers ?? {}) },
|
||||
...opts
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json", ...(opts.headers ?? {}) },
|
||||
...opts,
|
||||
});
|
||||
|
||||
if (res.status === 204) return undefined as T;
|
||||
@@ -31,7 +31,14 @@ async function request<T>(path: string, opts: RequestInit = {}): Promise<T> {
|
||||
export const api = {
|
||||
get: <T>(p: string) => request<T>(p),
|
||||
post: <T>(p: string, body?: unknown) =>
|
||||
request<T>(p, { method: 'POST', body: body ? JSON.stringify(body) : undefined }),
|
||||
request<T>(p, {
|
||||
method: "POST",
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
}),
|
||||
patch: <T>(p: string, body?: unknown) =>
|
||||
request<T>(p, { method: 'PATCH', body: body ? JSON.stringify(body) : undefined })
|
||||
request<T>(p, {
|
||||
method: "PATCH",
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
}),
|
||||
del: <T>(p: string) => request<T>(p, { method: "DELETE" }),
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { api } from './api';
|
||||
import { api } from "./api";
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
@@ -23,7 +23,7 @@ class AuthStore {
|
||||
|
||||
async refresh() {
|
||||
try {
|
||||
const me = await api.get<Me>('/auth/me');
|
||||
const me = await api.get<Me>("/auth/me");
|
||||
this.user = me.user;
|
||||
this.settings = me.settings;
|
||||
} catch {
|
||||
@@ -42,7 +42,7 @@ class AuthStore {
|
||||
|
||||
async logout() {
|
||||
try {
|
||||
await api.post('/auth/logout');
|
||||
await api.post("/auth/logout");
|
||||
} finally {
|
||||
this.user = null;
|
||||
this.settings = null;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { api } from "./api";
|
||||
|
||||
export type ItemStatus = "coveted" | "acquired" | "renounced";
|
||||
|
||||
export type List = {
|
||||
id: string;
|
||||
name: string;
|
||||
emoji: string | null;
|
||||
description: string | null;
|
||||
position: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type Item = {
|
||||
id: string;
|
||||
list_id: string;
|
||||
title: string;
|
||||
url: string | null;
|
||||
note: string | null;
|
||||
status: ItemStatus;
|
||||
target_price: number | null;
|
||||
position: number;
|
||||
// Filled by the Phase 3 fetcher; null until then.
|
||||
title_fetched: string | null;
|
||||
current_price: number | null;
|
||||
currency: string | null;
|
||||
image_url: string | null;
|
||||
in_stock: boolean | null;
|
||||
source: string | null;
|
||||
fetched_at: string | null;
|
||||
track_enabled: boolean;
|
||||
last_error: string | null;
|
||||
checked_at: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type PricePoint = {
|
||||
price: number;
|
||||
currency: string;
|
||||
in_stock: boolean | null;
|
||||
fetched_at: string;
|
||||
};
|
||||
|
||||
export type NewList = {
|
||||
name: string;
|
||||
emoji?: string | null;
|
||||
description?: string | null;
|
||||
};
|
||||
export type NewItem = {
|
||||
title: string;
|
||||
url?: string | null;
|
||||
note?: string | null;
|
||||
target_price?: number | null;
|
||||
};
|
||||
|
||||
// ---- Lists ----------------------------------------------------------------
|
||||
|
||||
export const listsApi = {
|
||||
all: () => api.get<List[]>("/lists"),
|
||||
create: (b: NewList) => api.post<List>("/lists", b),
|
||||
update: (id: string, b: Partial<NewList> & { position?: number }) =>
|
||||
api.patch<List>(`/lists/${id}`, b),
|
||||
remove: (id: string) => api.del<{ deleted: string }>(`/lists/${id}`),
|
||||
|
||||
items: (listId: string) => api.get<Item[]>(`/lists/${listId}/items`),
|
||||
addItem: (listId: string, b: NewItem) =>
|
||||
api.post<Item>(`/lists/${listId}/items`, b),
|
||||
updateItem: (
|
||||
id: string,
|
||||
b: Partial<NewItem> & { status?: ItemStatus; position?: number },
|
||||
) => api.patch<Item>(`/items/${id}`, b),
|
||||
removeItem: (id: string) => api.del<{ deleted: string }>(`/items/${id}`),
|
||||
refetch: (id: string) => api.post<Item>(`/items/${id}/refetch`, {}),
|
||||
history: (id: string) => api.get<PricePoint[]>(`/items/${id}/history`),
|
||||
};
|
||||
|
||||
/** Reactive store for the user's lists. */
|
||||
class ListsStore {
|
||||
items = $state<List[]>([]);
|
||||
loaded = $state(false);
|
||||
|
||||
async load() {
|
||||
this.items = await listsApi.all();
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
async create(b: NewList): Promise<List> {
|
||||
const created = await listsApi.create(b);
|
||||
this.items.push(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
await listsApi.remove(id);
|
||||
this.items = this.items.filter((l) => l.id !== id);
|
||||
}
|
||||
}
|
||||
|
||||
export const lists = new ListsStore();
|
||||
Reference in New Issue
Block a user