Features: Add group nesting/subgroups

This commit is contained in:
damii 2024-11-27 15:07:25 +11:00
parent 5ee2ea559c
commit 4fa024bca6
6 changed files with 88 additions and 35 deletions

View File

@ -21,6 +21,25 @@ Groups are defined as top-level array entries.
<img width="1038" alt="Service Groups" src="https://user-images.githubusercontent.com/82196/187040754-28065242-4534-4409-881c-93d1921c6141.png"> <img width="1038" alt="Service Groups" src="https://user-images.githubusercontent.com/82196/187040754-28065242-4534-4409-881c-93d1921c6141.png">
## Subgroups
Subgroups are defined as array entries with `type: group` and an array `services: ...`,
```
- Group A:
- Subgroup A:
type: group
services:
- Service A:
href: http://localhost/
- Service B:
href: http://localhost/
- Group B:
- Service C:
href: http://localhost/
```
## Services ## Services
Services are defined as array entries on groups, Services are defined as array entries on groups,

View File

@ -8,12 +8,14 @@ import ResolvedIcon from "components/resolvedicon";
export default function ServicesGroup({ export default function ServicesGroup({
group, group,
subgroups,
services, services,
layout, layout,
fiveColumns, fiveColumns,
disableCollapse, disableCollapse,
useEqualHeights, useEqualHeights,
groupsInitiallyCollapsed, groupsInitiallyCollapsed,
isSubgroup = false,
}) { }) {
const panel = useRef(); const panel = useRef();
@ -29,6 +31,7 @@ export default function ServicesGroup({
layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4", layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4",
layout?.style !== "row" && fiveColumns ? "3xl:basis-1/5" : "", layout?.style !== "row" && fiveColumns ? "3xl:basis-1/5" : "",
layout?.header === false ? "flex-1 px-1 -my-1" : "flex-1 p-1", layout?.header === false ? "flex-1 px-1 -my-1" : "flex-1 p-1",
isSubgroup === false ? "" : "bg-theme-500/20 dark:bg-white/5 rounded-md px-2 py-2",
)} )}
> >
<Disclosure defaultOpen={!(layout?.initiallyCollapsed ?? groupsInitiallyCollapsed) ?? true}> <Disclosure defaultOpen={!(layout?.initiallyCollapsed ?? groupsInitiallyCollapsed) ?? true}>
@ -74,7 +77,13 @@ export default function ServicesGroup({
}} }}
> >
<Disclosure.Panel className="transition-all overflow-hidden duration-300 ease-out" ref={panel} static> <Disclosure.Panel className="transition-all overflow-hidden duration-300 ease-out" ref={panel} static>
<List group={group} services={services.services} layout={layout} useEqualHeights={useEqualHeights} /> <List
group={group}
subgroups={subgroups}
services={services.services}
layout={layout}
useEqualHeights={useEqualHeights}
/>
</Disclosure.Panel> </Disclosure.Panel>
</Transition> </Transition>
</> </>

View File

@ -3,23 +3,37 @@ import classNames from "classnames";
import { columnMap } from "../../utils/layout/columns"; import { columnMap } from "../../utils/layout/columns";
import Item from "components/services/item"; import Item from "components/services/item";
import Group from "components/services/group";
export default function List({ group, services, layout, useEqualHeights }) { export default function List({ group, subgroups = [], services, layout, useEqualHeights }) {
return ( return (
<ul <>
className={classNames( <ul
layout?.style === "row" ? `grid ${columnMap[layout?.columns]} gap-x-2` : "flex flex-col", className={classNames(
"mt-3 services-list", layout?.style === "row" ? `grid ${columnMap[layout?.columns]} gap-x-2` : "flex flex-col",
)} "mt-3 services-list",
> )}
{services.map((service) => ( >
<Item {services.map((service) => (
key={[service.container, service.app, service.name].filter((s) => s).join("-")} <Item
service={service} key={[service.container, service.app, service.name].filter((s) => s).join("-")}
group={group} service={service}
useEqualHeights={layout?.useEqualHeights ?? useEqualHeights} group={group}
/> useEqualHeights={layout?.useEqualHeights ?? useEqualHeights}
))} />
</ul> ))}
</ul>
{subgroups
.filter((subgroup) => subgroup != [])
.map((subgroup) => (
<Group
group={subgroup}
services={subgroup}
layout={layout}
useEqualHeights={useEqualHeights}
isSubgroup={true}
/>
))}
</>
); );
} }

View File

