@victusvinceere/saas-admin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.d.mts +59 -0
- package/dist/components/index.d.ts +59 -0
- package/dist/components/index.js +281 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +252 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +281 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +252 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/admin-sidebar.tsx
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import Link from "next/link";
|
|
6
|
+
import { usePathname } from "next/navigation";
|
|
7
|
+
import {
|
|
8
|
+
LayoutDashboard,
|
|
9
|
+
Users,
|
|
10
|
+
BarChart3,
|
|
11
|
+
CreditCard,
|
|
12
|
+
Settings,
|
|
13
|
+
ArrowLeft,
|
|
14
|
+
ChevronsLeft,
|
|
15
|
+
ChevronsRight,
|
|
16
|
+
Shield
|
|
17
|
+
} from "lucide-react";
|
|
18
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
var defaultNavItems = [
|
|
20
|
+
{
|
|
21
|
+
title: "Overview",
|
|
22
|
+
url: "/admin",
|
|
23
|
+
icon: LayoutDashboard
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
title: "Users",
|
|
27
|
+
url: "/admin/users",
|
|
28
|
+
icon: Users
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: "Analytics",
|
|
32
|
+
url: "/admin/analytics",
|
|
33
|
+
icon: BarChart3
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: "Subscriptions",
|
|
37
|
+
url: "/admin/subscriptions",
|
|
38
|
+
icon: CreditCard
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
title: "Settings",
|
|
42
|
+
url: "/admin/settings",
|
|
43
|
+
icon: Settings
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
function AdminSidebar({
|
|
47
|
+
user,
|
|
48
|
+
navItems = defaultNavItems,
|
|
49
|
+
dashboardUrl = "/dashboard",
|
|
50
|
+
className
|
|
51
|
+
}) {
|
|
52
|
+
const pathname = usePathname();
|
|
53
|
+
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
|
54
|
+
const isActive = (url) => {
|
|
55
|
+
if (url === "/admin") {
|
|
56
|
+
return pathname === url;
|
|
57
|
+
}
|
|
58
|
+
return pathname === url || pathname.startsWith(url);
|
|
59
|
+
};
|
|
60
|
+
return /* @__PURE__ */ jsx(
|
|
61
|
+
"aside",
|
|
62
|
+
{
|
|
63
|
+
className: `hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:flex-col transition-all duration-300 ${isCollapsed ? "lg:w-16" : "lg:w-64"} ${className}`,
|
|
64
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex grow flex-col gap-y-5 overflow-y-auto border-r border-red-500/20 bg-red-500/5 pb-4", children: [
|
|
65
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-16 shrink-0 items-center border-b border-red-500/20 px-4 ${isCollapsed ? "justify-center px-2" : ""}`, children: /* @__PURE__ */ jsxs(Link, { href: "/admin", className: "flex items-center gap-2", children: [
|
|
66
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-red-500 text-white", children: /* @__PURE__ */ jsx(Shield, { className: "h-5 w-5" }) }),
|
|
67
|
+
!isCollapsed && /* @__PURE__ */ jsx("span", { className: "text-lg font-semibold", children: "Admin Panel" })
|
|
68
|
+
] }) }),
|
|
69
|
+
/* @__PURE__ */ jsx("nav", { className: "flex flex-1 flex-col px-2", children: /* @__PURE__ */ jsxs("ul", { className: "flex flex-1 flex-col gap-y-1", children: [
|
|
70
|
+
!isCollapsed && /* @__PURE__ */ jsx("li", { className: "mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: "Management" }),
|
|
71
|
+
navItems.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
72
|
+
Link,
|
|
73
|
+
{
|
|
74
|
+
href: item.url,
|
|
75
|
+
className: `group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${isActive(item.url) ? "bg-red-500 text-white" : "text-muted-foreground hover:bg-red-500/10 hover:text-red-600"} ${isCollapsed ? "justify-center px-2" : ""}`,
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ jsx(item.icon, { className: "h-5 w-5 shrink-0" }),
|
|
78
|
+
!isCollapsed && /* @__PURE__ */ jsx("span", { children: item.title })
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
) }, item.title)),
|
|
82
|
+
/* @__PURE__ */ jsx("li", { className: "mt-auto", children: /* @__PURE__ */ jsxs(
|
|
83
|
+
Link,
|
|
84
|
+
{
|
|
85
|
+
href: dashboardUrl,
|
|
86
|
+
className: `group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${isCollapsed ? "justify-center px-2" : ""}`,
|
|
87
|
+
children: [
|
|
88
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "h-5 w-5 shrink-0" }),
|
|
89
|
+
!isCollapsed && /* @__PURE__ */ jsx("span", { children: "Back to Dashboard" })
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
) })
|
|
93
|
+
] }) }),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t border-red-500/20 px-2 pt-4", children: [
|
|
95
|
+
/* @__PURE__ */ jsxs("div", { className: `flex items-center gap-3 rounded-lg px-3 py-2 ${isCollapsed ? "justify-center px-2" : ""}`, children: [
|
|
96
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-red-500/10 text-red-600", children: user.name?.[0]?.toUpperCase() || "A" }),
|
|
97
|
+
!isCollapsed && /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden", children: [
|
|
98
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: user.name }),
|
|
99
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.role.replace("_", " ") })
|
|
100
|
+
] })
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
"button",
|
|
104
|
+
{
|
|
105
|
+
onClick: () => setIsCollapsed(!isCollapsed),
|
|
106
|
+
className: `mt-2 flex w-full items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${isCollapsed ? "justify-center px-2" : ""}`,
|
|
107
|
+
children: isCollapsed ? /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
108
|
+
/* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" }),
|
|
109
|
+
/* @__PURE__ */ jsx("span", { children: "Collapse" })
|
|
110
|
+
] })
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
] })
|
|
114
|
+
] })
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/components/admin-header.tsx
|
|
120
|
+
import * as React2 from "react";
|
|
121
|
+
import Link2 from "next/link";
|
|
122
|
+
import { usePathname as usePathname2 } from "next/navigation";
|
|
123
|
+
import { Menu, Shield as Shield2, ChevronRight } from "lucide-react";
|
|
124
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
125
|
+
var defaultRouteLabels = {
|
|
126
|
+
admin: "Admin",
|
|
127
|
+
users: "Users",
|
|
128
|
+
analytics: "Analytics",
|
|
129
|
+
subscriptions: "Subscriptions",
|
|
130
|
+
settings: "Settings"
|
|
131
|
+
};
|
|
132
|
+
function AdminHeader({
|
|
133
|
+
onMenuClick,
|
|
134
|
+
routeLabels = defaultRouteLabels,
|
|
135
|
+
children
|
|
136
|
+
}) {
|
|
137
|
+
const pathname = usePathname2();
|
|
138
|
+
const getBreadcrumbs = () => {
|
|
139
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
140
|
+
const breadcrumbs2 = [];
|
|
141
|
+
let currentPath = "";
|
|
142
|
+
segments.forEach((segment, index) => {
|
|
143
|
+
currentPath += `/${segment}`;
|
|
144
|
+
const label = routeLabels[segment] || segment.charAt(0).toUpperCase() + segment.slice(1);
|
|
145
|
+
breadcrumbs2.push({
|
|
146
|
+
label,
|
|
147
|
+
href: currentPath,
|
|
148
|
+
isLast: index === segments.length - 1
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
return breadcrumbs2;
|
|
152
|
+
};
|
|
153
|
+
const breadcrumbs = getBreadcrumbs();
|
|
154
|
+
return /* @__PURE__ */ jsx2("header", { className: "flex h-16 shrink-0 items-center gap-2 border-b border-red-500/20 bg-red-500/5", children: /* @__PURE__ */ jsxs2("div", { className: "flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6", children: [
|
|
155
|
+
onMenuClick && /* @__PURE__ */ jsxs2(
|
|
156
|
+
"button",
|
|
157
|
+
{
|
|
158
|
+
onClick: onMenuClick,
|
|
159
|
+
className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-muted hover:text-foreground md:hidden",
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ jsx2(Menu, { className: "h-5 w-5" }),
|
|
162
|
+
/* @__PURE__ */ jsx2("span", { className: "sr-only", children: "Toggle menu" })
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
),
|
|
166
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 rounded-md bg-red-500/10 px-2 py-1 text-xs font-medium text-red-600 dark:text-red-400 mr-2", children: [
|
|
167
|
+
/* @__PURE__ */ jsx2(Shield2, { className: "h-3 w-3" }),
|
|
168
|
+
"ADMIN"
|
|
169
|
+
] }),
|
|
170
|
+
/* @__PURE__ */ jsx2("nav", { className: "flex items-center gap-1 text-sm", children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
|
|
171
|
+
index > 0 && /* @__PURE__ */ jsx2(ChevronRight, { className: "h-4 w-4 text-muted-foreground" }),
|
|
172
|
+
crumb.isLast ? /* @__PURE__ */ jsx2("span", { className: "font-medium text-foreground", children: crumb.label }) : /* @__PURE__ */ jsx2(
|
|
173
|
+
Link2,
|
|
174
|
+
{
|
|
175
|
+
href: crumb.href,
|
|
176
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
177
|
+
children: crumb.label
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
] }, crumb.href)) }),
|
|
181
|
+
/* @__PURE__ */ jsx2("div", { className: "flex-1" }),
|
|
182
|
+
children
|
|
183
|
+
] }) });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/components/admin-layout.tsx
|
|
187
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
188
|
+
function AdminLayout({
|
|
189
|
+
children,
|
|
190
|
+
user,
|
|
191
|
+
navItems,
|
|
192
|
+
dashboardUrl,
|
|
193
|
+
headerContent,
|
|
194
|
+
routeLabels
|
|
195
|
+
}) {
|
|
196
|
+
return /* @__PURE__ */ jsxs3("div", { className: "min-h-screen bg-background", children: [
|
|
197
|
+
/* @__PURE__ */ jsx3(
|
|
198
|
+
AdminSidebar,
|
|
199
|
+
{
|
|
200
|
+
user,
|
|
201
|
+
navItems,
|
|
202
|
+
dashboardUrl
|
|
203
|
+
}
|
|
204
|
+
),
|
|
205
|
+
/* @__PURE__ */ jsxs3("div", { className: "lg:pl-64", children: [
|
|
206
|
+
/* @__PURE__ */ jsx3(AdminHeader, { routeLabels, children: headerContent }),
|
|
207
|
+
/* @__PURE__ */ jsx3("main", { className: "p-4 sm:p-6 lg:p-8", children })
|
|
208
|
+
] })
|
|
209
|
+
] });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/components/stats-card.tsx
|
|
213
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
214
|
+
function StatsCard({
|
|
215
|
+
title,
|
|
216
|
+
value,
|
|
217
|
+
description,
|
|
218
|
+
icon: Icon,
|
|
219
|
+
trend,
|
|
220
|
+
className
|
|
221
|
+
}) {
|
|
222
|
+
return /* @__PURE__ */ jsxs4("div", { className: `rounded-lg border bg-card p-6 ${className}`, children: [
|
|
223
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between", children: [
|
|
224
|
+
/* @__PURE__ */ jsx4("p", { className: "text-sm font-medium text-muted-foreground", children: title }),
|
|
225
|
+
Icon && /* @__PURE__ */ jsx4("div", { className: "rounded-md bg-primary/10 p-2", children: /* @__PURE__ */ jsx4(Icon, { className: "h-4 w-4 text-primary" }) })
|
|
226
|
+
] }),
|
|
227
|
+
/* @__PURE__ */ jsxs4("div", { className: "mt-2", children: [
|
|
228
|
+
/* @__PURE__ */ jsx4("p", { className: "text-2xl font-bold", children: value }),
|
|
229
|
+
(description || trend) && /* @__PURE__ */ jsxs4("div", { className: "mt-1 flex items-center gap-2 text-sm", children: [
|
|
230
|
+
trend && /* @__PURE__ */ jsxs4(
|
|
231
|
+
"span",
|
|
232
|
+
{
|
|
233
|
+
className: trend.isPositive ? "text-green-600" : "text-red-600",
|
|
234
|
+
children: [
|
|
235
|
+
trend.isPositive ? "+" : "-",
|
|
236
|
+
Math.abs(trend.value),
|
|
237
|
+
"%"
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
),
|
|
241
|
+
description && /* @__PURE__ */ jsx4("span", { className: "text-muted-foreground", children: description })
|
|
242
|
+
] })
|
|
243
|
+
] })
|
|
244
|
+
] });
|
|
245
|
+
}
|
|
246
|
+
export {
|
|
247
|
+
AdminHeader,
|
|
248
|
+
AdminLayout,
|
|
249
|
+
AdminSidebar,
|
|
250
|
+
StatsCard
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/admin-sidebar.tsx","../../src/components/admin-header.tsx","../../src/components/admin-layout.tsx","../../src/components/stats-card.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\nimport {\n LayoutDashboard,\n Users,\n BarChart3,\n CreditCard,\n Settings,\n ArrowLeft,\n ChevronsLeft,\n ChevronsRight,\n Shield,\n type LucideIcon,\n} from \"lucide-react\";\n\nexport interface AdminNavItem {\n title: string;\n url: string;\n icon: LucideIcon;\n}\n\nexport interface AdminSidebarProps {\n user: {\n name: string;\n email: string;\n avatar?: string;\n role: string;\n };\n navItems?: AdminNavItem[];\n dashboardUrl?: string;\n className?: string;\n}\n\nconst defaultNavItems: AdminNavItem[] = [\n {\n title: \"Overview\",\n url: \"/admin\",\n icon: LayoutDashboard,\n },\n {\n title: \"Users\",\n url: \"/admin/users\",\n icon: Users,\n },\n {\n title: \"Analytics\",\n url: \"/admin/analytics\",\n icon: BarChart3,\n },\n {\n title: \"Subscriptions\",\n url: \"/admin/subscriptions\",\n icon: CreditCard,\n },\n {\n title: \"Settings\",\n url: \"/admin/settings\",\n icon: Settings,\n },\n];\n\nexport function AdminSidebar({\n user,\n navItems = defaultNavItems,\n dashboardUrl = \"/dashboard\",\n className,\n}: AdminSidebarProps) {\n const pathname = usePathname();\n const [isCollapsed, setIsCollapsed] = React.useState(false);\n\n const isActive = (url: string) => {\n if (url === \"/admin\") {\n return pathname === url;\n }\n return pathname === url || pathname.startsWith(url);\n };\n\n return (\n <aside\n className={`hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:flex-col transition-all duration-300 ${\n isCollapsed ? \"lg:w-16\" : \"lg:w-64\"\n } ${className}`}\n >\n <div className=\"flex grow flex-col gap-y-5 overflow-y-auto border-r border-red-500/20 bg-red-500/5 pb-4\">\n {/* Header */}\n <div className={`flex h-16 shrink-0 items-center border-b border-red-500/20 px-4 ${isCollapsed ? \"justify-center px-2\" : \"\"}`}>\n <Link href=\"/admin\" className=\"flex items-center gap-2\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-red-500 text-white\">\n <Shield className=\"h-5 w-5\" />\n </div>\n {!isCollapsed && (\n <span className=\"text-lg font-semibold\">Admin Panel</span>\n )}\n </Link>\n </div>\n\n {/* Navigation */}\n <nav className=\"flex flex-1 flex-col px-2\">\n <ul className=\"flex flex-1 flex-col gap-y-1\">\n {!isCollapsed && (\n <li className=\"mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n Management\n </li>\n )}\n {navItems.map((item) => (\n <li key={item.title}>\n <Link\n href={item.url}\n className={`group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${\n isActive(item.url)\n ? \"bg-red-500 text-white\"\n : \"text-muted-foreground hover:bg-red-500/10 hover:text-red-600\"\n } ${isCollapsed ? \"justify-center px-2\" : \"\"}`}\n >\n <item.icon className=\"h-5 w-5 shrink-0\" />\n {!isCollapsed && <span>{item.title}</span>}\n </Link>\n </li>\n ))}\n\n <li className=\"mt-auto\">\n <Link\n href={dashboardUrl}\n className={`group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${\n isCollapsed ? \"justify-center px-2\" : \"\"\n }`}\n >\n <ArrowLeft className=\"h-5 w-5 shrink-0\" />\n {!isCollapsed && <span>Back to Dashboard</span>}\n </Link>\n </li>\n </ul>\n </nav>\n\n {/* Footer */}\n <div className=\"border-t border-red-500/20 px-2 pt-4\">\n <div className={`flex items-center gap-3 rounded-lg px-3 py-2 ${isCollapsed ? \"justify-center px-2\" : \"\"}`}>\n <div className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-red-500/10 text-red-600\">\n {user.name?.[0]?.toUpperCase() || \"A\"}\n </div>\n {!isCollapsed && (\n <div className=\"flex-1 overflow-hidden\">\n <p className=\"truncate text-sm font-medium\">{user.name}</p>\n <p className=\"truncate text-xs text-muted-foreground\">\n {user.role.replace(\"_\", \" \")}\n </p>\n </div>\n )}\n </div>\n <button\n onClick={() => setIsCollapsed(!isCollapsed)}\n className={`mt-2 flex w-full items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${\n isCollapsed ? \"justify-center px-2\" : \"\"\n }`}\n >\n {isCollapsed ? (\n <ChevronsRight className=\"h-4 w-4\" />\n ) : (\n <>\n <ChevronsLeft className=\"h-4 w-4\" />\n <span>Collapse</span>\n </>\n )}\n </button>\n </div>\n </div>\n </aside>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\nimport { Menu, Shield, ChevronRight } from \"lucide-react\";\n\nexport interface AdminHeaderProps {\n onMenuClick?: () => void;\n routeLabels?: Record<string, string>;\n children?: React.ReactNode;\n}\n\nconst defaultRouteLabels: Record<string, string> = {\n admin: \"Admin\",\n users: \"Users\",\n analytics: \"Analytics\",\n subscriptions: \"Subscriptions\",\n settings: \"Settings\",\n};\n\nexport function AdminHeader({\n onMenuClick,\n routeLabels = defaultRouteLabels,\n children,\n}: AdminHeaderProps) {\n const pathname = usePathname();\n\n const getBreadcrumbs = () => {\n const segments = pathname.split(\"/\").filter(Boolean);\n const breadcrumbs: { label: string; href: string; isLast: boolean }[] = [];\n\n let currentPath = \"\";\n segments.forEach((segment, index) => {\n currentPath += `/${segment}`;\n const label =\n routeLabels[segment] ||\n segment.charAt(0).toUpperCase() + segment.slice(1);\n breadcrumbs.push({\n label,\n href: currentPath,\n isLast: index === segments.length - 1,\n });\n });\n\n return breadcrumbs;\n };\n\n const breadcrumbs = getBreadcrumbs();\n\n return (\n <header className=\"flex h-16 shrink-0 items-center gap-2 border-b border-red-500/20 bg-red-500/5\">\n <div className=\"flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6\">\n {onMenuClick && (\n <button\n onClick={onMenuClick}\n className=\"inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-muted hover:text-foreground md:hidden\"\n >\n <Menu className=\"h-5 w-5\" />\n <span className=\"sr-only\">Toggle menu</span>\n </button>\n )}\n <div className=\"flex items-center gap-2 rounded-md bg-red-500/10 px-2 py-1 text-xs font-medium text-red-600 dark:text-red-400 mr-2\">\n <Shield className=\"h-3 w-3\" />\n ADMIN\n </div>\n <nav className=\"flex items-center gap-1 text-sm\">\n {breadcrumbs.map((crumb, index) => (\n <React.Fragment key={crumb.href}>\n {index > 0 && (\n <ChevronRight className=\"h-4 w-4 text-muted-foreground\" />\n )}\n {crumb.isLast ? (\n <span className=\"font-medium text-foreground\">\n {crumb.label}\n </span>\n ) : (\n <Link\n href={crumb.href}\n className=\"text-muted-foreground hover:text-foreground\"\n >\n {crumb.label}\n </Link>\n )}\n </React.Fragment>\n ))}\n </nav>\n <div className=\"flex-1\" />\n {children}\n </div>\n </header>\n );\n}\n","\"use client\";\n\nimport { type ReactNode } from \"react\";\nimport { AdminSidebar, type AdminNavItem } from \"./admin-sidebar\";\nimport { AdminHeader } from \"./admin-header\";\n\nexport interface AdminLayoutProps {\n children: ReactNode;\n user: {\n name: string;\n email: string;\n avatar?: string;\n role: string;\n };\n navItems?: AdminNavItem[];\n dashboardUrl?: string;\n headerContent?: ReactNode;\n routeLabels?: Record<string, string>;\n}\n\nexport function AdminLayout({\n children,\n user,\n navItems,\n dashboardUrl,\n headerContent,\n routeLabels,\n}: AdminLayoutProps) {\n return (\n <div className=\"min-h-screen bg-background\">\n <AdminSidebar\n user={user}\n navItems={navItems}\n dashboardUrl={dashboardUrl}\n />\n <div className=\"lg:pl-64\">\n <AdminHeader routeLabels={routeLabels}>{headerContent}</AdminHeader>\n <main className=\"p-4 sm:p-6 lg:p-8\">{children}</main>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport type { LucideIcon } from \"lucide-react\";\n\nexport interface StatsCardProps {\n title: string;\n value: string | number;\n description?: string;\n icon?: LucideIcon;\n trend?: {\n value: number;\n isPositive: boolean;\n };\n className?: string;\n}\n\nexport function StatsCard({\n title,\n value,\n description,\n icon: Icon,\n trend,\n className,\n}: StatsCardProps) {\n return (\n <div className={`rounded-lg border bg-card p-6 ${className}`}>\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n {Icon && (\n <div className=\"rounded-md bg-primary/10 p-2\">\n <Icon className=\"h-4 w-4 text-primary\" />\n </div>\n )}\n </div>\n <div className=\"mt-2\">\n <p className=\"text-2xl font-bold\">{value}</p>\n {(description || trend) && (\n <div className=\"mt-1 flex items-center gap-2 text-sm\">\n {trend && (\n <span\n className={\n trend.isPositive\n ? \"text-green-600\"\n : \"text-red-600\"\n }\n >\n {trend.isPositive ? \"+\" : \"-\"}{Math.abs(trend.value)}%\n </span>\n )}\n {description && (\n <span className=\"text-muted-foreground\">{description}</span>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["React","Link","usePathname","Shield","jsx","jsxs","breadcrumbs","jsx","jsxs","jsx","jsxs"]}
|
package/dist/index.d.mts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var src_exports = {};
|
|
33
|
+
__export(src_exports, {
|
|
34
|
+
AdminHeader: () => AdminHeader,
|
|
35
|
+
AdminLayout: () => AdminLayout,
|
|
36
|
+
AdminSidebar: () => AdminSidebar,
|
|
37
|
+
StatsCard: () => StatsCard
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(src_exports);
|
|
40
|
+
|
|
41
|
+
// src/components/admin-sidebar.tsx
|
|
42
|
+
var React = __toESM(require("react"));
|
|
43
|
+
var import_link = __toESM(require("next/link"));
|
|
44
|
+
var import_navigation = require("next/navigation");
|
|
45
|
+
var import_lucide_react = require("lucide-react");
|
|
46
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
47
|
+
var defaultNavItems = [
|
|
48
|
+
{
|
|
49
|
+
title: "Overview",
|
|
50
|
+
url: "/admin",
|
|
51
|
+
icon: import_lucide_react.LayoutDashboard
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
title: "Users",
|
|
55
|
+
url: "/admin/users",
|
|
56
|
+
icon: import_lucide_react.Users
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: "Analytics",
|
|
60
|
+
url: "/admin/analytics",
|
|
61
|
+
icon: import_lucide_react.BarChart3
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
title: "Subscriptions",
|
|
65
|
+
url: "/admin/subscriptions",
|
|
66
|
+
icon: import_lucide_react.CreditCard
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
title: "Settings",
|
|
70
|
+
url: "/admin/settings",
|
|
71
|
+
icon: import_lucide_react.Settings
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
function AdminSidebar({
|
|
75
|
+
user,
|
|
76
|
+
navItems = defaultNavItems,
|
|
77
|
+
dashboardUrl = "/dashboard",
|
|
78
|
+
className
|
|
79
|
+
}) {
|
|
80
|
+
const pathname = (0, import_navigation.usePathname)();
|
|
81
|
+
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
|
82
|
+
const isActive = (url) => {
|
|
83
|
+
if (url === "/admin") {
|
|
84
|
+
return pathname === url;
|
|
85
|
+
}
|
|
86
|
+
return pathname === url || pathname.startsWith(url);
|
|
87
|
+
};
|
|
88
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
89
|
+
"aside",
|
|
90
|
+
{
|
|
91
|
+
className: `hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:flex-col transition-all duration-300 ${isCollapsed ? "lg:w-16" : "lg:w-64"} ${className}`,
|
|
92
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex grow flex-col gap-y-5 overflow-y-auto border-r border-red-500/20 bg-red-500/5 pb-4", children: [
|
|
93
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `flex h-16 shrink-0 items-center border-b border-red-500/20 px-4 ${isCollapsed ? "justify-center px-2" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_link.default, { href: "/admin", className: "flex items-center gap-2", children: [
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-red-500 text-white", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Shield, { className: "h-5 w-5" }) }),
|
|
95
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-lg font-semibold", children: "Admin Panel" })
|
|
96
|
+
] }) }),
|
|
97
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { className: "flex flex-1 flex-col px-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ul", { className: "flex flex-1 flex-col gap-y-1", children: [
|
|
98
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: "Management" }),
|
|
99
|
+
navItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
100
|
+
import_link.default,
|
|
101
|
+
{
|
|
102
|
+
href: item.url,
|
|
103
|
+
className: `group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${isActive(item.url) ? "bg-red-500 text-white" : "text-muted-foreground hover:bg-red-500/10 hover:text-red-600"} ${isCollapsed ? "justify-center px-2" : ""}`,
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(item.icon, { className: "h-5 w-5 shrink-0" }),
|
|
106
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.title })
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
) }, item.title)),
|
|
110
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "mt-auto", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
111
|
+
import_link.default,
|
|
112
|
+
{
|
|
113
|
+
href: dashboardUrl,
|
|
114
|
+
className: `group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${isCollapsed ? "justify-center px-2" : ""}`,
|
|
115
|
+
children: [
|
|
116
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowLeft, { className: "h-5 w-5 shrink-0" }),
|
|
117
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Back to Dashboard" })
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
) })
|
|
121
|
+
] }) }),
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "border-t border-red-500/20 px-2 pt-4", children: [
|
|
123
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `flex items-center gap-3 rounded-lg px-3 py-2 ${isCollapsed ? "justify-center px-2" : ""}`, children: [
|
|
124
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-red-500/10 text-red-600", children: user.name?.[0]?.toUpperCase() || "A" }),
|
|
125
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 overflow-hidden", children: [
|
|
126
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "truncate text-sm font-medium", children: user.name }),
|
|
127
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: user.role.replace("_", " ") })
|
|
128
|
+
] })
|
|
129
|
+
] }),
|
|
130
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
131
|
+
"button",
|
|
132
|
+
{
|
|
133
|
+
onClick: () => setIsCollapsed(!isCollapsed),
|
|
134
|
+
className: `mt-2 flex w-full items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${isCollapsed ? "justify-center px-2" : ""}`,
|
|
135
|
+
children: isCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronsRight, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
136
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronsLeft, { className: "h-4 w-4" }),
|
|
137
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Collapse" })
|
|
138
|
+
] })
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
] })
|
|
142
|
+
] })
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/components/admin-header.tsx
|
|
148
|
+
var React2 = __toESM(require("react"));
|
|
149
|
+
var import_link2 = __toESM(require("next/link"));
|
|
150
|
+
var import_navigation2 = require("next/navigation");
|
|
151
|
+
var import_lucide_react2 = require("lucide-react");
|
|
152
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
153
|
+
var defaultRouteLabels = {
|
|
154
|
+
admin: "Admin",
|
|
155
|
+
users: "Users",
|
|
156
|
+
analytics: "Analytics",
|
|
157
|
+
subscriptions: "Subscriptions",
|
|
158
|
+
settings: "Settings"
|
|
159
|
+
};
|
|
160
|
+
function AdminHeader({
|
|
161
|
+
onMenuClick,
|
|
162
|
+
routeLabels = defaultRouteLabels,
|
|
163
|
+
children
|
|
164
|
+
}) {
|
|
165
|
+
const pathname = (0, import_navigation2.usePathname)();
|
|
166
|
+
const getBreadcrumbs = () => {
|
|
167
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
168
|
+
const breadcrumbs2 = [];
|
|
169
|
+
let currentPath = "";
|
|
170
|
+
segments.forEach((segment, index) => {
|
|
171
|
+
currentPath += `/${segment}`;
|
|
172
|
+
const label = routeLabels[segment] || segment.charAt(0).toUpperCase() + segment.slice(1);
|
|
173
|
+
breadcrumbs2.push({
|
|
174
|
+
label,
|
|
175
|
+
href: currentPath,
|
|
176
|
+
isLast: index === segments.length - 1
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
return breadcrumbs2;
|
|
180
|
+
};
|
|
181
|
+
const breadcrumbs = getBreadcrumbs();
|
|
182
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("header", { className: "flex h-16 shrink-0 items-center gap-2 border-b border-red-500/20 bg-red-500/5", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6", children: [
|
|
183
|
+
onMenuClick && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
184
|
+
"button",
|
|
185
|
+
{
|
|
186
|
+
onClick: onMenuClick,
|
|
187
|
+
className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-muted hover:text-foreground md:hidden",
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Menu, { className: "h-5 w-5" }),
|
|
190
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "sr-only", children: "Toggle menu" })
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 rounded-md bg-red-500/10 px-2 py-1 text-xs font-medium text-red-600 dark:text-red-400 mr-2", children: [
|
|
195
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Shield, { className: "h-3 w-3" }),
|
|
196
|
+
"ADMIN"
|
|
197
|
+
] }),
|
|
198
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("nav", { className: "flex items-center gap-1 text-sm", children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(React2.Fragment, { children: [
|
|
199
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ChevronRight, { className: "h-4 w-4 text-muted-foreground" }),
|
|
200
|
+
crumb.isLast ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium text-foreground", children: crumb.label }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
201
|
+
import_link2.default,
|
|
202
|
+
{
|
|
203
|
+
href: crumb.href,
|
|
204
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
205
|
+
children: crumb.label
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] }, crumb.href)) }),
|
|
209
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-1" }),
|
|
210
|
+
children
|
|
211
|
+
] }) });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/components/admin-layout.tsx
|
|
215
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
216
|
+
function AdminLayout({
|
|
217
|
+
children,
|
|
218
|
+
user,
|
|
219
|
+
navItems,
|
|
220
|
+
dashboardUrl,
|
|
221
|
+
headerContent,
|
|
222
|
+
routeLabels
|
|
223
|
+
}) {
|
|
224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "min-h-screen bg-background", children: [
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
226
|
+
AdminSidebar,
|
|
227
|
+
{
|
|
228
|
+
user,
|
|
229
|
+
navItems,
|
|
230
|
+
dashboardUrl
|
|
231
|
+
}
|
|
232
|
+
),
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lg:pl-64", children: [
|
|
234
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AdminHeader, { routeLabels, children: headerContent }),
|
|
235
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("main", { className: "p-4 sm:p-6 lg:p-8", children })
|
|
236
|
+
] })
|
|
237
|
+
] });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/components/stats-card.tsx
|
|
241
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
242
|
+
function StatsCard({
|
|
243
|
+
title,
|
|
244
|
+
value,
|
|
245
|
+
description,
|
|
246
|
+
icon: Icon,
|
|
247
|
+
trend,
|
|
248
|
+
className
|
|
249
|
+
}) {
|
|
250
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `rounded-lg border bg-card p-6 ${className}`, children: [
|
|
251
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
252
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm font-medium text-muted-foreground", children: title }),
|
|
253
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rounded-md bg-primary/10 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: "h-4 w-4 text-primary" }) })
|
|
254
|
+
] }),
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mt-2", children: [
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-2xl font-bold", children: value }),
|
|
257
|
+
(description || trend) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mt-1 flex items-center gap-2 text-sm", children: [
|
|
258
|
+
trend && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
259
|
+
"span",
|
|
260
|
+
{
|
|
261
|
+
className: trend.isPositive ? "text-green-600" : "text-red-600",
|
|
262
|
+
children: [
|
|
263
|
+
trend.isPositive ? "+" : "-",
|
|
264
|
+
Math.abs(trend.value),
|
|
265
|
+
"%"
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
),
|
|
269
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-muted-foreground", children: description })
|
|
270
|
+
] })
|
|
271
|
+
] })
|
|
272
|
+
] });
|
|
273
|
+
}
|
|
274
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
275
|
+
0 && (module.exports = {
|
|
276
|
+
AdminHeader,
|
|
277
|
+
AdminLayout,
|
|
278
|
+
AdminSidebar,
|
|
279
|
+
StatsCard
|
|
280
|
+
});
|
|
281
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/admin-sidebar.tsx","../src/components/admin-header.tsx","../src/components/admin-layout.tsx","../src/components/stats-card.tsx"],"sourcesContent":["export {\n AdminSidebar,\n AdminHeader,\n AdminLayout,\n StatsCard,\n} from \"./components\";\n\nexport type {\n AdminSidebarProps,\n AdminNavItem,\n AdminHeaderProps,\n AdminLayoutProps,\n StatsCardProps,\n} from \"./components\";\n","\"use client\";\n\nimport * as React from \"react\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\nimport {\n LayoutDashboard,\n Users,\n BarChart3,\n CreditCard,\n Settings,\n ArrowLeft,\n ChevronsLeft,\n ChevronsRight,\n Shield,\n type LucideIcon,\n} from \"lucide-react\";\n\nexport interface AdminNavItem {\n title: string;\n url: string;\n icon: LucideIcon;\n}\n\nexport interface AdminSidebarProps {\n user: {\n name: string;\n email: string;\n avatar?: string;\n role: string;\n };\n navItems?: AdminNavItem[];\n dashboardUrl?: string;\n className?: string;\n}\n\nconst defaultNavItems: AdminNavItem[] = [\n {\n title: \"Overview\",\n url: \"/admin\",\n icon: LayoutDashboard,\n },\n {\n title: \"Users\",\n url: \"/admin/users\",\n icon: Users,\n },\n {\n title: \"Analytics\",\n url: \"/admin/analytics\",\n icon: BarChart3,\n },\n {\n title: \"Subscriptions\",\n url: \"/admin/subscriptions\",\n icon: CreditCard,\n },\n {\n title: \"Settings\",\n url: \"/admin/settings\",\n icon: Settings,\n },\n];\n\nexport function AdminSidebar({\n user,\n navItems = defaultNavItems,\n dashboardUrl = \"/dashboard\",\n className,\n}: AdminSidebarProps) {\n const pathname = usePathname();\n const [isCollapsed, setIsCollapsed] = React.useState(false);\n\n const isActive = (url: string) => {\n if (url === \"/admin\") {\n return pathname === url;\n }\n return pathname === url || pathname.startsWith(url);\n };\n\n return (\n <aside\n className={`hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:flex-col transition-all duration-300 ${\n isCollapsed ? \"lg:w-16\" : \"lg:w-64\"\n } ${className}`}\n >\n <div className=\"flex grow flex-col gap-y-5 overflow-y-auto border-r border-red-500/20 bg-red-500/5 pb-4\">\n {/* Header */}\n <div className={`flex h-16 shrink-0 items-center border-b border-red-500/20 px-4 ${isCollapsed ? \"justify-center px-2\" : \"\"}`}>\n <Link href=\"/admin\" className=\"flex items-center gap-2\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-red-500 text-white\">\n <Shield className=\"h-5 w-5\" />\n </div>\n {!isCollapsed && (\n <span className=\"text-lg font-semibold\">Admin Panel</span>\n )}\n </Link>\n </div>\n\n {/* Navigation */}\n <nav className=\"flex flex-1 flex-col px-2\">\n <ul className=\"flex flex-1 flex-col gap-y-1\">\n {!isCollapsed && (\n <li className=\"mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n Management\n </li>\n )}\n {navItems.map((item) => (\n <li key={item.title}>\n <Link\n href={item.url}\n className={`group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${\n isActive(item.url)\n ? \"bg-red-500 text-white\"\n : \"text-muted-foreground hover:bg-red-500/10 hover:text-red-600\"\n } ${isCollapsed ? \"justify-center px-2\" : \"\"}`}\n >\n <item.icon className=\"h-5 w-5 shrink-0\" />\n {!isCollapsed && <span>{item.title}</span>}\n </Link>\n </li>\n ))}\n\n <li className=\"mt-auto\">\n <Link\n href={dashboardUrl}\n className={`group flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${\n isCollapsed ? \"justify-center px-2\" : \"\"\n }`}\n >\n <ArrowLeft className=\"h-5 w-5 shrink-0\" />\n {!isCollapsed && <span>Back to Dashboard</span>}\n </Link>\n </li>\n </ul>\n </nav>\n\n {/* Footer */}\n <div className=\"border-t border-red-500/20 px-2 pt-4\">\n <div className={`flex items-center gap-3 rounded-lg px-3 py-2 ${isCollapsed ? \"justify-center px-2\" : \"\"}`}>\n <div className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-red-500/10 text-red-600\">\n {user.name?.[0]?.toUpperCase() || \"A\"}\n </div>\n {!isCollapsed && (\n <div className=\"flex-1 overflow-hidden\">\n <p className=\"truncate text-sm font-medium\">{user.name}</p>\n <p className=\"truncate text-xs text-muted-foreground\">\n {user.role.replace(\"_\", \" \")}\n </p>\n </div>\n )}\n </div>\n <button\n onClick={() => setIsCollapsed(!isCollapsed)}\n className={`mt-2 flex w-full items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-muted-foreground hover:bg-muted hover:text-foreground ${\n isCollapsed ? \"justify-center px-2\" : \"\"\n }`}\n >\n {isCollapsed ? (\n <ChevronsRight className=\"h-4 w-4\" />\n ) : (\n <>\n <ChevronsLeft className=\"h-4 w-4\" />\n <span>Collapse</span>\n </>\n )}\n </button>\n </div>\n </div>\n </aside>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\nimport { Menu, Shield, ChevronRight } from \"lucide-react\";\n\nexport interface AdminHeaderProps {\n onMenuClick?: () => void;\n routeLabels?: Record<string, string>;\n children?: React.ReactNode;\n}\n\nconst defaultRouteLabels: Record<string, string> = {\n admin: \"Admin\",\n users: \"Users\",\n analytics: \"Analytics\",\n subscriptions: \"Subscriptions\",\n settings: \"Settings\",\n};\n\nexport function AdminHeader({\n onMenuClick,\n routeLabels = defaultRouteLabels,\n children,\n}: AdminHeaderProps) {\n const pathname = usePathname();\n\n const getBreadcrumbs = () => {\n const segments = pathname.split(\"/\").filter(Boolean);\n const breadcrumbs: { label: string; href: string; isLast: boolean }[] = [];\n\n let currentPath = \"\";\n segments.forEach((segment, index) => {\n currentPath += `/${segment}`;\n const label =\n routeLabels[segment] ||\n segment.charAt(0).toUpperCase() + segment.slice(1);\n breadcrumbs.push({\n label,\n href: currentPath,\n isLast: index === segments.length - 1,\n });\n });\n\n return breadcrumbs;\n };\n\n const breadcrumbs = getBreadcrumbs();\n\n return (\n <header className=\"flex h-16 shrink-0 items-center gap-2 border-b border-red-500/20 bg-red-500/5\">\n <div className=\"flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6\">\n {onMenuClick && (\n <button\n onClick={onMenuClick}\n className=\"inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-muted hover:text-foreground md:hidden\"\n >\n <Menu className=\"h-5 w-5\" />\n <span className=\"sr-only\">Toggle menu</span>\n </button>\n )}\n <div className=\"flex items-center gap-2 rounded-md bg-red-500/10 px-2 py-1 text-xs font-medium text-red-600 dark:text-red-400 mr-2\">\n <Shield className=\"h-3 w-3\" />\n ADMIN\n </div>\n <nav className=\"flex items-center gap-1 text-sm\">\n {breadcrumbs.map((crumb, index) => (\n <React.Fragment key={crumb.href}>\n {index > 0 && (\n <ChevronRight className=\"h-4 w-4 text-muted-foreground\" />\n )}\n {crumb.isLast ? (\n <span className=\"font-medium text-foreground\">\n {crumb.label}\n </span>\n ) : (\n <Link\n href={crumb.href}\n className=\"text-muted-foreground hover:text-foreground\"\n >\n {crumb.label}\n </Link>\n )}\n </React.Fragment>\n ))}\n </nav>\n <div className=\"flex-1\" />\n {children}\n </div>\n </header>\n );\n}\n","\"use client\";\n\nimport { type ReactNode } from \"react\";\nimport { AdminSidebar, type AdminNavItem } from \"./admin-sidebar\";\nimport { AdminHeader } from \"./admin-header\";\n\nexport interface AdminLayoutProps {\n children: ReactNode;\n user: {\n name: string;\n email: string;\n avatar?: string;\n role: string;\n };\n navItems?: AdminNavItem[];\n dashboardUrl?: string;\n headerContent?: ReactNode;\n routeLabels?: Record<string, string>;\n}\n\nexport function AdminLayout({\n children,\n user,\n navItems,\n dashboardUrl,\n headerContent,\n routeLabels,\n}: AdminLayoutProps) {\n return (\n <div className=\"min-h-screen bg-background\">\n <AdminSidebar\n user={user}\n navItems={navItems}\n dashboardUrl={dashboardUrl}\n />\n <div className=\"lg:pl-64\">\n <AdminHeader routeLabels={routeLabels}>{headerContent}</AdminHeader>\n <main className=\"p-4 sm:p-6 lg:p-8\">{children}</main>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport type { LucideIcon } from \"lucide-react\";\n\nexport interface StatsCardProps {\n title: string;\n value: string | number;\n description?: string;\n icon?: LucideIcon;\n trend?: {\n value: number;\n isPositive: boolean;\n };\n className?: string;\n}\n\nexport function StatsCard({\n title,\n value,\n description,\n icon: Icon,\n trend,\n className,\n}: StatsCardProps) {\n return (\n <div className={`rounded-lg border bg-card p-6 ${className}`}>\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n {Icon && (\n <div className=\"rounded-md bg-primary/10 p-2\">\n <Icon className=\"h-4 w-4 text-primary\" />\n </div>\n )}\n </div>\n <div className=\"mt-2\">\n <p className=\"text-2xl font-bold\">{value}</p>\n {(description || trend) && (\n <div className=\"mt-1 flex items-center gap-2 text-sm\">\n {trend && (\n <span\n className={\n trend.isPositive\n ? \"text-green-600\"\n : \"text-red-600\"\n }\n >\n {trend.isPositive ? \"+\" : \"-\"}{Math.abs(trend.value)}%\n </span>\n )}\n {description && (\n <span className=\"text-muted-foreground\">{description}</span>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,YAAuB;AACvB,kBAAiB;AACjB,wBAA4B;AAC5B,0BAWO;AAyEG;AArDV,IAAM,kBAAkC;AAAA,EACtC;AAAA,IACE,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AACF;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AACF,GAAsB;AACpB,QAAM,eAAW,+BAAY;AAC7B,QAAM,CAAC,aAAa,cAAc,IAAU,eAAS,KAAK;AAE1D,QAAM,WAAW,CAAC,QAAgB;AAChC,QAAI,QAAQ,UAAU;AACpB,aAAO,aAAa;AAAA,IACtB;AACA,WAAO,aAAa,OAAO,SAAS,WAAW,GAAG;AAAA,EACpD;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,wFACT,cAAc,YAAY,SAC5B,IAAI,SAAS;AAAA,MAEb,uDAAC,SAAI,WAAU,2FAEb;AAAA,oDAAC,SAAI,WAAW,mEAAmE,cAAc,wBAAwB,EAAE,IACzH,uDAAC,YAAAA,SAAA,EAAK,MAAK,UAAS,WAAU,2BAC5B;AAAA,sDAAC,SAAI,WAAU,6EACb,sDAAC,8BAAO,WAAU,WAAU,GAC9B;AAAA,UACC,CAAC,eACA,4CAAC,UAAK,WAAU,yBAAwB,yBAAW;AAAA,WAEvD,GACF;AAAA,QAGA,4CAAC,SAAI,WAAU,6BACb,uDAAC,QAAG,WAAU,gCACX;AAAA,WAAC,eACA,4CAAC,QAAG,WAAU,kFAAiF,wBAE/F;AAAA,UAED,SAAS,IAAI,CAAC,SACb,4CAAC,QACC;AAAA,YAAC,YAAAA;AAAA,YAAA;AAAA,cACC,MAAM,KAAK;AAAA,cACX,WAAW,8FACT,SAAS,KAAK,GAAG,IACb,0BACA,8DACN,IAAI,cAAc,wBAAwB,EAAE;AAAA,cAE5C;AAAA,4DAAC,KAAK,MAAL,EAAU,WAAU,oBAAmB;AAAA,gBACvC,CAAC,eAAe,4CAAC,UAAM,eAAK,OAAM;AAAA;AAAA;AAAA,UACrC,KAXO,KAAK,KAYd,CACD;AAAA,UAED,4CAAC,QAAG,WAAU,WACZ;AAAA,YAAC,YAAAA;AAAA,YAAA;AAAA,cACC,MAAM;AAAA,cACN,WAAW,yJACT,cAAc,wBAAwB,EACxC;AAAA,cAEA;AAAA,4DAAC,iCAAU,WAAU,oBAAmB;AAAA,gBACvC,CAAC,eAAe,4CAAC,UAAK,+BAAiB;AAAA;AAAA;AAAA,UAC1C,GACF;AAAA,WACF,GACF;AAAA,QAGA,6CAAC,SAAI,WAAU,wCACb;AAAA,uDAAC,SAAI,WAAW,gDAAgD,cAAc,wBAAwB,EAAE,IACtG;AAAA,wDAAC,SAAI,WAAU,kFACZ,eAAK,OAAO,CAAC,GAAG,YAAY,KAAK,KACpC;AAAA,YACC,CAAC,eACA,6CAAC,SAAI,WAAU,0BACb;AAAA,0DAAC,OAAE,WAAU,gCAAgC,eAAK,MAAK;AAAA,cACvD,4CAAC,OAAE,WAAU,0CACV,eAAK,KAAK,QAAQ,KAAK,GAAG,GAC7B;AAAA,eACF;AAAA,aAEJ;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,eAAe,CAAC,WAAW;AAAA,cAC1C,WAAW,+JACT,cAAc,wBAAwB,EACxC;AAAA,cAEC,wBACC,4CAAC,qCAAc,WAAU,WAAU,IAEnC,4EACE;AAAA,4DAAC,oCAAa,WAAU,WAAU;AAAA,gBAClC,4CAAC,UAAK,sBAAQ;AAAA,iBAChB;AAAA;AAAA,UAEJ;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;;;ACzKA,IAAAC,SAAuB;AACvB,IAAAC,eAAiB;AACjB,IAAAC,qBAA4B;AAC5B,IAAAC,uBAA2C;AAiDjC,IAAAC,sBAAA;AAzCV,IAAM,qBAA6C;AAAA,EACjD,OAAO;AAAA,EACP,OAAO;AAAA,EACP,WAAW;AAAA,EACX,eAAe;AAAA,EACf,UAAU;AACZ;AAEO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAAqB;AACnB,QAAM,eAAW,gCAAY;AAE7B,QAAM,iBAAiB,MAAM;AAC3B,UAAM,WAAW,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AACnD,UAAMC,eAAkE,CAAC;AAEzE,QAAI,cAAc;AAClB,aAAS,QAAQ,CAAC,SAAS,UAAU;AACnC,qBAAe,IAAI,OAAO;AAC1B,YAAM,QACJ,YAAY,OAAO,KACnB,QAAQ,OAAO,CAAC,EAAE,YAAY,IAAI,QAAQ,MAAM,CAAC;AACnD,MAAAA,aAAY,KAAK;AAAA,QACf;AAAA,QACA,MAAM;AAAA,QACN,QAAQ,UAAU,SAAS,SAAS;AAAA,MACtC,CAAC;AAAA,IACH,CAAC;AAED,WAAOA;AAAA,EACT;AAEA,QAAM,cAAc,eAAe;AAEnC,SACE,6CAAC,YAAO,WAAU,iFAChB,wDAAC,SAAI,WAAU,wDACZ;AAAA,mBACC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QAEV;AAAA,uDAAC,6BAAK,WAAU,WAAU;AAAA,UAC1B,6CAAC,UAAK,WAAU,WAAU,yBAAW;AAAA;AAAA;AAAA,IACvC;AAAA,IAEF,8CAAC,SAAI,WAAU,sHACb;AAAA,mDAAC,+BAAO,WAAU,WAAU;AAAA,MAAE;AAAA,OAEhC;AAAA,IACA,6CAAC,SAAI,WAAU,mCACZ,sBAAY,IAAI,CAAC,OAAO,UACvB,8CAAO,iBAAN,EACE;AAAA,cAAQ,KACP,6CAAC,qCAAa,WAAU,iCAAgC;AAAA,MAEzD,MAAM,SACL,6CAAC,UAAK,WAAU,+BACb,gBAAM,OACT,IAEA;AAAA,QAAC,aAAAC;AAAA,QAAA;AAAA,UACC,MAAM,MAAM;AAAA,UACZ,WAAU;AAAA,UAET,gBAAM;AAAA;AAAA,MACT;AAAA,SAdiB,MAAM,IAgB3B,CACD,GACH;AAAA,IACA,6CAAC,SAAI,WAAU,UAAS;AAAA,IACvB;AAAA,KACH,GACF;AAEJ;;;AC9DM,IAAAC,sBAAA;AAVC,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,SACE,8CAAC,SAAI,WAAU,8BACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IACA,8CAAC,SAAI,WAAU,YACb;AAAA,mDAAC,eAAY,aAA2B,yBAAc;AAAA,MACtD,6CAAC,UAAK,WAAU,qBAAqB,UAAS;AAAA,OAChD;AAAA,KACF;AAEJ;;;ACfM,IAAAC,sBAAA;AAVC,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AACF,GAAmB;AACjB,SACE,8CAAC,SAAI,WAAW,iCAAiC,SAAS,IACxD;AAAA,kDAAC,SAAI,WAAU,qCACb;AAAA,mDAAC,OAAE,WAAU,6CAA6C,iBAAM;AAAA,MAC/D,QACC,6CAAC,SAAI,WAAU,gCACb,uDAAC,QAAK,WAAU,wBAAuB,GACzC;AAAA,OAEJ;AAAA,IACA,8CAAC,SAAI,WAAU,QACb;AAAA,mDAAC,OAAE,WAAU,sBAAsB,iBAAM;AAAA,OACvC,eAAe,UACf,8CAAC,SAAI,WAAU,wCACZ;AAAA,iBACC;AAAA,UAAC;AAAA;AAAA,YACC,WACE,MAAM,aACF,mBACA;AAAA,YAGL;AAAA,oBAAM,aAAa,MAAM;AAAA,cAAK,KAAK,IAAI,MAAM,KAAK;AAAA,cAAE;AAAA;AAAA;AAAA,QACvD;AAAA,QAED,eACC,6CAAC,UAAK,WAAU,yBAAyB,uBAAY;AAAA,SAEzD;AAAA,OAEJ;AAAA,KACF;AAEJ;","names":["Link","React","import_link","import_navigation","import_lucide_react","import_jsx_runtime","breadcrumbs","Link","import_jsx_runtime","import_jsx_runtime"]}
|