mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-12 23:54:01 +09:00
feat(dashboard): show categories brought by tapped repositories in the sidebars
The marketplace and console sidebars rendered a fixed IaaS / PaaS / NaaS list and dropped every other spec.dashboard.category. A repository tapped with cozypkg that brings its own category was reachable only through the full application list, and its installed applications had no console section. Follow groupByCategory instead, the order the marketplace page already uses, and keep Administration in the Admin portal. Assisted-by: LLM Signed-off-by: tym83 <6355522@gmail.com>
This commit is contained in:
1 parent
091b8549c2
commit
b9ca07d021
2 files changed
+67
-15
No files matched your search
+58
-2
@@ -24,9 +24,12 @@ const emptyAppDefList: K8sList<unknown> = {
|
||||
|
||||
// The admin gates issue two SSARs (nodes/list for Cluster Usage,
|
||||
// backupclasses/update for Backup Classes); answer each by requested resource.
|
||||
function makeClient(allow: Record<string, boolean | "pending">): K8sClient {
|
||||
function makeClient(
|
||||
allow: Record<string, boolean | "pending">,
|
||||
appDefs: K8sList<unknown> = emptyAppDefList,
|
||||
): K8sClient {
|
||||
const client = new K8sClient()
|
||||
vi.spyOn(client, "list").mockResolvedValue(emptyAppDefList as K8sList<unknown>)
|
||||
vi.spyOn(client, "list").mockResolvedValue(appDefs as K8sList<unknown>)
|
||||
vi.spyOn(client, "create").mockImplementation(async (_g, _v, _p, body) => {
|
||||
const resource =
|
||||
(body as SelfSubjectAccessReview).spec?.resourceAttributes?.resource ?? ""
|
||||
@@ -109,6 +112,59 @@ describe("useConsoleSidebarSections — admin areas moved out", () => {
|
||||
})
|
||||
})
|
||||
|
||||
function appDef(name: string, kind: string, category: string, module = false) {
|
||||
return {
|
||||
apiVersion: "cozystack.io/v1alpha1",
|
||||
kind: "ApplicationDefinition",
|
||||
metadata: { name },
|
||||
spec: {
|
||||
application: { kind, plural: `${name}s` },
|
||||
dashboard: { category, ...(module ? { module: true } : {}) },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// A tapped repository brings its own category next to the built-in ones.
|
||||
const tappedAppDefs: K8sList<unknown> = {
|
||||
...emptyAppDefList,
|
||||
items: [
|
||||
appDef("postgres", "Postgres", "PaaS"),
|
||||
appDef("vminstance", "VMInstance", "IaaS"),
|
||||
appDef("oberonvm", "OberonVM", "Paleocomputing"),
|
||||
appDef("ingress", "Ingress", "Administration", true),
|
||||
appDef("seaweedfs", "SeaweedFS", "Administration"),
|
||||
],
|
||||
}
|
||||
|
||||
describe("custom categories from tapped repositories", () => {
|
||||
it("lists a tapped category in the marketplace sidebar after the built-in ones", async () => {
|
||||
const client = makeClient({}, tappedAppDefs)
|
||||
const { result } = renderHook(() => useMarketplaceSidebarSections(), {
|
||||
wrapper: makeWrapper(client),
|
||||
})
|
||||
await waitFor(() =>
|
||||
expect(findItem(result.current, "Paleocomputing")?.to).toBe(
|
||||
"/marketplace/c/Paleocomputing",
|
||||
),
|
||||
)
|
||||
const labels = result.current[0].items.map((i) => i.label)
|
||||
expect(labels).toEqual(["Marketplace", "IaaS", "PaaS", "Paleocomputing"])
|
||||
})
|
||||
|
||||
it("gives a tapped category its own console section and keeps Administration out", async () => {
|
||||
const client = makeClient({}, tappedAppDefs)
|
||||
const { result } = renderHook(() => useConsoleSidebarSections(), {
|
||||
wrapper: makeWrapper(client),
|
||||
})
|
||||
await waitFor(() => expect(sectionIndex(result.current, "Paleocomputing")).not.toBe(-1))
|
||||
expect(hasItemTo(result.current, "/console/oberonvms")).toBe(true)
|
||||
expect(sectionIndex(result.current, "PaaS")).toBeLessThan(
|
||||
sectionIndex(result.current, "Paleocomputing"),
|
||||
)
|
||||
expect(sectionIndex(result.current, "Administration")).toBe(-1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useMarketplaceSidebarSections", () => {
|
||||
it("leaves Repositories to the Admin portal", () => {
|
||||
const client = makeClient({})
|
||||
|
||||
+9
-13
@@ -29,7 +29,8 @@ import {
|
||||
} from "../lib/sidebar-icons.tsx"
|
||||
import type { ComponentType } from "react"
|
||||
|
||||
const MARKETPLACE_CATEGORIES = ["IaaS", "PaaS", "NaaS"]
|
||||
// Lives in the Admin portal; any other category, tapped ones included, gets an entry.
|
||||
const ADMIN_CATEGORY = "Administration"
|
||||
const CATEGORY_ICON: Record<string, LucideIcon> = {
|
||||
IaaS: Cloud,
|
||||
PaaS: Database,
|
||||
@@ -38,7 +39,8 @@ const CATEGORY_ICON: Record<string, LucideIcon> = {
|
||||
|
||||
/**
|
||||
* Marketplace sidebar: a flat list of filters — "All applications" followed
|
||||
* by the three categories. Category links rely on pathname-based matching
|
||||
* by every category in {@link groupByCategory} order (IaaS, PaaS, NaaS, then
|
||||
* the rest alphabetically), Administration excepted. Category links rely on pathname-based matching
|
||||
* (`/marketplace/c/<category>`) so NavLink correctly highlights the active
|
||||
* entry; see MarketplaceList for the counterpart.
|
||||
*/
|
||||
@@ -47,10 +49,9 @@ export function useMarketplaceSidebarSections(): SidebarSection[] {
|
||||
const grouped = useMemo(() => groupByCategory(data), [data])
|
||||
|
||||
return useMemo<SidebarSection[]>(() => {
|
||||
const available = grouped
|
||||
const ordered = grouped
|
||||
.map((g) => g.category)
|
||||
.filter((c) => MARKETPLACE_CATEGORIES.includes(c))
|
||||
const ordered = MARKETPLACE_CATEGORIES.filter((c) => available.includes(c))
|
||||
.filter((c) => c !== ADMIN_CATEGORY)
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -70,7 +71,8 @@ export function useMarketplaceSidebarSections(): SidebarSection[] {
|
||||
|
||||
/**
|
||||
* Console sidebar: every non-module, non-Tenant ApplicationDefinition in the
|
||||
* cluster as a separate entry grouped by IaaS / PaaS / NaaS, plus the per-tenant
|
||||
* cluster as a separate entry grouped by category in {@link groupByCategory}
|
||||
* order, Administration excepted, plus the per-tenant
|
||||
* Backups section. Administration (Info, Modules, External IPs, Tenants) now
|
||||
* lives in the Admin portal — see {@link useAdminSidebarSections}.
|
||||
*/
|
||||
@@ -79,13 +81,7 @@ export function useConsoleSidebarSections(): SidebarSection[] {
|
||||
const grouped = useMemo(() => groupByCategory(data), [data])
|
||||
|
||||
return useMemo<SidebarSection[]>(() => {
|
||||
const sorted = [...grouped]
|
||||
.filter(({ category }) => MARKETPLACE_CATEGORIES.includes(category))
|
||||
.sort(
|
||||
(a, b) =>
|
||||
MARKETPLACE_CATEGORIES.indexOf(a.category) -
|
||||
MARKETPLACE_CATEGORIES.indexOf(b.category),
|
||||
)
|
||||
const sorted = grouped.filter(({ category }) => category !== ADMIN_CATEGORY)
|
||||
|
||||
const categorySections: SidebarSection[] = sorted.map(({ category, items }) => ({
|
||||
title: category,
|
||||
|
||||
Reference in new issue
Block a user