mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
feat(documents): run the vault's months side by side by default
An account that never picked an arrangement now sees the months flowing across the width. The layout PUT stores only the fields the person chose, so an untouched field keeps following the default while a saved choice stays.
This commit is contained in:
1 parent
88836452eb
commit
7340cd3ab7
7 files changed
+86
-24
No files matched your search
@@ -89,9 +89,27 @@ describe("GET /api/documents/inbound/layout", () => {
|
||||
const res = await callGet();
|
||||
expect(res.status).toBe(200);
|
||||
expect((await res.json()).data).toEqual(DEFAULT_DOCUMENTS_LAYOUT);
|
||||
expect(DEFAULT_DOCUMENTS_LAYOUT).toEqual({
|
||||
version: 1,
|
||||
view: "cards",
|
||||
arrangement: "flow",
|
||||
});
|
||||
expect(prisma.user.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps a saved stacked arrangement although the default is flow", async () => {
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue({
|
||||
documentsLayoutJson: {
|
||||
version: 1,
|
||||
view: "cards",
|
||||
arrangement: "stacked",
|
||||
},
|
||||
} as never);
|
||||
|
||||
const res = await callGet();
|
||||
expect((await res.json()).data.arrangement).toBe("stacked");
|
||||
});
|
||||
|
||||
it("serves the stored choice, repairing an unknown value field by field", async () => {
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue({
|
||||
documentsLayoutJson: { version: 1, view: "list", arrangement: "spiral" },
|
||||
@@ -101,7 +119,7 @@ describe("GET /api/documents/inbound/layout", () => {
|
||||
expect((await res.json()).data).toEqual({
|
||||
version: 1,
|
||||
view: "list",
|
||||
arrangement: "stacked",
|
||||
arrangement: "flow",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -146,6 +164,23 @@ describe("PUT /api/documents/inbound/layout", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("stores only the chosen field, so the untouched one keeps following the default", async () => {
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue({
|
||||
documentsLayoutJson: null,
|
||||
} as never);
|
||||
|
||||
const res = await put({ version: 1, view: "list" });
|
||||
expect((await res.json()).data).toEqual({
|
||||
version: 1,
|
||||
view: "list",
|
||||
arrangement: "flow",
|
||||
});
|
||||
expect(prisma.user.update).toHaveBeenCalledWith({
|
||||
where: { id: "user-1" },
|
||||
data: { documentsLayoutJson: { version: 1, view: "list" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("refuses an unknown value and an unknown key with 422 and writes nothing", async () => {
|
||||
const bad = await put({ version: 1, view: "carousel" });
|
||||
expect(bad.status).toBe(422);
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
DOCUMENTS_LAYOUT_ARRANGEMENTS,
|
||||
DOCUMENTS_LAYOUT_VIEWS,
|
||||
resolveDocumentsLayout,
|
||||
storedDocumentsLayoutChoices,
|
||||
type DocumentsLayout,
|
||||
} from "@/lib/documents/documents-layout";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
@@ -35,12 +36,12 @@ const layoutPutSchema = z
|
||||
})
|
||||
.strict();
|
||||
|
||||
async function readLayout(userId: string): Promise<DocumentsLayout> {
|
||||
async function readStoredLayout(userId: string): Promise<unknown> {
|
||||
const row = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { documentsLayoutJson: true },
|
||||
});
|
||||
return resolveDocumentsLayout(row?.documentsLayoutJson);
|
||||
return row?.documentsLayoutJson;
|
||||
}
|
||||
|
||||
export const GET = apiHandler(async () => {
|
||||
@@ -51,7 +52,7 @@ export const GET = apiHandler(async () => {
|
||||
const gate = await requireModuleEnabled(user.id, "inboundDocuments");
|
||||
if (!gate.enabled) return gate.response;
|
||||
|
||||
const layout = await readLayout(user.id);
|
||||
const layout = resolveDocumentsLayout(await readStoredLayout(user.id));
|
||||
annotate({
|
||||
action: { name: "documents.layout.read" },
|
||||
meta: { view: layout.view, arrangement: layout.arrangement },
|
||||
@@ -78,17 +79,20 @@ export const PUT = apiHandler(async (request: Request) => {
|
||||
}
|
||||
|
||||
// Preserve-when-absent: one stored read fills whichever field the body
|
||||
// left out.
|
||||
const existing = await readLayout(user.id);
|
||||
const next: DocumentsLayout = {
|
||||
version: 1,
|
||||
view: parsed.data.view ?? existing.view,
|
||||
arrangement: parsed.data.arrangement ?? existing.arrangement,
|
||||
// left out. Only fields the user has chosen are written, so a field never
|
||||
// picked keeps following the default (which can change between releases).
|
||||
const choices = {
|
||||
...storedDocumentsLayoutChoices(await readStoredLayout(user.id)),
|
||||
...(parsed.data.view ? { view: parsed.data.view } : {}),
|
||||
...(parsed.data.arrangement
|
||||
? { arrangement: parsed.data.arrangement }
|
||||
: {}),
|
||||
};
|
||||
const next: DocumentsLayout = resolveDocumentsLayout(choices);
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { documentsLayoutJson: toJson(next) },
|
||||
data: { documentsLayoutJson: toJson({ version: 1, ...choices }) },
|
||||
});
|
||||
|
||||
annotate({
|
||||
|
||||
Reference in new issue
Block a user