mirror of
https://github.com/karl0ss/homepage.git
synced 2025-04-29 12:03:41 +01:00
Enhancement: support dynamic list rendering in custom api widget (#5012)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
parent
de9c015f7f
commit
8d20f22932
@ -138,7 +138,15 @@ You can manipulate data with the following tools `remap`, `scale`, `prefix` and
|
||||
prefix: "$"
|
||||
```
|
||||
|
||||
## List View
|
||||
## Display Options
|
||||
|
||||
The widget supports different display modes that can be set using the `display` property.
|
||||
|
||||
### Block View (Default)
|
||||
|
||||
The default display mode is `block`, which shows fields in a block format.
|
||||
|
||||
### List View
|
||||
|
||||
You can change the default block view to a list view by setting the `display` option to `list`.
|
||||
|
||||
@ -169,6 +177,48 @@ The list view can optionally display an additional field next to the primary fie
|
||||
format: date
|
||||
```
|
||||
|
||||
### Dynamic List View
|
||||
|
||||
To display a list of items from an array in the API response, set the `display` property to `dynamic-list` and configure the `mappings` object with the following properties:
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: customapi
|
||||
url: https://example.com/api/servers
|
||||
display: dynamic-list
|
||||
mappings:
|
||||
items: data # optional, the path to the array in the API response. Omit this option if the array is at the root level
|
||||
name: id # required, field in each item to use as the item name (left side)
|
||||
label: ip_address # required, field in each item to use as the item label (right side)
|
||||
limit: 5 # optional, limit the number of items to display
|
||||
target: https://example.com/server/{id} # optional, makes items clickable with template support
|
||||
```
|
||||
|
||||
This configuration would work with an API that returns a response like:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{ "id": "server1", "name": "Server 1", "ip_address": "192.168.0.1" },
|
||||
{ "id": "server2", "name": "Server 2", "ip_address": "192.168.0.2" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The widget would display a list with two items:
|
||||
|
||||
- "Server 1" on the left and "192.168.0.1" on the right, clickable to "https://example.com/server/server1"
|
||||
- "Server 2" on the left and "192.168.0.2" on the right, clickable to "https://example.com/server/server2"
|
||||
|
||||
For nested fields in the items, you can use dot notation:
|
||||
|
||||
```yaml
|
||||
mappings:
|
||||
items: data.results.servers
|
||||
name: details.id
|
||||
label: details.name
|
||||
```
|
||||
|
||||
## Custom Headers
|
||||
|
||||
Pass custom headers using the `headers` option, for example:
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
import classNames from "classnames";
|
||||
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
import * as shvl from "utils/config/shvl";
|
||||
|
||||
function getValue(field, data) {
|
||||
let value = data;
|
||||
@ -165,6 +167,16 @@ export default function Component({ service }) {
|
||||
|
||||
if (!customData) {
|
||||
switch (display) {
|
||||
case "dynamic-list":
|
||||
return (
|
||||
<Container service={service}>
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded-sm m-1 flex-1 flex flex-row items-center justify-between p-1 text-xs animate-pulse">
|
||||
<div className="font-thin pl-2">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
case "list":
|
||||
return (
|
||||
<Container service={service}>
|
||||
@ -196,6 +208,68 @@ export default function Component({ service }) {
|
||||
}
|
||||
|
||||
switch (display) {
|
||||
case "dynamic-list":
|
||||
let listItems = customData;
|
||||
if (mappings.items) listItems = shvl.get(customData, mappings.items, null);
|
||||
let error;
|
||||
if (!listItems || !Array.isArray(listItems)) {
|
||||
error = { message: "Unable to find items" };
|
||||
}
|
||||
const name = mappings.name;
|
||||
const label = mappings.label;
|
||||
if (!name || !label) {
|
||||
error = { message: "Name and label properties are required" };
|
||||
}
|
||||
if (error) {
|
||||
return <Container service={service} error={error}></Container>;
|
||||
}
|
||||
|
||||
const target = mappings.target;
|
||||
if (mappings.limit && parseInt(mappings.limit, 10) > 0) {
|
||||
listItems.splice(mappings.limit);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<div className="flex flex-col w-full">
|
||||
{listItems.length === 0 ? (
|
||||
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded-sm m-1 flex-1 flex flex-row items-center justify-between p-1 text-xs">
|
||||
<div className="font-thin pl-2">No items found</div>
|
||||
</div>
|
||||
) : (
|
||||
listItems.map((item, index) => {
|
||||
const itemName = shvl.get(item, name, "");
|
||||
const itemLabel = shvl.get(item, label, "");
|
||||
const itemUrl = target ? target.replace(/\{([^}]+)\}/g, (_, key) => item[key] || "") : null;
|
||||
const className =
|
||||
"bg-theme-200/50 dark:bg-theme-900/20 rounded-sm m-1 flex-1 flex flex-row items-center justify-between p-1 text-xs";
|
||||
|
||||
return itemUrl ? (
|
||||
<a
|
||||
key={`${itemName}-${index}`}
|
||||
className={classNames(className, "hover:bg-theme-300/50 dark:hover:bg-theme-800/20")}
|
||||
href={itemUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="font-thin pl-2">{itemName}</div>
|
||||
<div className="flex flex-row text-right">
|
||||
<div className="font-bold mr-2">{itemLabel}</div>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div key={`${itemName}-${index}`} className={className}>
|
||||
<div className="font-thin pl-2">{itemName}</div>
|
||||
<div className="flex flex-row text-right">
|
||||
<div className="font-bold mr-2">{itemLabel}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
case "list":
|
||||
return (
|
||||
<Container service={service}>
|
||||
|
Loading…
x
Reference in New Issue
Block a user