@imksh/create 1.0.30 → 1.0.31

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.
Files changed (102) hide show
  1. package/package.json +1 -1
  2. package/src/utils/askTemplate.js +2 -1
  3. package/templates/layers/electron/base/electron/main.js +262 -0
  4. package/templates/layers/electron/base/electron/preload.js +37 -0
  5. package/templates/layers/electron/base/electron-builder.json5 +68 -0
  6. package/templates/layers/electron/javascript/_gitignore +38 -0
  7. package/templates/layers/electron/javascript/package.json +49 -0
  8. package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
  9. package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
  10. package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
  11. package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
  12. package/templates/layers/electron/javascript/src/main.jsx +17 -0
  13. package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
  14. package/templates/layers/electron/javascript/vite.config.js +29 -0
  15. package/templates/layers/electron/typescript/_gitignore +38 -0
  16. package/templates/layers/electron/typescript/electron/main.ts +213 -0
  17. package/templates/layers/electron/typescript/electron/preload.ts +52 -0
  18. package/templates/layers/electron/typescript/package.json +52 -0
  19. package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
  20. package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
  21. package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
  22. package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
  23. package/templates/layers/electron/typescript/src/main.tsx +17 -0
  24. package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
  25. package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
  26. package/templates/layers/electron/typescript/tsconfig.json +25 -0
  27. package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
  28. package/templates/layers/electron/typescript/vite.config.ts +29 -0
  29. package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
  30. package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
  31. package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
  32. package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
  33. package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
  34. package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
  35. package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
  36. package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
  37. package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
  38. package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
  39. package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
  40. package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
  41. package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
  42. package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
  43. package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
  44. package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
  45. package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
  46. package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
  47. package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
  48. package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
  49. package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
  50. package/templates/layers/next/typescript/src/components/common/Loading.tsx +1 -0
  51. package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
  52. package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
  53. package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
  54. package/templates/layers/next/typescript/src/components/common/Table.tsx +201 -35
  55. package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
  56. package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
  57. package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
  58. package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
  59. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +1 -0
  60. package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
  61. package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
  62. package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
  63. package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
  64. package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
  65. package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
  66. package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
  67. package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
  68. package/templates/layers/node/typescript/package-lock.json +2 -2
  69. package/templates/layers/node/typescript/package.json +1 -1
  70. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  71. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  72. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  73. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  74. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  75. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  76. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  77. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  78. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  79. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  80. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  81. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  82. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  83. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  84. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  85. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  86. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  87. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  88. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  89. package/templates/layers/react/typescript/src/components/common/Table.tsx +197 -34
  90. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  91. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  92. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  93. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  94. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  95. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  96. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  97. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  98. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  99. package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
  100. package/templates/presets/electron/preset.json +9 -0
  101. package/templates/presets/electron-ts/preset.json +9 -0
  102. package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
