Preview
Full width desktop view
Code
bills-12.tsx
1"use client";
2
3import React, { useState, useEffect } from "react";
4import { motion, AnimatePresence } from "framer-motion";
5import {
6 Home,
7 User,
8 Settings,
9 LogOut,
10 Menu,
11 X,
12 ChevronLeft,
13 ChevronRight,
14 BarChart3,
15 FileText,
16 Bell,
17 Search,
18 HelpCircle,
19 CreditCard,
20 DollarSign,
21 Calendar,
22 Check,
23 Plus,
24 Minus,
25 AlertCircle,
26 Building,
27 Users,
28 Zap,
29 Crown,
30 Rocket,
31 Star,
32 Shield,
33} from "lucide-react";
34
35interface NavigationItem {
36 id: string;
37 name: string;
38 icon: React.ComponentType<{ className?: string }>;
39 href: string;
40 badge?: string;
41}
42
43interface Plan {
44 id: string;
45 name: string;
46 description: string;
47 price: {
48 monthly: number;
49 yearly: number;
50 };
51 features: string[];
52 icon: React.ComponentType<{ className?: string }>;
53 popular?: boolean;
54 current?: boolean;
55}
56
57interface AddOn {
58 id: string;
59 name: string;
60 description: string;
61 price: {
62 monthly: number;
63 yearly: number;
64 };
65 enabled: boolean;
66}
67
68interface Bill {
69 id: string;
70 description: string;
71 amount: number;
72 dueDate: string;
73 status: "paid" | "pending" | "overdue";
74 type: "subscription" | "addon" | "usage";
75}
76
77const navigationItems: NavigationItem[] = [
78 { id: "dashboard", name: "Dashboard", icon: Home, href: "/dashboard" },
79 {
80 id: "billing",
81 name: "Billing & Plans",
82 icon: CreditCard,
83 href: "/billing",
84 badge: "2",
85 },
86 { id: "analytics", name: "Analytics", icon: BarChart3, href: "/analytics" },
87 { id: "users", name: "Team Members", icon: Users, href: "/users" },
88 { id: "settings", name: "Settings", icon: Settings, href: "/settings" },
89 { id: "help", name: "Help & Support", icon: HelpCircle, href: "/help" },
90];
91
92const plans: Plan[] = [
93 {
94 id: "starter",
95 name: "Starter",
96 description: "Perfect for small teams getting started",
97 price: { monthly: 29, yearly: 290 },
98 features: [
99 "Up to 5 team members",
100 "10GB storage",
101 "Basic analytics",
102 "Email support",
103 "Standard integrations",
104 ],
105 icon: Zap,
106 current: false,
107 },
108 {
109 id: "professional",
110 name: "Professional",
111 description: "Advanced features for growing businesses",
112 price: { monthly: 99, yearly: 990 },
113 features: [
114 "Up to 25 team members",
115 "100GB storage",
116 "Advanced analytics",
117 "Priority support",
118 "Premium integrations",
119 "Custom workflows",
120 "API access",
121 ],
122 icon: Crown,
123 popular: true,
124 current: true,
125 },
126 {
127 id: "enterprise",
128 name: "Enterprise",
129 description: "Complete solution for large organizations",
130 price: { monthly: 299, yearly: 2990 },
131 features: [
132 "Unlimited team members",
133 "1TB storage",
134 "Enterprise analytics",
135 "24/7 dedicated support",
136 "Custom integrations",
137 "Advanced security",
138 "SLA guarantee",
139 "White-label options",
140 ],
141 icon: Rocket,
142 current: false,
143 },
144];
145
146const addOns: AddOn[] = [
147 {
148 id: "extra-storage",
149 name: "Extra Storage",
150 description: "Additional 100GB storage space",
151 price: { monthly: 15, yearly: 150 },
152 enabled: true,
153 },
154 {
155 id: "advanced-security",
156 name: "Advanced Security",
157 description: "Enhanced security features and compliance",
158 price: { monthly: 25, yearly: 250 },
159 enabled: false,
160 },
161 {
162 id: "priority-support",
163 name: "Priority Support",
164 description: "24/7 priority customer support",
165 price: { monthly: 20, yearly: 200 },
166 enabled: true,
167 },
168];
169
170const bills: Bill[] = [
171 {
172 id: "1",
173 description: "Professional Plan - Monthly",
174 amount: 99,
175 dueDate: "2024-02-15",
176 status: "pending",
177 type: "subscription",
178 },
179 {
180 id: "2",
181 description: "Extra Storage Add-on",
182 amount: 15,
183 dueDate: "2024-02-15",
184 status: "pending",
185 type: "addon",
186 },
187 {
188 id: "3",
189 description: "Priority Support Add-on",
190 amount: 20,
191 dueDate: "2024-02-15",
192 status: "pending",
193 type: "addon",
194 },
195 {
196 id: "4",
197 description: "Professional Plan - January",
198 amount: 99,
199 dueDate: "2024-01-15",
200 status: "paid",
201 type: "subscription",
202 },
203];
204
205function Sidebar({
206 className = "",
207 activeItem,
208 setActiveItem,
209}: {
210 className?: string;
211 activeItem: string;
212 setActiveItem: (item: string) => void;
213}) {
214 const [isOpen, setIsOpen] = useState(false);
215 const [isCollapsed, setIsCollapsed] = useState(false);
216
217 useEffect(() => {
218 const handleResize = () => {
219 if (window.innerWidth >= 768) {
220 setIsOpen(true);
221 } else {
222 setIsOpen(false);
223 }
224 };
225
226 handleResize();
227 window.addEventListener("resize", handleResize);
228 return () => window.removeEventListener("resize", handleResize);
229 }, []);
230
231 const toggleSidebar = () => setIsOpen(!isOpen);
232 const toggleCollapse = () => setIsCollapsed(!isCollapsed);
233
234 const handleItemClick = (itemId: string) => {
235 setActiveItem(itemId);
236 if (window.innerWidth < 768) {
237 setIsOpen(false);
238 }
239 };
240
241 return (
242 <>
243 <button
244 onClick={toggleSidebar}
245 className="fixed top-6 left-6 z-50 p-3 rounded-lg bg-background shadow-md border border-border md:hidden hover:bg-accent transition-all duration-200"
246 aria-label="Toggle sidebar"
247 >
248 {isOpen ? (
249 <X className="h-5 w-5 text-foreground" />
250 ) : (
251 <Menu className="h-5 w-5 text-foreground" />
252 )}
253 </button>
254
255 {isOpen && (
256 <div
257 className="fixed inset-0 bg-black/40 backdrop-blur-sm z-30 md:hidden transition-opacity duration-300"
258 onClick={toggleSidebar}
259 />
260 )}
261
262 <div
263 className={`
264 fixed top-0 left-0 h-full bg-background border-r border-border z-40 transition-all duration-300 ease-in-out flex flex-col
265 ${isOpen ? "translate-x-0" : "-translate-x-full"}
266 ${isCollapsed ? "w-20" : "w-72"}
267 md:translate-x-0 md:static md:z-auto
268 ${className}
269 `}
270 >
271 <div className="flex items-center justify-between p-5 border-b border-border bg-muted/30">
272 {!isCollapsed && (
273 <div className="flex items-center space-x-2.5">
274 <div className="w-9 h-9 bg-primary rounded-lg flex items-center justify-center shadow-sm">
275 <Building className="text-primary-foreground h-5 w-5" />
276 </div>
277 <div className="flex flex-col">
278 <span className="font-semibold text-foreground text-base">
279 B2B Admin
280 </span>
281 <span className="text-xs text-muted-foreground">
282 Customer Portal
283 </span>
284 </div>
285 </div>
286 )}
287
288 {isCollapsed && (
289 <div className="w-9 h-9 bg-primary rounded-lg flex items-center justify-center mx-auto shadow-sm">
290 <Building className="text-primary-foreground h-5 w-5" />
291 </div>
292 )}
293
294 <button
295 onClick={toggleCollapse}
296 className="hidden md:flex p-1.5 rounded-md hover:bg-accent transition-all duration-200"
297 aria-label={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
298 >
299 {isCollapsed ? (
300 <ChevronRight className="h-4 w-4 text-muted-foreground" />
301 ) : (
302 <ChevronLeft className="h-4 w-4 text-muted-foreground" />
303 )}
304 </button>
305 </div>
306
307 {!isCollapsed && (
308 <div className="px-4 py-3">
309 <div className="relative">
310 <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
311 <input
312 type="text"
313 placeholder="Search..."
314 className="w-full pl-9 pr-4 py-2 bg-muted border border-border rounded-md text-sm placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent transition-all duration-200"
315 />
316 </div>
317 </div>
318 )}
319
320 <nav className="flex-1 px-3 py-2 overflow-y-auto">
321 <ul className="space-y-0.5">
322 {navigationItems.map((item) => {
323 const Icon = item.icon;
324 const isActive = activeItem === item.id;
325
326 return (
327 <li key={item.id}>
328 <button
329 onClick={() => handleItemClick(item.id)}
330 className={`
331 w-full flex items-center space-x-2.5 px-3 py-2.5 rounded-md text-left transition-all duration-200 group relative
332 ${
333 isActive
334 ? "bg-primary/10 text-primary"
335 : "text-muted-foreground hover:bg-accent hover:text-foreground"
336 }
337 ${isCollapsed ? "justify-center px-2" : ""}
338 `}
339 title={isCollapsed ? item.name : undefined}
340 >
341 <div className="flex items-center justify-center min-w-[24px]">
342 <Icon
343 className={`
344 h-4.5 w-4.5 flex-shrink-0
345 ${
346 isActive
347 ? "text-primary"
348 : "text-muted-foreground group-hover:text-foreground"
349 }
350 `}
351 />
352 </div>
353
354 {!isCollapsed && (
355 <div className="flex items-center justify-between w-full">
356 <span
357 className={`text-sm ${
358 isActive ? "font-medium" : "font-normal"
359 }`}
360 >
361 {item.name}
362 </span>
363 {item.badge && (
364 <span
365 className={`
366 px-1.5 py-0.5 text-xs font-medium rounded-full
367 ${
368 isActive
369 ? "bg-primary/20 text-primary"
370 : "bg-muted text-muted-foreground"
371 }
372 `}
373 >
374 {item.badge}
375 </span>
376 )}
377 </div>
378 )}
379
380 {isCollapsed && item.badge && (
381 <div className="absolute top-1 right-1 w-4 h-4 flex items-center justify-center rounded-full bg-primary/20 border border-background">
382 <span className="text-[10px] font-medium text-primary">
383 {parseInt(item.badge) > 9 ? "9+" : item.badge}
384 </span>
385 </div>
386 )}
387 </button>
388 </li>
389 );
390 })}
391 </ul>
392 </nav>
393
394 <div className="mt-auto border-t border-border">
395 <div
396 className={`border-b border-border bg-muted/30 ${
397 isCollapsed ? "py-3 px-2" : "p-3"
398 }`}
399 >
400 {!isCollapsed ? (
401 <div className="flex items-center px-3 py-2 rounded-md bg-background hover:bg-accent transition-colors duration-200">
402 <div className="w-8 h-8 bg-muted rounded-full flex items-center justify-center">
403 <span className="text-foreground font-medium text-sm">
404 JD
405 </span>
406 </div>
407 <div className="flex-1 min-w-0 ml-2.5">
408 <p className="text-sm font-medium text-foreground truncate">
409 John Doe
410 </p>
411 <p className="text-xs text-muted-foreground truncate">
412 Admin User
413 </p>
414 </div>
415 <div
416 className="w-2 h-2 bg-green-500 rounded-full ml-2"
417 title="Online"
418 />
419 </div>
420 ) : (
421 <div className="flex justify-center">
422 <div className="relative">
423 <div className="w-9 h-9 bg-muted rounded-full flex items-center justify-center">
424 <span className="text-foreground font-medium text-sm">
425 JD
426 </span>
427 </div>
428 <div className="absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-background" />
429 </div>
430 </div>
431 )}
432 </div>
433
434 <div className="p-3">
435 <button
436 className={`
437 w-full flex items-center rounded-md text-left transition-all duration-200 group
438 text-destructive hover:bg-destructive/10 hover:text-destructive
439 ${
440 isCollapsed
441 ? "justify-center p-2.5"
442 : "space-x-2.5 px-3 py-2.5"
443 }
444 `}
445 title={isCollapsed ? "Logout" : undefined}
446 >
447 <div className="flex items-center justify-center min-w-[24px]">
448 <LogOut className="h-4.5 w-4.5 flex-shrink-0 text-destructive" />
449 </div>
450
451 {!isCollapsed && <span className="text-sm">Logout</span>}
452 </button>
453 </div>
454 </div>
455 </div>
456 </>
457 );
458}
459
460function BillingDashboard() {
461 const [isYearly, setIsYearly] = useState(false);
462 const [selectedPlan, setSelectedPlan] = useState("professional");
463 const [enabledAddOns, setEnabledAddOns] = useState<string[]>([
464 "extra-storage",
465 "priority-support",
466 ]);
467
468 const currentPlan = plans.find((plan) => plan.current);
469 const pendingBills = bills.filter((bill) => bill.status === "pending");
470 const totalPending = pendingBills.reduce((sum, bill) => sum + bill.amount, 0);
471
472 const toggleAddOn = (addOnId: string) => {
473 setEnabledAddOns((prev) =>
474 prev.includes(addOnId)
475 ? prev.filter((id) => id !== addOnId)
476 : [...prev, addOnId]
477 );
478 };
479
480 const calculateTotal = () => {
481 const plan = plans.find((p) => p.id === selectedPlan);
482 const planPrice = plan
483 ? isYearly
484 ? plan.price.yearly
485 : plan.price.monthly
486 : 0;
487 const addOnPrice = addOns
488 .filter((addon) => enabledAddOns.includes(addon.id))
489 .reduce(
490 (sum, addon) =>
491 sum + (isYearly ? addon.price.yearly : addon.price.monthly),
492 0
493 );
494 return planPrice + addOnPrice;
495 };
496
497 return (
498 <div className="p-6 space-y-8">
499 {/* Header */}
500 <div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
501 <div>
502 <h1 className="text-3xl font-bold text-foreground">
503 Billing & Plans
504 </h1>
505 <p className="text-muted-foreground mt-1">
506 Manage your subscription and billing information
507 </p>
508 </div>
509
510 <div className="flex items-center gap-4">
511 <div className="flex items-center gap-2 text-sm">
512 <span
513 className={
514 !isYearly
515 ? "text-foreground font-medium"
516 : "text-muted-foreground"
517 }
518 >
519 Monthly
520 </span>
521 <button
522 onClick={() => setIsYearly(!isYearly)}
523 className={`relative w-12 h-6 rounded-full transition-colors ${
524 isYearly ? "bg-primary" : "bg-muted"
525 }`}
526 >
527 <div
528 className={`absolute top-0.5 w-5 h-5 bg-background rounded-full shadow-sm transition-transform ${
529 isYearly ? "translate-x-6" : "translate-x-0.5"
530 }`}
531 />
532 </button>
533 <span
534 className={
535 isYearly
536 ? "text-foreground font-medium"
537 : "text-muted-foreground"
538 }
539 >
540 Yearly
541 </span>
542 {isYearly && (
543 <span className="px-2 py-1 bg-green-100 text-green-700 text-xs rounded-full font-medium">
544 Save 20%
545 </span>
546 )}
547 </div>
548 </div>
549 </div>
550
551 {/* Current Plan & Bills Overview */}
552 <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
553 {/* Current Plan */}
554 <div className="lg:col-span-2">
555 <div className="bg-background border border-border rounded-lg p-6">
556 <div className="flex items-center justify-between mb-4">
557 <h2 className="text-xl font-semibold text-foreground">
558 Current Plan
559 </h2>
560 {currentPlan?.popular && (
561 <span className="px-3 py-1 bg-primary/10 text-primary text-sm rounded-full font-medium">
562 Most Popular
563 </span>
564 )}
565 </div>
566
567 {currentPlan && (
568 <div className="space-y-4">
569 <div className="flex items-center gap-3">
570 <div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center">
571 <currentPlan.icon className="w-6 h-6 text-primary" />
572 </div>
573 <div>
574 <h3 className="text-lg font-semibold text-foreground">
575 {currentPlan.name}
576 </h3>
577 <p className="text-muted-foreground">
578 {currentPlan.description}
579 </p>
580 </div>
581 </div>
582
583 <div className="flex items-baseline gap-2">
584 <span className="text-3xl font-bold text-foreground">
585 $
586 {isYearly
587 ? currentPlan.price.yearly
588 : currentPlan.price.monthly}
589 </span>
590 <span className="text-muted-foreground">
591 /{isYearly ? "year" : "month"}
592 </span>
593 </div>
594
595 <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
596 {currentPlan.features.map((feature, index) => (
597 <div key={index} className="flex items-center gap-2">
598 <Check className="w-4 h-4 text-green-500 flex-shrink-0" />
599 <span className="text-sm text-muted-foreground">
600 {feature}
601 </span>
602 </div>
603 ))}
604 </div>
605 </div>
606 )}
607 </div>
608 </div>
609
610 {/* Pending Bills */}
611 <div className="bg-background border border-border rounded-lg p-6">
612 <h2 className="text-xl font-semibold text-foreground mb-4">
613 Pending Bills
614 </h2>
615
616 <div className="space-y-3">
617 <div className="flex items-center justify-between p-3 bg-orange-50 border border-orange-200 rounded-lg">
618 <div className="flex items-center gap-2">
619 <AlertCircle className="w-4 h-4 text-orange-500" />
620 <span className="text-sm font-medium text-orange-700">
621 Total Due
622 </span>
623 </div>
624 <span className="text-lg font-bold text-orange-700">
625 ${totalPending}
626 </span>
627 </div>
628
629 {pendingBills.map((bill) => (
630 <div
631 key={bill.id}
632 className="flex items-center justify-between py-2"
633 >
634 <div>
635 <p className="text-sm font-medium text-foreground">
636 {bill.description}
637 </p>
638 <p className="text-xs text-muted-foreground">
639 Due: {bill.dueDate}
640 </p>
641 </div>
642 <span className="text-sm font-medium text-foreground">
643 ${bill.amount}
644 </span>
645 </div>
646 ))}
647
648 <button className="w-full mt-4 bg-primary text-primary-foreground py-2 px-4 rounded-md hover:bg-primary/90 transition-colors">
649 Pay Now
650 </button>
651 </div>
652 </div>
653 </div>
654
655 {/* Available Plans */}
656 <div>
657 <h2 className="text-2xl font-semibold text-foreground mb-6">
658 Available Plans
659 </h2>
660
661 <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
662 {plans.map((plan) => (
663 <motion.div
664 key={plan.id}
665 className={`relative bg-background border rounded-lg p-6 cursor-pointer transition-all ${
666 selectedPlan === plan.id
667 ? "border-primary ring-2 ring-primary/20"
668 : "border-border hover:border-primary/50"
669 } ${
670 plan.current ? "ring-2 ring-green-500/20 border-green-500" : ""
671 }`}
672 onClick={() => setSelectedPlan(plan.id)}
673 whileHover={{ scale: 1.02 }}
674 whileTap={{ scale: 0.98 }}
675 >
676 {plan.popular && (
677 <div className="absolute -top-3 left-1/2 transform -translate-x-1/2">
678 <span className="bg-primary text-primary-foreground px-3 py-1 rounded-full text-xs font-medium">
679 Most Popular
680 </span>
681 </div>
682 )}
683
684 {plan.current && (
685 <div className="absolute -top-3 right-4">
686 <span className="bg-green-500 text-white px-3 py-1 rounded-full text-xs font-medium">
687 Current Plan
688 </span>
689 </div>
690 )}
691
692 <div className="flex items-center gap-3 mb-4">
693 <div
694 className={`w-10 h-10 rounded-lg flex items-center justify-center ${
695 selectedPlan === plan.id ? "bg-primary/10" : "bg-muted"
696 }`}
697 >
698 <plan.icon
699 className={`w-5 h-5 ${
700 selectedPlan === plan.id
701 ? "text-primary"
702 : "text-muted-foreground"
703 }`}
704 />
705 </div>
706 <div>
707 <h3 className="text-lg font-semibold text-foreground">
708 {plan.name}
709 </h3>
710 <p className="text-sm text-muted-foreground">
711 {plan.description}
712 </p>
713 </div>
714 </div>
715
716 <div className="mb-4">
717 <div className="flex items-baseline gap-2">
718 <span className="text-2xl font-bold text-foreground">
719 ${isYearly ? plan.price.yearly : plan.price.monthly}
720 </span>
721 <span className="text-muted-foreground">
722 /{isYearly ? "year" : "month"}
723 </span>
724 </div>
725 {isYearly && (
726 <p className="text-sm text-green-600 mt-1">
727 Save ${plan.price.monthly * 12 - plan.price.yearly} per year
728 </p>
729 )}
730 </div>
731
732 <div className="space-y-2">
733 {plan.features.slice(0, 4).map((feature, index) => (
734 <div key={index} className="flex items-center gap-2">
735 <Check className="w-3 h-3 text-green-500 flex-shrink-0" />
736 <span className="text-xs text-muted-foreground">
737 {feature}
738 </span>
739 </div>
740 ))}
741 {plan.features.length > 4 && (
742 <p className="text-xs text-muted-foreground">
743 +{plan.features.length - 4} more features
744 </p>
745 )}
746 </div>
747
748 {!plan.current && (
749 <button
750 className={`w-full mt-4 py-2 px-4 rounded-md transition-colors ${
751 selectedPlan === plan.id
752 ? "bg-primary text-primary-foreground hover:bg-primary/90"
753 : "bg-muted text-muted-foreground hover:bg-muted/80"
754 }`}
755 >
756 {selectedPlan === plan.id ? "Selected" : "Select Plan"}
757 </button>
758 )}
759 </motion.div>
760 ))}
761 </div>
762 </div>
763
764 {/* Add-ons */}
765 <div>
766 <h2 className="text-2xl font-semibold text-foreground mb-6">Add-ons</h2>
767
768 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
769 {addOns.map((addon) => (
770 <div
771 key={addon.id}
772 className="bg-background border border-border rounded-lg p-6"
773 >
774 <div className="flex items-center justify-between mb-4">
775 <div>
776 <h3 className="text-lg font-semibold text-foreground">
777 {addon.name}
778 </h3>
779 <p className="text-sm text-muted-foreground">
780 {addon.description}
781 </p>
782 </div>
783 <button
784 onClick={() => toggleAddOn(addon.id)}
785 className={`relative w-10 h-6 rounded-full transition-colors ${
786 enabledAddOns.includes(addon.id) ? "bg-primary" : "bg-muted"
787 }`}
788 >
789 <div
790 className={`absolute top-0.5 w-5 h-5 bg-background rounded-full shadow-sm transition-transform ${
791 enabledAddOns.includes(addon.id)
792 ? "translate-x-4"
793 : "translate-x-0.5"
794 }`}
795 />
796 </button>
797 </div>
798
799 <div className="flex items-baseline gap-2">
800 <span className="text-xl font-bold text-foreground">
801 ${isYearly ? addon.price.yearly : addon.price.monthly}
802 </span>
803 <span className="text-muted-foreground">
804 /{isYearly ? "year" : "month"}
805 </span>
806 </div>
807 </div>
808 ))}
809 </div>
810 </div>
811
812 {/* Total Cost Summary */}
813 <div className="bg-muted/50 border border-border rounded-lg p-6">
814 <h2 className="text-xl font-semibold text-foreground mb-4">
815 Cost Summary
816 </h2>
817
818 <div className="space-y-3">
819 <div className="flex items-center justify-between">
820 <span className="text-foreground">
821 {plans.find((p) => p.id === selectedPlan)?.name} Plan
822 </span>
823 <span className="font-medium text-foreground">
824 $
825 {plans.find((p) => p.id === selectedPlan)
826 ? isYearly
827 ? plans.find((p) => p.id === selectedPlan)!.price.yearly
828 : plans.find((p) => p.id === selectedPlan)!.price.monthly
829 : 0}
830 </span>
831 </div>
832
833 {addOns
834 .filter((addon) => enabledAddOns.includes(addon.id))
835 .map((addon) => (
836 <div key={addon.id} className="flex items-center justify-between">
837 <span className="text-muted-foreground">{addon.name}</span>
838 <span className="text-foreground">
839 ${isYearly ? addon.price.yearly : addon.price.monthly}
840 </span>
841 </div>
842 ))}
843
844 <div className="border-t border-border pt-3">
845 <div className="flex items-center justify-between">
846 <span className="text-lg font-semibold text-foreground">
847 Total {isYearly ? "Yearly" : "Monthly"}
848 </span>
849 <span className="text-2xl font-bold text-foreground">
850 ${calculateTotal()}
851 </span>
852 </div>
853 </div>
854 </div>
855
856 <button className="w-full mt-6 bg-primary text-primary-foreground py-3 px-4 rounded-md hover:bg-primary/90 transition-colors font-medium">
857 Update Plan
858 </button>
859 </div>
860
861 {/* Recent Bills */}
862 <div>
863 <h2 className="text-2xl font-semibold text-foreground mb-6">
864 Recent Bills
865 </h2>
866
867 <div className="bg-background border border-border rounded-lg overflow-hidden">
868 <div className="overflow-x-auto">
869 <table className="w-full">
870 <thead className="bg-muted/50">
871 <tr>
872 <th className="text-left py-3 px-4 font-medium text-foreground">
873 Description
874 </th>
875 <th className="text-left py-3 px-4 font-medium text-foreground">
876 Amount
877 </th>
878 <th className="text-left py-3 px-4 font-medium text-foreground">
879 Due Date
880 </th>
881 <th className="text-left py-3 px-4 font-medium text-foreground">
882 Status
883 </th>
884 <th className="text-left py-3 px-4 font-medium text-foreground">
885 Type
886 </th>
887 </tr>
888 </thead>
889 <tbody>
890 {bills.map((bill) => (
891 <tr key={bill.id} className="border-t border-border">
892 <td className="py-3 px-4 text-foreground">
893 {bill.description}
894 </td>
895 <td className="py-3 px-4 text-foreground font-medium">
896 ${bill.amount}
897 </td>
898 <td className="py-3 px-4 text-muted-foreground">
899 {bill.dueDate}
900 </td>
901 <td className="py-3 px-4">
902 <span
903 className={`px-2 py-1 rounded-full text-xs font-medium ${
904 bill.status === "paid"
905 ? "bg-green-100 text-green-700"
906 : bill.status === "pending"
907 ? "bg-orange-100 text-orange-700"
908 : "bg-red-100 text-red-700"
909 }`}
910 >
911 {bill.status.charAt(0).toUpperCase() +
912 bill.status.slice(1)}
913 </span>
914 </td>
915 <td className="py-3 px-4 text-muted-foreground capitalize">
916 {bill.type}
917 </td>
918 </tr>
919 ))}
920 </tbody>
921 </table>
922 </div>
923 </div>
924 </div>
925 </div>
926 );
927}
928
929function CustomerAdminDashboard() {
930 const [activeItem, setActiveItem] = useState("billing");
931
932 return (
933 <div className="flex h-screen bg-background">
934 <Sidebar activeItem={activeItem} setActiveItem={setActiveItem} />
935
936 <div className="flex-1 overflow-auto">
937 <div className="md:ml-72">
938 {activeItem === "billing" && <BillingDashboard />}
939 {activeItem === "dashboard" && (
940 <div className="p-6">
941 <h1 className="text-3xl font-bold text-foreground">Dashboard</h1>
942 <p className="text-muted-foreground mt-2">
943 Welcome to your admin dashboard
944 </p>
945 </div>
946 )}
947 {activeItem !== "billing" && activeItem !== "dashboard" && (
948 <div className="p-6">
949 <h1 className="text-3xl font-bold text-foreground capitalize">
950 {activeItem}
951 </h1>
952 <p className="text-muted-foreground mt-2">
953 This section is under development
954 </p>
955 </div>
956 )}
957 </div>
958 </div>
959 </div>
960 );
961}
962
963export default CustomerAdminDashboard;
Dependencies
External Libraries
framer-motionlucide-reactreact