Add Application UI

This commit is contained in:
headlessdev 2025-04-11 16:19:02 +02:00
parent 9bda3e7394
commit 638adac706
5 changed files with 275 additions and 19 deletions

View File

@ -30,8 +30,21 @@ import {
PaginationItem, PaginationItem,
PaginationLink, PaginationLink,
PaginationNext, PaginationNext,
PaginationPrevious, PaginationPrevious, } from "@/components/ui/pagination"
} from "@/components/ui/pagination" import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
export default function Dashboard() { export default function Dashboard() {
return ( return (
@ -62,9 +75,46 @@ export default function Dashboard() {
<div className="pl-4 pr-4"> <div className="pl-4 pr-4">
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-2xl font-semibold">Your Applications</span> <span className="text-2xl font-semibold">Your Applications</span>
<Button variant="outline" size="icon"> <AlertDialog>
<Plus /> <AlertDialogTrigger asChild>
</Button> <Button variant="outline" size="icon">
<Plus />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Add an application</AlertDialogTitle>
<AlertDialogDescription>
<p className="space-y-4 pt-4">
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="picture">Name</Label>
<Input id="name" type="text" placeholder="e.g. Portainer" />
</div>
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="picture">Description <span className="text-stone-600">(optional)</span></Label>
<Textarea id="description" placeholder="e.g. Protainer is a self-hosted, open-source platform for managing Docker containers and services via an intuitive web interface."/>
</div>
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="picture">Icon</Label>
<Input id="name" type="text" placeholder="e.g. https://www.portainer.io/hubfs/portainer-logo-black.svg" />
</div>
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="picture">Public URL</Label>
<Input id="name" type="text" placeholder="e.g. https://portainer.lastname.com" />
</div>
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="picture">Local URL</Label>
<Input id="name" type="text" placeholder="e.g. hhtp://localhost:3000" />
</div>
</p>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Add</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div> </div>
<br /> <br />
<Card className="w-full"> <Card className="w-full">
@ -92,20 +142,21 @@ export default function Dashboard() {
</div> </div>
</CardHeader> </CardHeader>
</Card> </Card>
<div className="pt-4">
<Pagination> <Pagination>
<PaginationContent> <PaginationContent>
<PaginationItem> <PaginationItem>
<PaginationPrevious href="#" /> <PaginationPrevious href="#" />
</PaginationItem> </PaginationItem>
<PaginationItem> <PaginationItem>
<PaginationLink href="#">1</PaginationLink> <PaginationLink href="#">1</PaginationLink>
</PaginationItem> </PaginationItem>
<PaginationItem> <PaginationItem>
<PaginationNext href="#" /> <PaginationNext href="#" />
</PaginationItem> </PaginationItem>
</PaginationContent> </PaginationContent>
</Pagination> </Pagination>
</div>
</div> </div>
</SidebarInset> </SidebarInset>
</SidebarProvider> </SidebarProvider>

View File

@ -0,0 +1,157 @@
"use client"
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
function AlertDialog({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}
function AlertDialogTrigger({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
return (
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
)
}
function AlertDialogPortal({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
return (
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
)
}
function AlertDialogOverlay({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
return (
<AlertDialogPrimitive.Overlay
data-slot="alert-dialog-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
)}
{...props}
/>
)
}
function AlertDialogContent({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
return (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
data-slot="alert-dialog-content"
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className
)}
{...props}
/>
</AlertDialogPortal>
)
}
function AlertDialogHeader({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
)
}
function AlertDialogFooter({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
)}
{...props}
/>
)
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn("text-lg font-semibold", className)}
{...props}
/>
)
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}
function AlertDialogAction({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
return (
<AlertDialogPrimitive.Action
className={cn(buttonVariants(), className)}
{...props}
/>
)
}
function AlertDialogCancel({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
return (
<AlertDialogPrimitive.Cancel
className={cn(buttonVariants({ variant: "outline" }), className)}
{...props}
/>
)
}
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}

View File

@ -0,0 +1,18 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
<textarea
data-slot="textarea"
className={cn(
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
{...props}
/>
)
}
export { Textarea }

29
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "corecontrol", "name": "corecontrol",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.7",
"@radix-ui/react-dialog": "^1.1.7", "@radix-ui/react-dialog": "^1.1.7",
"@radix-ui/react-label": "^2.1.3", "@radix-ui/react-label": "^2.1.3",
"@radix-ui/react-separator": "^1.1.3", "@radix-ui/react-separator": "^1.1.3",
@ -616,6 +617,34 @@
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@radix-ui/react-alert-dialog": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.7.tgz",
"integrity": "sha512-7Gx1gcoltd0VxKoR8mc+TAVbzvChJyZryZsTam0UhoL92z0L+W8ovxvcgvd+nkz24y7Qc51JQKBAGe4+825tYw==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dialog": "1.1.7",
"@radix-ui/react-primitive": "2.0.3",
"@radix-ui/react-slot": "1.2.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-arrow": { "node_modules/@radix-ui/react-arrow": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.3.tgz",

View File

@ -9,6 +9,7 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.7",
"@radix-ui/react-dialog": "^1.1.7", "@radix-ui/react-dialog": "^1.1.7",
"@radix-ui/react-label": "^2.1.3", "@radix-ui/react-label": "^2.1.3",
"@radix-ui/react-separator": "^1.1.3", "@radix-ui/react-separator": "^1.1.3",