feat: consolidate onboarding
This commit is contained in:
1 parent
8239a631f2
commit
7b1a801ab8
8 files changed
+153
-25
No files matched your search
@@ -23,6 +23,7 @@ import {
|
||||
integrationItems,
|
||||
integrationUserPermissions,
|
||||
integrations,
|
||||
apps,
|
||||
itemLayouts,
|
||||
items,
|
||||
layouts,
|
||||
@@ -295,6 +296,11 @@ export const boardRouter = createTRPCRouter({
|
||||
width: true,
|
||||
height: true,
|
||||
},
|
||||
with: {
|
||||
item: {
|
||||
columns: { kind: true, options: true },
|
||||
},
|
||||
},
|
||||
where: and(
|
||||
inArray(itemLayouts.layoutId, previewLayoutIds),
|
||||
inArray(itemLayouts.sectionId, rootSectionIds),
|
||||
@@ -333,6 +339,27 @@ export const boardRouter = createTRPCRouter({
|
||||
])
|
||||
: [[], []];
|
||||
|
||||
const appIdByItemId = new Map(
|
||||
previewItemLayouts.flatMap((layout) => {
|
||||
if (layout.item.kind !== "app") return [];
|
||||
try {
|
||||
const { appId } = superjson.parse<{ appId?: unknown }>(layout.item.options);
|
||||
return typeof appId === "string" ? [[layout.itemId, appId] as const] : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}),
|
||||
);
|
||||
const previewAppIds = [...new Set(appIdByItemId.values())];
|
||||
const previewApps =
|
||||
previewAppIds.length > 0
|
||||
? await ctx.db.query.apps.findMany({
|
||||
columns: { id: true, iconUrl: true },
|
||||
where: inArray(apps.id, previewAppIds),
|
||||
})
|
||||
: [];
|
||||
const appIconUrlById = new Map(previewApps.map((app) => [app.id, app.iconUrl]));
|
||||
|
||||
return dbBoards.map(({ layouts: boardLayouts, sections: boardSections, ...board }) => {
|
||||
const previewLayout = boardLayouts.at(0);
|
||||
|
||||
@@ -365,7 +392,15 @@ export const boardRouter = createTRPCRouter({
|
||||
const itemPreview = previewLayout
|
||||
? previewItemLayouts
|
||||
.filter((layout) => layout.layoutId === previewLayout.id && isInsideRootLane(layout))
|
||||
.map((layout) => ({ id: layout.itemId, layouts: [layout] }))
|
||||
.map((layout) => {
|
||||
const appId = appIdByItemId.get(layout.itemId);
|
||||
return {
|
||||
id: layout.itemId,
|
||||
kind: layout.item.kind,
|
||||
iconUrl: appId ? appIconUrlById.get(appId) : undefined,
|
||||
layouts: [layout],
|
||||
};
|
||||
})
|
||||
: [];
|
||||
const containerPreview = previewLayout
|
||||
? previewSectionLayouts
|
||||
|
||||
@@ -6,6 +6,7 @@ import { createId } from "@homarr/common";
|
||||
import type { Database, InferInsertModel } from "@homarr/db";
|
||||
import { and, eq } from "@homarr/db";
|
||||
import {
|
||||
apps,
|
||||
boardGroupPermissions,
|
||||
boards,
|
||||
boardUserPermissions,
|
||||
@@ -457,6 +458,7 @@ describe("getManageOverview", () => {
|
||||
items: [
|
||||
{
|
||||
id: itemId,
|
||||
kind: "clock",
|
||||
layouts: [{ layoutId: baseLayoutId, sectionId: rootSectionId, width: 4, height: 2 }],
|
||||
},
|
||||
],
|
||||
@@ -475,6 +477,64 @@ describe("getManageOverview", () => {
|
||||
expect(result[0]?.preview?.sections).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("includes app and widget icons in compact previews", async () => {
|
||||
const db = createDb();
|
||||
const caller = boardRouter.createCaller({ db, deviceType: undefined, session: defaultSession });
|
||||
const boardId = createId();
|
||||
const layoutId = createId();
|
||||
const rootSectionId = createId();
|
||||
const appId = createId();
|
||||
const appItemId = createId();
|
||||
|
||||
await db.insert(users).values({ id: defaultCreatorId });
|
||||
await db.insert(boards).values({ id: boardId, name: "icon-overview", creatorId: defaultCreatorId });
|
||||
await db.insert(layouts).values({
|
||||
id: layoutId,
|
||||
name: "Base",
|
||||
boardId,
|
||||
columnCount: 12,
|
||||
breakpoint: 768,
|
||||
role: "base",
|
||||
});
|
||||
await db.insert(sections).values({
|
||||
id: rootSectionId,
|
||||
boardId,
|
||||
kind: "empty",
|
||||
xOffset: 0,
|
||||
yOffset: 0,
|
||||
});
|
||||
await db.insert(apps).values({
|
||||
id: appId,
|
||||
name: "Sonarr",
|
||||
iconUrl: "https://example.com/sonarr.svg",
|
||||
});
|
||||
await db.insert(items).values({
|
||||
id: appItemId,
|
||||
boardId,
|
||||
kind: "app",
|
||||
options: SuperJSON.stringify({ appId }),
|
||||
});
|
||||
await db.insert(itemLayouts).values({
|
||||
itemId: appItemId,
|
||||
sectionId: rootSectionId,
|
||||
layoutId,
|
||||
xOffset: 0,
|
||||
yOffset: 0,
|
||||
width: 2,
|
||||
height: 2,
|
||||
});
|
||||
|
||||
const result = await caller.getManageOverview();
|
||||
|
||||
expect(result[0]?.preview?.items).toEqual([
|
||||
expect.objectContaining({
|
||||
id: appItemId,
|
||||
kind: "app",
|
||||
iconUrl: "https://example.com/sonarr.svg",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test("uses the highest-breakpoint layout when a board has no base layout", async () => {
|
||||
const db = createDb();
|
||||
const caller = boardRouter.createCaller({ db, deviceType: undefined, session: defaultSession });
|
||||
|
||||
Reference in new issue
Block a user