Feature: add todo widget
This commit is contained in:
parent
1c47d9d70e
commit
b5a8e8aaac
13
docs/widgets/services/todo.md
Normal file
13
docs/widgets/services/todo.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: Todo
|
||||||
|
description: Todo Widget Configuration
|
||||||
|
---
|
||||||
|
|
||||||
|
The Todo widget provides a simple to-do list functionality.
|
||||||
|
It allows users to add, remove, and mark tasks as completed.
|
||||||
|
The component utilizes local storage to persist the to-do list data even when the user refreshes the page.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
widget:
|
||||||
|
type: todo
|
||||||
|
```
|
||||||
@ -122,6 +122,7 @@ nav:
|
|||||||
- widgets/services/syncthing-relay-server.md
|
- widgets/services/syncthing-relay-server.md
|
||||||
- widgets/services/tailscale.md
|
- widgets/services/tailscale.md
|
||||||
- widgets/services/tdarr.md
|
- widgets/services/tdarr.md
|
||||||
|
- widgets/services/todo.md
|
||||||
- widgets/services/traefik.md
|
- widgets/services/traefik.md
|
||||||
- widgets/services/transmission.md
|
- widgets/services/transmission.md
|
||||||
- widgets/services/truenas.md
|
- widgets/services/truenas.md
|
||||||
|
|||||||
@ -96,6 +96,7 @@ const components = {
|
|||||||
tautulli: dynamic(() => import("./tautulli/component")),
|
tautulli: dynamic(() => import("./tautulli/component")),
|
||||||
tdarr: dynamic(() => import("./tdarr/component")),
|
tdarr: dynamic(() => import("./tdarr/component")),
|
||||||
traefik: dynamic(() => import("./traefik/component")),
|
traefik: dynamic(() => import("./traefik/component")),
|
||||||
|
todo: dynamic(() => import("./todo/component")),
|
||||||
transmission: dynamic(() => import("./transmission/component")),
|
transmission: dynamic(() => import("./transmission/component")),
|
||||||
tubearchivist: dynamic(() => import("./tubearchivist/component")),
|
tubearchivist: dynamic(() => import("./tubearchivist/component")),
|
||||||
truenas: dynamic(() => import("./truenas/component")),
|
truenas: dynamic(() => import("./truenas/component")),
|
||||||
|
|||||||
96
src/widgets/todo/component.jsx
Normal file
96
src/widgets/todo/component.jsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
("use client");
|
||||||
|
|
||||||
|
import { MdOutlineDelete } from "react-icons/md";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import Container from "components/services/widget/container";
|
||||||
|
|
||||||
|
export default function Component({ service }) {
|
||||||
|
const [todos, setTodos] = useState([]);
|
||||||
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
|
||||||
|
function handleChange(e) {
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const localStorageTodos = localStorage.getItem("todos");
|
||||||
|
|
||||||
|
const parsedTodos = localStorageTodos !== null ? JSON.parse(localStorageTodos) : [];
|
||||||
|
|
||||||
|
setTodos(parsedTodos);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (todos.length === 0) return;
|
||||||
|
localStorage.setItem("todos", JSON.stringify(todos));
|
||||||
|
}, [todos]);
|
||||||
|
|
||||||
|
function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (inputValue.trim() === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTodos([...todos, inputValue]);
|
||||||
|
setInputValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = (index) => {
|
||||||
|
const newTodos = [...todos];
|
||||||
|
newTodos.splice(index, 1);
|
||||||
|
setTodos(newTodos);
|
||||||
|
|
||||||
|
if (newTodos.length === 0) {
|
||||||
|
localStorage.removeItem("todos");
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("todos", JSON.stringify(newTodos));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<div>
|
||||||
|
{todos.map((todo, index) => (
|
||||||
|
<div key={`${todo}_${index}`} className="flex flex-row items-center">
|
||||||
|
<input type="checkbox" className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1" />
|
||||||
|
<input
|
||||||
|
className={classNames(
|
||||||
|
"bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center text-center p-1",
|
||||||
|
inputValue === undefined ? "animate-pulse" : "",
|
||||||
|
"service-block",
|
||||||
|
)}
|
||||||
|
type="text"
|
||||||
|
value={todo}
|
||||||
|
onChange={handleChange}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<button type="button" onClick={() => handleDelete(index)}>
|
||||||
|
<MdOutlineDelete />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<form className="flex flex-row items-center">
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 w-4 h-4 flex flex-col items-center justify-center text-center p-1 border border-[#6b7280]"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
value="+"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className={classNames(
|
||||||
|
"bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center text-center p-1",
|
||||||
|
inputValue === undefined ? "animate-pulse" : "",
|
||||||
|
"service-block",
|
||||||
|
)}
|
||||||
|
type="text"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
10
src/widgets/todo/widget.js
Normal file
10
src/widgets/todo/widget.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const widget = {
|
||||||
|
mappings: {
|
||||||
|
"todo/latest": {
|
||||||
|
endpoint: "todo/latest",
|
||||||
|
validate: ["data"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
||||||
@ -88,6 +88,7 @@ import strelaysrv from "./strelaysrv/widget";
|
|||||||
import tailscale from "./tailscale/widget";
|
import tailscale from "./tailscale/widget";
|
||||||
import tautulli from "./tautulli/widget";
|
import tautulli from "./tautulli/widget";
|
||||||
import tdarr from "./tdarr/widget";
|
import tdarr from "./tdarr/widget";
|
||||||
|
import todo from "./todo/widget";
|
||||||
import traefik from "./traefik/widget";
|
import traefik from "./traefik/widget";
|
||||||
import transmission from "./transmission/widget";
|
import transmission from "./transmission/widget";
|
||||||
import tubearchivist from "./tubearchivist/widget";
|
import tubearchivist from "./tubearchivist/widget";
|
||||||
@ -193,6 +194,7 @@ const widgets = {
|
|||||||
tailscale,
|
tailscale,
|
||||||
tautulli,
|
tautulli,
|
||||||
tdarr,
|
tdarr,
|
||||||
|
todo,
|
||||||
traefik,
|
traefik,
|
||||||
transmission,
|
transmission,
|
||||||
tubearchivist,
|
tubearchivist,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user