forked from emdevelopa/Stellar_Payment_API
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSidebar.tsx
More file actions
194 lines (179 loc) · 8.06 KB
/
Copy pathSidebar.tsx
File metadata and controls
194 lines (179 loc) · 8.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useTranslations } from "next-intl";
import { motion } from "framer-motion";
import { memo, useCallback, useMemo } from "react";
function getNavItems(t: ReturnType<typeof useTranslations>) {
return [
{
label: t("overview"),
href: "/dashboard",
icon: (
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
</svg>
),
},
{
label: t("createPayment"),
href: "/create",
icon: (
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
),
highlight: true,
},
{
label: t("payments"),
href: "/payments",
icon: (
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
),
},
{
label: "History",
href: "/payment-history",
icon: (
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
),
},
{
label: t("webhookLogs"),
href: "/webhook-logs",
icon: (
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9h8M8 13h5m-7 8h12a2 2 0 002-2V7l-4-4H6a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
),
},
{
label: t("settings"),
href: "/settings",
icon: (
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
),
},
];
}
interface SidebarProps {
mobileOpen: boolean;
onMobileOpenChange: (open: boolean) => void;
}
const NavLinks = memo(function NavLinks({
pathname,
t,
onNavigate,
}: {
pathname: string;
t: ReturnType<typeof useTranslations>;
onNavigate?: () => void;
}) {
const navItems = useMemo(() => getNavItems(t), [t]);
return (
<nav aria-label="Dashboard navigation" className="flex flex-1 flex-col gap-1 px-4 py-6">
{navItems.map((item) => {
const isActive = pathname === item.href || pathname.startsWith(`${item.href}/`);
const isHighlight = "highlight" in item && item.highlight;
const isExternal = "external" in item && item.external;
if (isExternal) {
return (
<a key={item.href} href={item.href} target="_blank" rel="noopener noreferrer" onClick={onNavigate}
className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-[#6B6B6B] transition-colors duration-150 hover:bg-[var(--pluto-50)] hover:text-[var(--pluto-800)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pluto-300)]">
<span className="shrink-0">{item.icon}</span>
<span className="text-xs font-semibold tracking-wide">{item.label}</span>
<svg className="h-3 w-3 ml-auto opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg>
</a>
);
}
return (
<Link
key={item.href}
href={item.href}
prefetch={true}
onClick={onNavigate}
className={`flex items-center gap-3 rounded-lg px-3 py-2.5 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pluto-300)] ${
isActive
? "bg-[var(--pluto-500)] text-white"
: isHighlight
? "border border-[var(--pluto-200)] bg-[var(--pluto-50)] text-[var(--pluto-700)] hover:border-[var(--pluto-500)] hover:bg-[var(--pluto-500)] hover:text-white"
: "text-[#6B6B6B] hover:bg-[var(--pluto-100)] hover:text-[var(--pluto-800)]"
}`}
>
<span className="shrink-0">{item.icon}</span>
<span className="text-xs font-semibold tracking-wide">{item.label}</span>
</Link>
);
})}
<div className="mt-auto pt-4 border-t border-[#E8E8E8]">
<Link href="/" onClick={onNavigate}
className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-[#6B6B6B] transition-colors duration-150 hover:bg-[var(--pluto-50)] hover:text-[var(--pluto-800)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--pluto-300)]"
>
<svg className="h-4 w-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span className="text-xs font-semibold tracking-wide">Home</span>
</Link>
</div>
</nav>
);
});
NavLinks.displayName = "NavLinks";
export default function Sidebar({
mobileOpen,
onMobileOpenChange,
}: SidebarProps) {
const t = useTranslations("sidebar");
const pathname = usePathname();
const handleNavigate = useCallback(() => onMobileOpenChange(false), [onMobileOpenChange]);
// Note: Collapsible logic removed to fix linting as it's not currently used in the UI.
const chrome = (
<>
<div className="flex h-16 items-center border-b border-[#E8E8E8] px-6">
<Link href="/" className="font-display text-2xl tracking-tight" style={{ color: "var(--pluto-500)" }}>
Pluto
</Link>
</div>
<NavLinks
pathname={pathname}
t={t}
onNavigate={handleNavigate}
/>
</>
);
return (
<>
<aside
id="dashboard-sidebar-navigation"
className="sticky top-0 hidden h-screen w-[240px] shrink-0 flex-col border-r border-[#E8E8E8] bg-white lg:flex"
>
{chrome}
</aside>
<motion.div
initial={false}
id="dashboard-sidebar-mobile"
animate={{
opacity: mobileOpen ? 1 : 0,
pointerEvents: mobileOpen ? "auto" : "none",
}}
className="fixed inset-0 z-50 bg-black/40 backdrop-blur-sm lg:hidden"
onClick={() => onMobileOpenChange(false)}
/>
<motion.aside
initial={false}
animate={{ x: mobileOpen ? 0 : "-100%" }}
transition={{ type: "spring", stiffness: 320, damping: 32 }}
className="fixed inset-y-0 left-0 z-[60] flex w-[280px] flex-col border-r border-[#E8E8E8] bg-white lg:hidden"
>
{chrome}
</motion.aside>
</>
);
}