Skip to content

Commit 04e8dd6

Browse files
committed
✨ Tambah komponen baru dan perbaiki struktur kode:
- **AppFooter**: Menambahkan komponen footer baru di `app-footer.tsx` untuk menampilkan informasi versi dan tanggal pembaruan (baris 1-34). - **AppSidebarHeader**: Memperbaiki tata letak dengan menambahkan dropdown untuk pengaturan tampilan dan toggle layar penuh (baris 1-18). - **AppearanceDropdown**: Mengubah dropdown tampilan untuk menyertakan pengaturan tema dan menambahkan komponen `ThemeCustomizer` (baris 1-73). - **AvatarTrigger**: Menambahkan komponen avatar minimal untuk dropdown (baris 1-101). - **FullscreenToggle**: Menambahkan toggle untuk mode layar penuh (baris 1-46). - **NavUser**: Mengganti `UserInfo` dengan `AvatarTrigger` untuk menampilkan avatar pengguna (baris 1-34). - **UserInfo**: Memperbaiki logika pemuatan gambar dan menambahkan fallback untuk inisial (baris 1-105). - **UserMenuContent**: Menambahkan label aksesibilitas dan animasi pada dropdown menu pengguna (baris 26-28). - **AppSidebarLayout**: Menambahkan `AppFooter` ke dalam layout sidebar (baris 1-7). - **Sidebar**: Menambahkan efek bayangan pada sidebar untuk tampilan yang lebih menarik (baris 205-205).
1 parent f15b3a0 commit 04e8dd6

14 files changed

Lines changed: 445 additions & 127 deletions