@@ -0,0 +1,199 @@
1
+ import React, { useEffect, ReactNode } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import { motion, AnimatePresence } from "framer-motion";
4
+ import { IoClose } from "react-icons/io5";
5
+ import { useUiStore } from "../../store/useUiStore";
6
+
7
+ export type SidebarModalSide = "left" | "right";
8
+
9
+ export interface SidebarModalProps {
10
+ isOpen: boolean;
11
+ onClose: () => void;
12
+ side?: SidebarModalSide;
13
+ title?: ReactNode;
14
+ description?: ReactNode;
15
+ children: ReactNode;
16
+ width?: string;
17
+ showCloseButton?: boolean;
18
+ closeOnBackdrop?: boolean;
19
+ closeOnEsc?: boolean;
20
+ className?: string;
21
+ header?: ReactNode;
22
+ footer?: ReactNode;
23
+ bodyClassName?: string;
24
+ hasBackdrop?: boolean;
25
+ }
26
+
27
+ export const SidebarModal: React.FC<SidebarModalProps> = ({
28
+ isOpen,
29
+ onClose,
30
+ side = "right",
31
+ title,
32
+ description,
33
+ children,
34
+ width = "w-full sm:w-[420px]",
35
+ showCloseButton = true,
36
+ closeOnBackdrop = true,
37
+ closeOnEsc = true,
38
+ className = "",
39
+ header,
40
+ footer,
41
+ bodyClassName = "p-4 space-y-4",
42
+ hasBackdrop = true,
43
+ }) => {
44
+ const setModalOpen = useUiStore((state) => state.setModalOpen);
45
+
46
+ // Sync with global UI store modal state for scroll locking
47
+ useEffect(() => {
48
+ if (isOpen) {
49
+ setModalOpen(true);
50
+ return () => {
51
+ setModalOpen(false);
52
+ };
53
+ }
54
+ }, [isOpen, setModalOpen]);
55
+
56
+ // Handle ESC key
57
+ useEffect(() => {
58
+ if (!isOpen || !closeOnEsc) return;
59
+
60
+ const handleKeyDown = (e: KeyboardEvent) => {
61
+ if (e.key === "Escape") {
62
+ onClose();
63
+ }
64
+ };
65
+
66
+ window.addEventListener("keydown", handleKeyDown);
67
+ return () => window.removeEventListener("keydown", handleKeyDown);
68
+ }, [isOpen, closeOnEsc, onClose]);
69
+
70
+ const slideVariants = {
71
+ hidden: {
72
+ x: side === "right" ? "100%" : "-100%",
73
+ opacity: 0.7,
74
+ },
75
+ visible: {
76
+ x: 0,
77
+ opacity: 1,
78
+ transition: {
79
+ type: "spring",
80
+ damping: 30,
81
+ stiffness: 300,
82
+ mass: 0.8,
83
+ },
84
+ },
85
+ exit: {
86
+ x: side === "right" ? "100%" : "-100%",
87
+ opacity: 0.5,
88
+ transition: {
89
+ type: "tween",
90
+ duration: 0.22,
91
+ ease: "easeInOut",
92
+ },
93
+ },
94
+ };
95
+
96
+ const backdropVariants = {
97
+ hidden: { opacity: 0 },
98
+ visible: { opacity: 1, transition: { duration: 0.2 } },
99
+ exit: { opacity: 0, transition: { duration: 0.18 } },
100
+ };
101
+
102
+ const content = (
103
+ <AnimatePresence>
104
+ {isOpen && (
105
+ <div className="fixed inset-0 z-50 overflow-hidden">
106
+ {/* Backdrop */}
107
+ {hasBackdrop && (
108
+ <motion.div
109
+ variants={backdropVariants}
110
+ initial="hidden"
111
+ animate="visible"
112
+ exit="exit"
113
+ className="fixed inset-0 bg-black/40 backdrop-blur-xs transition-opacity"
114
+ onClick={() => {
115
+ if (closeOnBackdrop) onClose();
116
+ }}
117
+ />
118
+ )}
119
+
120
+ {/* Sliding Panel */}
121
+ <div
122
+ className={`fixed inset-y-0 ${
123
+ side === "right" ? "right-0" : "left-0"
124
+ } max-w-full flex`}
125
+ >
126
+ <motion.div
127
+ variants={slideVariants}
128
+ initial="hidden"
129
+ animate="visible"
130
+ exit="exit"
131
+ className={`
132
+ relative h-full flex flex-col bg-base-100 border-base-300 shadow-2xl overflow-hidden
133
+ ${side === "right" ? "border-l" : "border-r"}
134
+ ${width}
135
+ ${className}
136
+ `}
137
+ onClick={(e) => e.stopPropagation()}
138
+ >
139
+ {/* Optional Custom Header or Default Header */}
140
+ {header ? (
141
+ <div className="shrink-0">{header}</div>
142
+ ) : title ? (
143
+ <div className="flex items-center justify-between p-4 border-b border-base-300 bg-base-200/40 shrink-0">
144
+ <div className="flex-1 min-w-0 pr-2">
145
+ <h2 className="text-base font-bold text-base-content truncate">
146
+ {title}
147
+ </h2>
148
+ {description && (
149
+ <p className="text-xs text-base-content/60 truncate mt-0.5">
150
+ {description}
151
+ </p>
152
+ )}
153
+ </div>
154
+ {showCloseButton && (
155
+ <button
156
+ type="button"
157
+ className="btn btn-circle btn-sm btn-error btn-soft text-error/70 hover:text-error hover:bg-error/20 hover:shadow-sm hover:rotate-180 transition-transform duration-200"
158
+ onClick={onClose}
159
+ title="Close"
160
+ >
161
+ <IoClose size={18} />
162
+ </button>
163
+ )}
164
+ </div>
165
+ ) : showCloseButton ? (
166
+ <button
167
+ type="button"
168
+ className="btn btn-circle btn-sm btn-error btn-soft text-error/70 hover:text-error hover:bg-error/20 hover:shadow-sm hover:rotate-180 transition-transform duration-200"
169
+ onClick={onClose}
170
+ title="Close"
171
+ >
172
+ <IoClose size={18} />
173
+ </button>
174
+ ) : null}
175
+
176
+ {/* Scrollable Body */}
177
+ <div className={`flex-1 overflow-y-auto ${bodyClassName}`}>
178
+ {children}
179
+ </div>
180
+
181
+ {/* Optional Footer */}
182
+ {footer && (
183
+ <div className="p-4 border-t border-base-300 bg-base-200/30 shrink-0">
184
+ {footer}
185
+ </div>
186
+ )}
187
+ </motion.div>
188
+ </div>
189
+ </div>
190
+ )}
191
+ </AnimatePresence>
192
+ );
193
+
194
+ return typeof document !== "undefined"
195
+ ? createPortal(content, document.body)
196
+ : null;
197
+ };
198
+
199
+ export default SidebarModal;
@@ -0,0 +1,231 @@
1
+ import React, { ReactNode } from "react";
2
+
3
+ export type StatsCardColor =
4
+ | "blue"
5
+ | "emerald"
6
+ | "amber"
7
+ | "purple"
8
+ | "primary"
9
+ | "secondary"
10
+ | "success"
11
+ | "warning"
12
+ | "info"
13
+ | "error";
14
+
15
+ export interface StatsCardTrend {
16
+ value: string | number;
17
+ isPositive?: boolean;
18
+ label?: string;
19
+ }
20
+
21
+ export interface StatsCardBadge {
22
+ label: string | number;
23
+ variant?:
24
+ | "info"
25
+ | "success"
26
+ | "warning"
27
+ | "error"
28
+ | "neutral"
29
+ | "ghost"
30
+ | "secondary"
31
+ | "primary";
32
+ }
33
+
34
+ export interface StatsCardProps {
35
+ /** Main title or label of the stat card */
36
+ title: ReactNode;
37
+ /** Primary metric value (e.g. number, count, currency) */
38
+ value: ReactNode;
39
+ /** Secondary value displayed beside the main value (e.g. "75%", "Queue", "USD") */
40
+ subValue?: ReactNode;
41
+ /** Trend indicator with direction and value */
42
+ trend?: StatsCardTrend | ReactNode;
43
+ /** Badge displayed in the bottom footer row */
44
+ badge?: StatsCardBadge | ReactNode;
45
+ /** Descriptive subtext displayed in the footer row */
46
+ description?: ReactNode;
47
+ /** Icon displayed in the top-right corner */
48
+ icon?: ReactNode;
49
+ /** Color theme for icon container and active selection border */
50
+ color?: StatsCardColor;
51
+ /** Active selection state with highlighting ring */
52
+ active?: boolean;
53
+ /** Displays a loading spinner in place of value */
54
+ loading?: boolean;
55
+ /** Click handler for interactive selection / drill-down */
56
+ onClick?: () => void;
57
+ /** Custom additional container classes */
58
+ className?: string;
59
+ /** @deprecated use `subValue` instead */
60
+ percentage?: number | string;
61
+ }
62
+
63
+ const COLOR_STYLES: Record<
64
+ StatsCardColor,
65
+ {
66
+ icon: string;
67
+ activeBorder: string;
68
+ }
69
+ > = {
70
+ blue: {
71
+ icon: "bg-blue-500/10 text-blue-600 dark:text-blue-400",
72
+ activeBorder:
73
+ "border-blue-500/50 shadow-sm ring-1 ring-blue-500/20 bg-gradient-to-br from-base-100 to-blue-500/[0.03]",
74
+ },
75
+ emerald: {
76
+ icon: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
77
+ activeBorder:
78
+ "border-emerald-500/50 shadow-sm ring-1 ring-emerald-500/20 bg-gradient-to-br from-base-100 to-emerald-500/[0.03]",
79
+ },
80
+ amber: {
81
+ icon: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
82
+ activeBorder:
83
+ "border-amber-500/50 shadow-sm ring-1 ring-amber-500/20 bg-gradient-to-br from-base-100 to-amber-500/[0.03]",
84
+ },
85
+ purple: {
86
+ icon: "bg-purple-500/10 text-purple-600 dark:text-purple-400",
87
+ activeBorder:
88
+ "border-purple-500/50 shadow-sm ring-1 ring-purple-500/20 bg-gradient-to-br from-base-100 to-purple-500/[0.03]",
89
+ },
90
+ primary: {
91
+ icon: "bg-primary/10 text-primary",
92
+ activeBorder:
93
+ "border-primary/50 shadow-sm ring-1 ring-primary/20 bg-gradient-to-br from-base-100 to-primary/[0.03]",
94
+ },
95
+ secondary: {
96
+ icon: "bg-secondary/10 text-secondary",
97
+ activeBorder:
98
+ "border-secondary/50 shadow-sm ring-1 ring-secondary/20 bg-gradient-to-br from-base-100 to-secondary/[0.03]",
99
+ },
100
+ success: {
101
+ icon: "bg-success/10 text-success",
102
+ activeBorder:
103
+ "border-success/50 shadow-sm ring-1 ring-success/20 bg-gradient-to-br from-base-100 to-success/[0.03]",
104
+ },
105
+ warning: {
106
+ icon: "bg-warning/10 text-warning",
107
+ activeBorder:
108
+ "border-warning/50 shadow-sm ring-1 ring-warning/20 bg-gradient-to-br from-base-100 to-warning/[0.03]",
109
+ },
110
+ info: {
111
+ icon: "bg-info/10 text-info",
112
+ activeBorder:
113
+ "border-info/50 shadow-sm ring-1 ring-info/20 bg-gradient-to-br from-base-100 to-info/[0.03]",
114
+ },
115
+ error: {
116
+ icon: "bg-error/10 text-error",
117
+ activeBorder:
118
+ "border-error/50 shadow-sm ring-1 ring-error/20 bg-gradient-to-br from-base-100 to-error/[0.03]",
119
+ },
120
+ };
121
+
122
+ export const StatsCard: React.FC<StatsCardProps> = ({
123
+ title,
124
+ value,
125
+ subValue,
126
+ trend,
127
+ icon,
128
+ color = "primary",
129
+ percentage,
130
+ badge,
131
+ description,
132
+ active = false,
133
+ loading = false,
134
+ onClick,
135
+ className = "",
136
+ }) => {
137
+ const styles = COLOR_STYLES[color] || COLOR_STYLES.primary;
138
+
139
+ // Support both new `subValue` prop and deprecated `percentage` prop
140
+ const displaySubValue =
141
+ subValue !== undefined
142
+ ? subValue
143
+ : percentage !== undefined && percentage !== null
144
+ ? typeof percentage === "number"
145
+ ? `${percentage}%`
146
+ : percentage
147
+ : null;
148
+
149
+ const renderBadge = () => {
150
+ if (!badge) return null;
151
+ if (React.isValidElement(badge)) return badge;
152
+ const b = badge as StatsCardBadge;
153
+ return (
154
+ <span className={`badge badge-xs font-medium ${b.variant ? `badge-${b.variant}` : "badge-ghost"}`}>
155
+ {b.label}
156
+ </span>
157
+ );
158
+ };
159
+
160
+ const renderTrend = () => {
161
+ if (!trend) return null;
162
+ if (React.isValidElement(trend)) return trend;
163
+ const t = trend as StatsCardTrend;
164
+ const isPos = t.isPositive ?? true;
165
+ return (
166
+ <span
167
+ className={`inline-flex items-center gap-0.5 text-xs font-semibold ${
168
+ isPos ? "text-success" : "text-error"
169
+ }`}
170
+ >
171
+ <span>{isPos ? "↑" : "↓"}</span>
172
+ <span>{t.value}</span>
173
+ {t.label && <span className="opacity-70 font-normal ml-0.5">{t.label}</span>}
174
+ </span>
175
+ );
176
+ };
177
+
178
+ return (
179
+ <div
180
+ onClick={onClick}
181
+ className={`card bg-base-100 border transition-all duration-200 p-4 rounded-2xl ${
182
+ onClick ? "cursor-pointer hover:shadow-sm active:scale-[0.99]" : ""
183
+ } ${
184
+ active
185
+ ? styles.activeBorder
186
+ : "border-base-200 hover:border-base-300"
187
+ } ${className}`}
188
+ >
189
+ {/* Top row: Title and Icon */}
190
+ <div className="flex items-center justify-between gap-2">
191
+ <span className="text-xs font-semibold uppercase tracking-wider text-base-content/60 truncate">
192
+ {title}
193
+ </span>
194
+ {icon && (
195
+ <div
196
+ className={`w-9 h-9 rounded-xl flex items-center justify-center shrink-0 ${styles.icon}`}
197
+ >
198
+ {icon}
199
+ </div>
200
+ )}
201
+ </div>
202
+
203
+ {/* Middle row: Value and optional SubValue/Trend */}
204
+ <div className="mt-2 flex items-baseline gap-2 flex-wrap">
205
+ <span className="text-2xl md:text-3xl font-bold tracking-tight text-base-content">
206
+ {loading ? (
207
+ <span className="loading loading-spinner loading-sm"></span>
208
+ ) : (
209
+ value
210
+ )}
211
+ </span>
212
+ {displaySubValue !== null && !loading && (
213
+ <span className="text-xs font-semibold opacity-80">
214
+ {displaySubValue}
215
+ </span>
216
+ )}
217
+ {!loading && renderTrend()}
218
+ </div>
219
+
220
+ {/* Bottom row: Badge and description */}
221
+ {(badge || description) && (
222
+ <div className="mt-2 flex items-center gap-1.5 text-xs text-base-content/60 min-w-0">
223
+ {renderBadge()}
224
+ {description && <span className="truncate">{description}</span>}
225
+ </div>
226
+ )}
227
+ </div>
228
+ );
229
+ };
230
+
231
+ export default StatsCard;