2025-04-11 17:35:06 +02:00
"use client" ;
2025-04-11 14:53:00 +02:00
import { AppSidebar } from "@/components/app-sidebar"
import {
Breadcrumb ,
BreadcrumbItem ,
BreadcrumbLink ,
BreadcrumbList ,
BreadcrumbPage ,
BreadcrumbSeparator ,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import {
SidebarInset ,
SidebarProvider ,
SidebarTrigger ,
} from "@/components/ui/sidebar"
2025-04-11 16:03:56 +02:00
import { Button } from "@/components/ui/button"
2025-04-11 22:55:17 +02:00
import { Plus , Link , Home , Trash2 } from "lucide-react" // Importiere Icons
2025-04-11 16:03:56 +02:00
import {
Card ,
CardContent ,
CardDescription ,
CardFooter ,
CardHeader ,
CardTitle ,
} from "@/components/ui/card"
import {
2025-04-11 22:55:17 +02:00
Pagination ,
PaginationContent ,
PaginationEllipsis ,
PaginationItem ,
PaginationLink ,
PaginationNext ,
PaginationPrevious ,
} from "@/components/ui/pagination"
2025-04-11 16:19:02 +02:00
import {
2025-04-11 22:55:17 +02:00
AlertDialog ,
AlertDialogAction ,
AlertDialogCancel ,
AlertDialogContent ,
AlertDialogDescription ,
AlertDialogFooter ,
AlertDialogHeader ,
AlertDialogTitle ,
AlertDialogTrigger ,
2025-04-11 16:19:02 +02:00
} from "@/components/ui/alert-dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
2025-04-11 14:53:00 +02:00
2025-04-11 22:55:17 +02:00
import { useState , useEffect } from "react" ;
2025-04-11 17:35:06 +02:00
import axios from 'axios' ;
2025-04-11 14:53:00 +02:00
export default function Dashboard() {
2025-04-11 17:35:06 +02:00
const [ name , setName ] = useState ( "" ) ;
const [ description , setDescription ] = useState ( "" ) ;
const [ icon , setIcon ] = useState ( "" ) ;
const [ publicURL , setPublicURL ] = useState ( "" ) ;
const [ localURL , setLocalURL ] = useState ( "" ) ;
2025-04-11 22:55:17 +02:00
const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
const [ maxPage , setMaxPage ] = useState ( 1 ) ;
const [ applications , setApplications ] = useState ( [ ] ) ;
2025-04-11 17:35:06 +02:00
const add = async ( ) = > {
try {
const response = await axios . post ( '/api/applications/add' , { name , description , icon , publicURL , localURL } ) ;
2025-04-11 22:55:17 +02:00
getApplications ( ) ;
} catch ( error : any ) {
console . log ( error . response . data ) ;
}
}
const getApplications = async ( ) = > {
try {
const response = await axios . post ( '/api/applications/get' , { page : currentPage } ) ;
setApplications ( response . data . applications ) ;
setMaxPage ( response . data . maxPage ) ;
} catch ( error : any ) {
console . log ( error . response ) ;
}
}
useEffect ( ( ) = > {
getApplications ( ) ;
} , [ currentPage ] ) ;
const handlePrevious = ( ) = > {
setCurrentPage ( prev = > Math . max ( 1 , prev - 1 ) ) ;
}
const handleNext = ( ) = > {
setCurrentPage ( prev = > Math . min ( maxPage , prev + 1 ) ) ;
}
const deleteApplication = async ( id : number ) = > {
try {
await axios . post ( '/api/applications/delete' , { id } ) ;
getApplications ( ) ;
2025-04-11 17:35:06 +02:00
} catch ( error : any ) {
console . log ( error . response . data ) ;
}
}
2025-04-11 14:53:00 +02:00
return (
< SidebarProvider >
< AppSidebar / >
< SidebarInset >
< header className = "flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12" >
< div className = "flex items-center gap-2 px-4" >
< SidebarTrigger className = "-ml-1" / >
< Separator orientation = "vertical" className = "mr-2 h-4" / >
< Breadcrumb >
< BreadcrumbList >
< BreadcrumbItem className = "hidden md:block" >
2025-04-11 16:03:56 +02:00
< BreadcrumbPage > / < / BreadcrumbPage >
2025-04-11 14:53:00 +02:00
< / BreadcrumbItem >
< BreadcrumbSeparator className = "hidden md:block" / >
< BreadcrumbItem >
< BreadcrumbPage > Customization < / BreadcrumbPage >
< / BreadcrumbItem >
< BreadcrumbSeparator className = "hidden md:block" / >
< BreadcrumbItem >
< BreadcrumbPage > Applications < / BreadcrumbPage >
< / BreadcrumbItem >
< / BreadcrumbList >
< / Breadcrumb >
< / div >
< / header >
2025-04-11 16:03:56 +02:00
< div className = "pl-4 pr-4" >
< div className = "flex justify-between" >
< span className = "text-2xl font-semibold" > Your Applications < / span >
2025-04-11 16:19:02 +02:00
< AlertDialog >
2025-04-11 22:55:17 +02:00
< AlertDialogTrigger asChild >
< Button variant = "outline" size = "icon" >
< Plus / >
< / Button >
< / AlertDialogTrigger >
< AlertDialogContent >
< AlertDialogHeader >
< AlertDialogTitle > Add an application < / AlertDialogTitle >
< AlertDialogDescription >
< div className = "space-y-4 pt-4" >
< div className = "grid w-full items-center gap-1.5" >
< Label htmlFor = "name" > Name < / Label >
< Input id = "name" type = "text" placeholder = "e.g. Portainer" onChange = { ( e ) = > setName ( e . target . value ) } / >
< / div >
< div className = "grid w-full items-center gap-1.5" >
< Label htmlFor = "description" > Description < span className = "text-stone-600" > ( optional ) < / span > < / Label >
< Textarea id = "description" placeholder = "e.g. Portainer is a self-hosted, open-source platform for managing Docker containers." onChange = { ( e ) = > setDescription ( e . target . value ) } / >
< / div >
< div className = "grid w-full items-center gap-1.5" >
< Label htmlFor = "icon" > Icon < / Label >
< Input id = "icon" type = "text" placeholder = "e.g. https://www.portainer.io/hubfs/portainer-logo-black.svg" onChange = { ( e ) = > setIcon ( e . target . value ) } / >
< / div >
< div className = "grid w-full items-center gap-1.5" >
< Label htmlFor = "publicURL" > Public URL < / Label >
< Input id = "publicURL" type = "text" placeholder = "e.g. https://portainer.lastname.com" onChange = { ( e ) = > setPublicURL ( e . target . value ) } / >
< / div >
< div className = "grid w-full items-center gap-1.5" >
< Label htmlFor = "localURL" > Local URL < span className = "text-stone-600" > ( optional ) < / span > < / Label >
< Input id = "localURL" type = "text" placeholder = "e.g. http://localhost:3000" onChange = { ( e ) = > setLocalURL ( e . target . value ) } / >
< / div >
< / div >
< / AlertDialogDescription >
< / AlertDialogHeader >
< AlertDialogFooter >
< AlertDialogCancel > Cancel < / AlertDialogCancel >
< Button onClick = { add } > Add < / Button >
< / AlertDialogFooter >
< / AlertDialogContent >
< / AlertDialog >
2025-04-11 16:03:56 +02:00
< / div >
< br / >
2025-04-11 22:55:17 +02:00
{ applications . map ( ( app ) = > (
< Card key = { app . id } className = "w-full mb-4" >
< CardHeader >
< div className = "flex items-center justify-between w-full" >
< div className = "flex items-center" >
< div className = "w-16 h-16 flex-shrink-0 flex items-center justify-center rounded-md" >
{ app . icon ? (
< img src = { app . icon } alt = { app . name } className = "w-full h-full object-contain rounded-md" / >
) : (
< span className = "text-gray-500 text-xs" > Image < / span >
) }
< / div >
< div className = "ml-4" >
< CardTitle className = "text-2xl font-bold" > { app . name } < / CardTitle >
< CardDescription className = "text-md" > { app . description } < / CardDescription >
< / div >
2025-04-11 16:03:56 +02:00
< / div >
2025-04-11 22:55:17 +02:00
< div className = "flex flex-col items-end justify-start space-y-2 w-[270px]" >
< div className = "flex items-center gap-2 w-full" >
< div className = "flex flex-col space-y-2 flex-grow" >
< Button
variant = "outline"
className = "gap-2 w-full"
onClick = { ( ) = > window . open ( app . publicURL , "_blank" ) }
>
< Link className = "h-4 w-4" / >
Open Public URL
< / Button >
{ app . localURL && (
< Button
variant = "outline"
className = "gap-2 w-full"
onClick = { ( ) = > window . open ( app . localURL , "_blank" ) }
>
< Home className = "h-4 w-4" / >
Open Local URL
< / Button >
) }
< / div >
< Button
variant = "destructive"
size = "icon"
className = "h-[72px] w-10"
onClick = { ( ) = > deleteApplication ( app . id ) }
>
< Trash2 className = "h-4 w-4" / >
< / Button >
< / div >
2025-04-11 16:03:56 +02:00
< / div >
< / div >
2025-04-11 22:55:17 +02:00
< / CardHeader >
< / Card >
) ) }
< div className = "pt-4" >
< Pagination >
< PaginationContent >
< PaginationItem >
< PaginationPrevious
href = "#"
onClick = { handlePrevious }
isActive = { currentPage > 1 }
/ >
< / PaginationItem >
< PaginationItem >
< PaginationLink isActive > { currentPage } < / PaginationLink >
< / PaginationItem >
< PaginationItem >
< PaginationNext
href = "#"
onClick = { handleNext }
isActive = { currentPage < maxPage }
/ >
< / PaginationItem >
< / PaginationContent >
< / Pagination >
< / div >
2025-04-11 14:53:00 +02:00
< / div >
< / SidebarInset >
< / SidebarProvider >
)
}