resources/js/components/app-content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function AppContent({
1616

1717
return (
1818
<main
19-
className="mx-auto flex h-full w-full max-w-7xl flex-1 flex-col gap-4 rounded-xl"
19+
className="mx-auto flex h-full w-full max-w-7xl flex-1 flex-col gap-4"
2020
{...props}
2121
>
2222
{children}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { usePage } from '@inertiajs/react';
2+
3+
interface PageProps extends Record<string, unknown> {
4+
version: string;
5+
updated_on: string;
6+
}
7+
8+
export default function AppFooter() {
9+
const { version, updated_on } = usePage<PageProps>().props;
10+
11+
return (
12+
<footer className="border-t bg-background/95 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/60">
13+
<div className="container mx-auto flex flex-col items-center justify-between gap-3 sm:flex-row sm:gap-4">
14+
{/* Copyright */}
15+
<p className="text-center text-xs text-muted-foreground sm:text-left">
16+
&copy; {new Date().getFullYear()} PonpesHub. All rights
17+
reserved.
18+
</p>
19+
20+
{/* Version Info */}
21+
<div className="flex flex-wrap justify-center gap-x-4 gap-y-1 text-xs text-muted-foreground">
22+
<div className="flex items-center gap-1.5 whitespace-nowrap">
23+
<span className="font-medium">Version:</span>
24+
<span className="font-mono">{version}</span>
25+
</div>
26+
<div className="flex items-center gap-1.5 whitespace-nowrap">
27+
<span className="font-medium">Updated:</span>
28+
<span className="font-mono">{updated_on}</span>
29+
</div>
30+
</div>
31+
</div>
32+
</footer>
33+
);
34+
}

resources/js/components/app-header.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Breadcrumbs } from '@/components/breadcrumbs';
22
import { Icon } from '@/components/icon';
3-
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
43
import { Button } from '@/components/ui/button';
54
import {
65
DropdownMenu,
@@ -26,8 +25,9 @@ import {
2625
TooltipProvider,
2726
TooltipTrigger,
2827
} from '@/components/ui/tooltip';
28+
import { UserInfo } from '@/components/user-info';
2929
import { UserMenuContent } from '@/components/user-menu-content';
30-
import { useInitials } from '@/hooks/use-initials';
30+
// useInitials not needed here; UserInfo handles initials
3131
import { cn } from '@/lib/utils';
3232
import { dashboard } from '@/routes';
3333
import { type BreadcrumbItem, type NavItem, type SharedData } from '@/types';
@@ -67,7 +67,7 @@ interface AppHeaderProps {
6767
export function AppHeader({ breadcrumbs = [] }: AppHeaderProps) {
6868
const page = usePage<SharedData>();
6969
const { auth } = page.props;
70-
const getInitials = useInitials();
70+
// UserInfo handles initials and fallbacks now
7171
return (
7272
<>
7373
<div className="border-b border-sidebar-border/80">
@@ -244,15 +244,11 @@ export function AppHeader({ breadcrumbs = [] }: AppHeaderProps) {
244244
variant="ghost"
245245
className="size-10 rounded-full p-1"
246246
>
247-
<Avatar className="size-8 overflow-hidden rounded-full">
248-
<AvatarImage
249-
src={auth.user.avatar}
250-
alt={auth.user.name}
251-
/>
252-
<AvatarFallback className="rounded-lg bg-neutral-200 text-black dark:bg-neutral-700 dark:text-white">
253-
{getInitials(auth.user.name)}
254-
</AvatarFallback>
255-
</Avatar>
247+
{/* Reuse UserInfo component for header avatar (includes blur-up + fallbacks) */}
248+
<UserInfo
249+
user={auth.user}
250+
className="size-8 overflow-hidden rounded-full"
251+
/>
256252
</Button>
257253
</DropdownMenuTrigger>
258254
<DropdownMenuContent className="w-56" align="end">

resources/js/components/app-sidebar-header.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import AppearanceDropdown from '@/components/appearance-dropdown';
2+
import FullscreenToggle from '@/components/fullscreen-toggle';
3+
14
import { Breadcrumbs } from '@/components/breadcrumbs';
25
import { SidebarTrigger } from '@/components/ui/sidebar';
36
import { type BreadcrumbItem as BreadcrumbItemType } from '@/types';
7+
import { NavUser } from './nav-user';
48

59
export function AppSidebarHeader({
610
breadcrumbs = [],
@@ -9,9 +13,18 @@ export function AppSidebarHeader({
913
}) {
1014
return (
1115
<header className="flex h-16 shrink-0 items-center gap-2 border-b border-sidebar-border/50 px-6 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 md:px-4">
12-
<div className="flex items-center gap-2">
13-
<SidebarTrigger className="-ml-1" />
14-
<Breadcrumbs breadcrumbs={breadcrumbs} />
16+
<div className="flex h-full w-full items-center justify-between">
17+
<div className="flex items-center gap-2">
18+
<SidebarTrigger className="-ml-1" />
19+
<Breadcrumbs breadcrumbs={breadcrumbs} />
20+
</div>
21+
<div>
22+
<div className="flex items-center gap-2">
23+
<AppearanceDropdown />
24+
<FullscreenToggle />
25+
<NavUser />
26+
</div>
27+
</div>
1528
</div>
1629
</header>
1730
);
Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
1-
import { NavFooter } from '@/components/nav-footer';
21
import { NavMain } from '@/components/nav-main';
3-
import { NavUser } from '@/components/nav-user';
42
import {
53
Sidebar,
64
SidebarContent,
7-
SidebarFooter,
85
SidebarHeader,
96
SidebarMenu,
107
SidebarMenuButton,
118
SidebarMenuItem,
129
} from '@/components/ui/sidebar';
1310
import { dashboard } from '@/routes';
14-
import { type MainNavItem, type NavItem } from '@/types';
11+
import { type MainNavItem } from '@/types';
1512
import { Link } from '@inertiajs/react';
16-
import {
17-
ArrowRight,
18-
Cog,
19-
LayoutGrid,
20-
Shield,
21-
Users,
22-
Wrench,
23-
} from 'lucide-react';
13+
import { Cog, Dot, LayoutGrid, Shield, Users, Wrench } from 'lucide-react';
2414
import AppLogo from './app-logo';
2515

2616
const mainNavItems: MainNavItem[] = [
@@ -46,22 +36,22 @@ const mainNavItems: MainNavItem[] = [
4636
{
4737
title: 'Profile',
4838
href: '/settings/profile',
49-
icon: ArrowRight,
39+
icon: Dot,
5040
},
5141
{
5242
title: 'Password',
5343
href: '/settings/password',
54-
icon: ArrowRight,
44+
icon: Dot,
5545
},
5646
{
5747
title: 'Two Factor Authentication',
5848
href: '/settings/two-factor',
59-
icon: ArrowRight,
49+
icon: Dot,
6050
},
6151
{
6252
title: 'Theme',
6353
href: '/settings/appearance',
64-
icon: ArrowRight,
54+
icon: Dot,
6555
},
6656
],
6757
},
@@ -72,46 +62,27 @@ const mainNavItems: MainNavItem[] = [
7262
{
7363
title: 'Audit Logs',
7464
href: '/admin/audit-logs',
75-
icon: ArrowRight,
65+
icon: Dot,
7666
},
7767
{
7868
title: 'Security Logs',
7969
href: '/admin/security-logs',
80-
icon: ArrowRight,
70+
icon: Dot,
8171
},
82-
],
83-
},
84-
{
85-
title: 'API',
86-
icon: Shield,
87-
subitem: [
8872
{
8973
title: 'API Documentation',
9074
href: '/admin/api-docs',
91-
icon: ArrowRight,
75+
icon: Dot,
9276
},
9377
{
9478
title: 'API Keys',
9579
href: '/admin/api-tokens',
96-
icon: ArrowRight,
80+
icon: Dot,
9781
},
9882
],
9983
},
10084
];
10185

102-
const footerNavItems: NavItem[] = [
103-
// {
104-
// title: 'Repository',
105-
// href: 'https://github.com/laravel/react-starter-kit',
106-
// icon: Folder,
107-
// },
108-
// {
109-
// title: 'Documentation',
110-
// href: 'https://laravel.com/docs/starter-kits#react',
111-
// icon: BookOpen,
112-
// },
113-
];
114-
11586
export function AppSidebar() {
11687
return (
11788
<Sidebar collapsible="icon" variant="inset">
@@ -130,11 +101,6 @@ export function AppSidebar() {
130101
<SidebarContent>
131102
<NavMain items={mainNavItems} />
132103
</SidebarContent>
133-
134-
<SidebarFooter>
135-
<NavFooter items={footerNavItems} className="mt-auto" />
136-
<NavUser />
137-
</SidebarFooter>
138104
</Sidebar>
139105
);
140106
}

resources/js/components/appearance-dropdown.tsx

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,73 @@
1+
import ThemeCustomizer from '@/components/theme-customizer';
12
import { Button } from '@/components/ui/button';
23
import {
34
DropdownMenu,
45
DropdownMenuContent,
5-
DropdownMenuItem,
66
DropdownMenuTrigger,
77
} from '@/components/ui/dropdown-menu';
88
import { useAppearance } from '@/hooks/use-appearance';
9-
import { Monitor, Moon, Sun } from 'lucide-react';
9+
import { Moon, Palette, Sun } from 'lucide-react';
1010
import { HTMLAttributes } from 'react';
1111

12-
export default function AppearanceToggleDropdown({
12+
export default function AppearanceDropdown({
1313
className = '',
1414
...props
1515
}: HTMLAttributes<HTMLDivElement>) {
1616
const { appearance, updateAppearance } = useAppearance();
1717

18-
const getCurrentIcon = () => {
19-
switch (appearance) {
20-
case 'dark':
21-
return <Moon className="h-5 w-5" />;
22-
case 'light':
23-
return <Sun className="h-5 w-5" />;
24-
default:
25-
return <Monitor className="h-5 w-5" />;
26-
}
18+
const isDark =
19+
appearance === 'dark' ||
20+
(typeof window !== 'undefined' &&
21+
appearance === 'system' &&
22+
window.matchMedia &&
23+
window.matchMedia('(prefers-color-scheme: dark)').matches);
24+
25+
const handleQuickToggle = (e?: React.MouseEvent) => {
26+
// Prevent the trigger from closing/opening the dropdown when toggling
27+
e?.stopPropagation();
28+
const newAppearance = isDark ? 'light' : 'dark';
29+
updateAppearance(newAppearance);
2730
};
2831

2932
return (
30-
<div className={className} {...props}>
33+
<div className={`flex items-center space-x-1 ${className}`} {...props}>
34+
{/* Quick toggle button */}
35+
<Button
36+
variant="ghost"
37+
size="icon"
38+
className="h-9 w-9 rounded-md"
39+
onClick={handleQuickToggle}
40+
aria-label={
41+
isDark ? 'Switch to light theme' : 'Switch to dark theme'
42+
}
43+
>
44+
{isDark ? (
45+
<Moon className="h-5 w-5" />
46+
) : (
47+
<Sun className="h-5 w-5" />
48+
)}
49+
</Button>
50+
51+
{/* Separate chevron trigger to open the customizer dropdown */}
3152
<DropdownMenu>
3253
<DropdownMenuTrigger asChild>
3354
<Button
3455
variant="ghost"
3556
size="icon"
36-
className="h-9 w-9 rounded-md"
57+
className="h-8 w-8 rounded-md p-1"
58+
aria-label="Open theme customizer"
3759
>
38-
{getCurrentIcon()}
39-
<span className="sr-only">Toggle theme</span>
60+
<Palette className="h-4 w-4" />
4061
</Button>
4162
</DropdownMenuTrigger>
42-
<DropdownMenuContent align="end">
43-
<DropdownMenuItem onClick={() => updateAppearance('light')}>
44-
<span className="flex items-center gap-2">
45-
<Sun className="h-5 w-5" />
46-
Light
47-
</span>
48-
</DropdownMenuItem>
49-
<DropdownMenuItem onClick={() => updateAppearance('dark')}>
50-
<span className="flex items-center gap-2">
51-
<Moon className="h-5 w-5" />
52-
Dark
53-
</span>
54-
</DropdownMenuItem>
55-
<DropdownMenuItem
56-
onClick={() => updateAppearance('system')}
57-
>
58-
<span className="flex items-center gap-2">
59-
<Monitor className="h-5 w-5" />
60-
System
61-
</span>
62-
</DropdownMenuItem>
63+
64+
<DropdownMenuContent align="end" className="w-80">
65+
<div className="p-4">
66+
<h3 className="mb-2 text-sm font-semibold">
67+
Customize theme
68+
</h3>
69+
<ThemeCustomizer />
70+
</div>
6371
</DropdownMenuContent>
6472
</DropdownMenu>
6573
</div>

0 commit comments

Comments
 (0)