diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 91df4dd8..9c9d05ce 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -76,6 +76,7 @@ public function share(Request $request): array ...parent::share($request), ...$data, 'name' => config('app.name'), + 'version' => config('app.version'), 'quote' => ['message' => trim($message), 'author' => trim($author)], 'auth' => [ 'user' => $user, diff --git a/resources/js/components/app-command.tsx b/resources/js/components/app-command.tsx index e942e564..d60881d7 100644 --- a/resources/js/components/app-command.tsx +++ b/resources/js/components/app-command.tsx @@ -21,7 +21,7 @@ export default function AppCommand() { return (
- + diff --git a/resources/js/components/app-header.tsx b/resources/js/components/app-header.tsx index e9910ef1..e57a70a8 100644 --- a/resources/js/components/app-header.tsx +++ b/resources/js/components/app-header.tsx @@ -6,6 +6,7 @@ import AppCommand from '@/components/app-command'; import { SiteSwitch } from '@/components/site-switch'; import { usePage } from '@inertiajs/react'; import { SharedData } from '@/types'; +import Refresh from '@/components/refresh'; export function AppHeader() { const page = usePage(); @@ -26,7 +27,10 @@ export function AppHeader() { )}
- +
+ + +
); } diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index 8ea67c39..3bfda8d1 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -12,12 +12,13 @@ import { SidebarMenuSub, SidebarMenuSubItem, } from '@/components/ui/sidebar'; -import { type NavItem } from '@/types'; -import { Link } from '@inertiajs/react'; +import { type NavItem, SharedData } from '@/types'; +import { Link, usePage } from '@inertiajs/react'; import { BookOpen, ChevronRightIcon, CogIcon, Folder, MousePointerClickIcon, ServerIcon, ZapIcon } from 'lucide-react'; import AppLogo from './app-logo'; import { Icon } from '@/components/icon'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; const mainNavItems: NavItem[] = [ { @@ -56,6 +57,8 @@ const footerNavItems: NavItem[] = [ ]; export function AppSidebar({ secondNavItems, secondNavTitle }: { secondNavItems?: NavItem[]; secondNavTitle?: string }) { + const page = usePage(); + return ( {/* This is the first sidebar */} @@ -67,7 +70,12 @@ export function AppSidebar({ secondNavItems, secondNavTitle }: { secondNavItems? - + + + + + {page.props.version} + diff --git a/resources/js/components/refresh.tsx b/resources/js/components/refresh.tsx new file mode 100644 index 00000000..17c79307 --- /dev/null +++ b/resources/js/components/refresh.tsx @@ -0,0 +1,71 @@ +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; +import { Button } from '@/components/ui/button'; +import { ChevronDownIcon, RefreshCwIcon } from 'lucide-react'; +import { useEffect, useState } from 'react'; +import { router } from '@inertiajs/react'; + +export default function Refresh() { + const [poll, setPoll] = useState<{ + stop: VoidFunction; + start: VoidFunction; + }>(); + const [polling, setPolling] = useState(false); + const storedInterval = (localStorage.getItem('refresh_interval') as '5' | '10' | '30' | '60' | '0') || '10'; + const [refreshInterval, setRefreshInterval] = useState<5 | 10 | 30 | 60 | 0>( + storedInterval === '0' ? 0 : (parseInt(storedInterval) as 5 | 10 | 30 | 60), + ); + const intervalLabels = { + 5: '5s', + 10: '10s', + 30: '30s', + 60: '1m', + 0: 'OFF', + }; + + const refresh = () => { + router.reload({ + onStart: () => { + setPolling(true); + }, + onFinish: () => { + setPolling(false); + }, + }); + }; + + useEffect(() => { + poll?.stop(); + if (refreshInterval > 0) { + setPoll(router.poll(refreshInterval * 1000)); + } else { + poll?.stop(); + setPoll(undefined); + } + localStorage.setItem('refresh_interval', refreshInterval.toString()); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [refreshInterval]); + + return ( +
+ + + + + + + setRefreshInterval(5)}>5s + setRefreshInterval(10)}>10s + setRefreshInterval(30)}>30s + setRefreshInterval(60)}>1m + setRefreshInterval(0)}>OFF + + +
+ ); +} diff --git a/resources/js/layouts/auth/layout.tsx b/resources/js/layouts/auth/layout.tsx index d1f7a8ef..d19de12a 100644 --- a/resources/js/layouts/auth/layout.tsx +++ b/resources/js/layouts/auth/layout.tsx @@ -1,6 +1,7 @@ import AppLogoIcon from '@/components/app-logo-icon'; -import { Link } from '@inertiajs/react'; +import { Link, usePage } from '@inertiajs/react'; import { type PropsWithChildren } from 'react'; +import { SharedData } from '@/types'; interface AuthLayoutProps { name?: string; @@ -9,6 +10,7 @@ interface AuthLayoutProps { } export default function AuthLayout({ children, title, description }: PropsWithChildren) { + const page = usePage(); return (
@@ -27,6 +29,16 @@ export default function AuthLayout({ children, title, description }: PropsWithCh
{children} +
+ VitoDeploy{' '} + + {page.props.version} + +
diff --git a/resources/js/layouts/server/layout.tsx b/resources/js/layouts/server/layout.tsx index 04aadfb4..f138918c 100644 --- a/resources/js/layouts/server/layout.tsx +++ b/resources/js/layouts/server/layout.tsx @@ -26,15 +26,13 @@ import { ReactNode } from 'react'; import { Server } from '@/types/server'; import ServerHeader from '@/pages/servers/components/header'; import Layout from '@/layouts/app/layout'; -import { usePage, usePoll } from '@inertiajs/react'; +import { usePage } from '@inertiajs/react'; import { Site } from '@/types/site'; import PHPIcon from '@/icons/php'; import NodeIcon from '@/icons/node'; import siteHelper from '@/lib/site-helper'; export default function ServerLayout({ children }: { children: ReactNode }) { - usePoll(7000); - const page = usePage<{ server: Server; site?: Site; diff --git a/resources/js/types/index.d.ts b/resources/js/types/index.d.ts index bc902c0f..672fd851 100644 --- a/resources/js/types/index.d.ts +++ b/resources/js/types/index.d.ts @@ -104,6 +104,7 @@ export interface Configs { export interface SharedData { name: string; + version: string; quote: { message: string; author: string }; auth: Auth; ziggy: Config & { location: string };