diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index aa53c60..011d208 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,4 +1,5 @@ import AppCard from "@/components/app-card"; +import PlusButton from "@/components/plus-button"; export default function Dashboard() { const mockService = { @@ -16,6 +17,7 @@ export default function Dashboard() { return (
+
diff --git a/bun.lock b/bun.lock index 1ec00e2..609845a 100644 --- a/bun.lock +++ b/bun.lock @@ -12,10 +12,12 @@ "lucide-react": "^1.21.0", "motion": "^12.42.0", "next": "16.2.9", + "next-themes": "^0.4.6", "radix-ui": "^1.6.0", "react": "19.2.4", "react-dom": "19.2.4", "shadcn": "^4.12.0", + "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", "tw-animate-css": "^1.4.0", }, @@ -758,6 +760,8 @@ "next": ["next@16.2.9", "", { "dependencies": { "@next/env": "16.2.9", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.9", "@next/swc-darwin-x64": "16.2.9", "@next/swc-linux-arm64-gnu": "16.2.9", "@next/swc-linux-arm64-musl": "16.2.9", "@next/swc-linux-x64-gnu": "16.2.9", "@next/swc-linux-x64-musl": "16.2.9", "@next/swc-win32-arm64-msvc": "16.2.9", "@next/swc-win32-x64-msvc": "16.2.9", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww=="], + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + "node-releases": ["node-releases@2.0.50", "", {}, "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg=="], "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], @@ -888,6 +892,8 @@ "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], diff --git a/components/plus-button.tsx b/components/plus-button.tsx new file mode 100644 index 0000000..196952a --- /dev/null +++ b/components/plus-button.tsx @@ -0,0 +1,147 @@ +"use client"; + +import { Plus } from "lucide-react"; +import { invoke } from "@tauri-apps/api/core"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { AnimatePresence, motion } from "motion/react"; +import { Field, FieldGroup, FieldLabel } from "./ui/field"; +import { Label } from "./ui/label"; +import { Input } from "./ui/input"; +import { Textarea } from "./ui/textarea"; +import { Checkbox } from "./ui/checkbox"; +import { useState } from "react"; + +export default function PlusButton() { + const [differentAddr, setDifferentAddr] = useState(false); + const [name, setName] = useState(""); + const [address, setAddress] = useState(""); + const [description, setDescription] = useState(""); + const [sshAddr, setSSHAddr] = useState(""); + + const onSubmit = async () => { + let tmp_description = undefined; + let tmp_ssh = undefined; + + if (!name.trim()) return; + if (!address.trim()) return; + if (description.trim()) tmp_description = description.trim(); + if (sshAddr.trim()) tmp_ssh = sshAddr.trim(); + + await invoke("new_app", { + name: name, + address: address, + description: description, + sshAddress: sshAddr, + }); + }; + + return ( + +
onSubmit()}> + + + + + + + + Create Service + + Create a new service. Click save when you're finished. + + + + + + setName(e.target.value.trim())} + defaultValue="Kubernetes" + /> + + + + setAddress(e.target.value.trim())} + defaultValue="192.168.1.10" + /> + + + +