fix(web): admin bibliothèques — correctifs complémentaires client et AdminPage
- web: AdminPage, client API (+ tests), styles Refs: #15
This commit is contained in:
@ -1,5 +1,9 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { ApiFallbackError, getApiFallback } from "./client";
|
import { api, ApiFallbackError, getApiFallback } from "./client";
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
describe("api fallback helpers", () => {
|
describe("api fallback helpers", () => {
|
||||||
it("extracts typed fallback payloads", () => {
|
it("extracts typed fallback payloads", () => {
|
||||||
@ -9,4 +13,21 @@ describe("api fallback helpers", () => {
|
|||||||
it("ignores non fallback errors", () => {
|
it("ignores non fallback errors", () => {
|
||||||
expect(getApiFallback<string[]>(new Error("boom"))).toBeUndefined();
|
expect(getApiFallback<string[]>(new Error("boom"))).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not send JSON content-type for bodyless delete requests", async () => {
|
||||||
|
const fetchMock = vi.fn().mockResolvedValue(
|
||||||
|
new Response(JSON.stringify({ ok: true }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
|
|
||||||
|
await api.deleteLibrary(42);
|
||||||
|
|
||||||
|
const init = fetchMock.mock.calls[0][1] as RequestInit;
|
||||||
|
const headers = new Headers(init.headers);
|
||||||
|
expect(init.method).toBe("DELETE");
|
||||||
|
expect(headers.has("Content-Type")).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -56,15 +56,20 @@ function apiErrorMessage(detail: string, fallback: string): string {
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function requestHeaders(options: RequestOptions): Headers {
|
||||||
|
const headers = new Headers(options.headers);
|
||||||
|
if (options.body !== undefined && !headers.has("Content-Type")) {
|
||||||
|
headers.set("Content-Type", "application/json");
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE}${path}`, {
|
const response = await fetch(`${API_BASE}${path}`, {
|
||||||
...options,
|
...options,
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
headers: {
|
headers: requestHeaders(options)
|
||||||
"Content-Type": "application/json",
|
|
||||||
...options.headers
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
@ -158,7 +158,7 @@ export function AdminPage() {
|
|||||||
<div className="library-table">
|
<div className="library-table">
|
||||||
{libraries.map((library) => (
|
{libraries.map((library) => (
|
||||||
<div key={library.id}>
|
<div key={library.id}>
|
||||||
<div>
|
<div className="library-copy">
|
||||||
<strong>{library.name}</strong>
|
<strong>{library.name}</strong>
|
||||||
<span>Chemin : {library.path}</span>
|
<span>Chemin : {library.path}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -284,6 +284,26 @@ h2 {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.library-copy {
|
||||||
|
display: grid;
|
||||||
|
gap: 7px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-copy strong,
|
||||||
|
.library-copy span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-copy strong {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-copy span {
|
||||||
|
color: var(--ink-muted);
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
.meter {
|
.meter {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user