@ -292,6 +292,7 @@ function Home({ initialSettings }) {
<ServicesGroup <ServicesGroup
key={group.name} key={group.name}
group={group.name} group={group.name}
subgroups={group.subgroups}
services={group} services={group}
layout={settings.layout?.[group.name]} layout={settings.layout?.[group.name]}
fiveColumns={settings.fiveColumns} fiveColumns={settings.fiveColumns}
@ -317,6 +318,7 @@ function Home({ initialSettings }) {
<ServicesGroup <ServicesGroup
key={group.name} key={group.name}
group={group.name} group={group.name}
subgroups={group.subgroups}
services={group} services={group}
layout={settings.layout?.[group.name]} layout={settings.layout?.[group.name]}
fiveColumns={settings.fiveColumns} fiveColumns={settings.fiveColumns}

View File

@ -142,9 +142,11 @@ export async function servicesResponse() {
mergedGroupsNames.forEach((groupName) => { mergedGroupsNames.forEach((groupName) => {
const discoveredDockerGroup = discoveredDockerServices.find((group) => group.name === groupName) || { const discoveredDockerGroup = discoveredDockerServices.find((group) => group.name === groupName) || {
services: [], services: [],
subgroups: [],
}; };
const discoveredKubernetesGroup = discoveredKubernetesServices.find((group) => group.name === groupName) || { const discoveredKubernetesGroup = discoveredKubernetesServices.find((group) => group.name === groupName) || {
services: [], services: [],
subgroups: [],
}; };
const configuredGroup = configuredServices.find((group) => group.name === groupName) || { services: [] }; const configuredGroup = configuredServices.find((group) => group.name === groupName) || { services: [] };
@ -153,6 +155,11 @@ export async function servicesResponse() {
services: [...discoveredDockerGroup.services, ...discoveredKubernetesGroup.services, ...configuredGroup.services] services: [...discoveredDockerGroup.services, ...discoveredKubernetesGroup.services, ...configuredGroup.services]
.filter((service) => service) .filter((service) => service)
.sort(compareServices), .sort(compareServices),
subgroups: [
...discoveredDockerGroup.subgroups,
...discoveredKubernetesGroup.subgroups,
...configuredGroup.subgroups,
].filter((subgroup) => subgroup),
}; };
if (definedLayouts) { if (definedLayouts) {

View File

@ -25,24 +25,22 @@ export async function servicesFromConfig() {
return []; return [];
} }
// map easy to write YAML objects into easy to consume JS arrays const mappingFunc = (servicesGroup, servicesArr = servicesGroup[Object.keys(servicesGroup)[0]]) => ({
const servicesArray = services.map((servicesGroup) => ({
name: Object.keys(servicesGroup)[0], name: Object.keys(servicesGroup)[0],
services: servicesGroup[Object.keys(servicesGroup)[0]].map((entries) => ({ services: servicesArr
name: Object.keys(entries)[0], .filter((entry) => entry[Object.keys(entry)[0]].type != "group")
...entries[Object.keys(entries)[0]], .map((entries, entryIndex) => ({
type: "service", name: Object.keys(entries)[0],
})), weight: (entryIndex + 1) * 100,
})); type: entries[Object.keys(entries)[0]].type ? entries[Object.keys(entries)[0]].type : "service",
...entries[Object.keys(entries)[0]],
// add default weight to services based on their position in the configuration })),
servicesArray.forEach((group, groupIndex) => { subgroups: servicesArr
group.services.forEach((service, serviceIndex) => { .filter((entry) => entry[Object.keys(entry)[0]].type == "group")
if (service.weight === undefined) { .map((entries) => mappingFunc(entries, entries[Object.keys(entries)[0]].services)),
servicesArray[groupIndex].services[serviceIndex].weight = (serviceIndex + 1) * 100;
}
});
}); });
// map easy to write YAML objects into easy to consume JS arrays
const servicesArray = services.map((servicesGroup) => mappingFunc(servicesGroup));
return servicesArray; return servicesArray;
} }
@ -134,6 +132,7 @@ export async function servicesFromDocker() {
mappedServiceGroups.push({ mappedServiceGroups.push({
name: serverService.group, name: serverService.group,
services: [], services: [],
subgroups: [],
}); });
serverGroup = mappedServiceGroups[mappedServiceGroups.length - 1]; serverGroup = mappedServiceGroups[mappedServiceGroups.length - 1];
} }
@ -317,6 +316,7 @@ export async function servicesFromKubernetes() {
mappedServiceGroups.push({ mappedServiceGroups.push({
name: serverService.group, name: serverService.group,
services: [], services: [],
subgroups: [],
}); });
serverGroup = mappedServiceGroups[mappedServiceGroups.length - 1]; serverGroup = mappedServiceGroups[mappedServiceGroups.length - 1];
} }
@ -338,7 +338,7 @@ export async function servicesFromKubernetes() {
} }
export function cleanServiceGroups(groups) { export function cleanServiceGroups(groups) {
return groups.map((serviceGroup) => ({ const cleanerFunc = (serviceGroup) => ({
name: serviceGroup.name, name: serviceGroup.name,
services: serviceGroup.services.map((service) => { services: serviceGroup.services.map((service) => {
const cleanedService = { ...service }; const cleanedService = { ...service };
@ -663,7 +663,9 @@ export function cleanServiceGroups(groups) {
return cleanedService; return cleanedService;
}), }),
})); subgroups: serviceGroup.subgroups.map((serviceGroup) => cleanerFunc(serviceGroup)),
});
return groups.map((serviceGroup) => cleanerFunc(serviceGroup));
} }
export async function getServiceItem(group, service) { export async function getServiceItem(group, service) {