@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,167 @@
1
+ import React from "react";
2
+
3
+ const COLOR_STYLES = {
4
+ blue: {
5
+ icon: "bg-blue-500/10 text-blue-600 dark:text-blue-400",
6
+ activeBorder:
7
+ "border-blue-500/50 shadow-sm ring-1 ring-blue-500/20 bg-gradient-to-br from-base-100 to-blue-500/[0.03]",
8
+ },
9
+ emerald: {
10
+ icon: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
11
+ activeBorder:
12
+ "border-emerald-500/50 shadow-sm ring-1 ring-emerald-500/20 bg-gradient-to-br from-base-100 to-emerald-500/[0.03]",
13
+ },
14
+ amber: {
15
+ icon: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
16
+ activeBorder:
17
+ "border-amber-500/50 shadow-sm ring-1 ring-amber-500/20 bg-gradient-to-br from-base-100 to-amber-500/[0.03]",
18
+ },
19
+ purple: {
20
+ icon: "bg-purple-500/10 text-purple-600 dark:text-purple-400",
21
+ activeBorder:
22
+ "border-purple-500/50 shadow-sm ring-1 ring-purple-500/20 bg-gradient-to-br from-base-100 to-purple-500/[0.03]",
23
+ },
24
+ primary: {
25
+ icon: "bg-primary/10 text-primary",
26
+ activeBorder:
27
+ "border-primary/50 shadow-sm ring-1 ring-primary/20 bg-gradient-to-br from-base-100 to-primary/[0.03]",
28
+ },
29
+ secondary: {
30
+ icon: "bg-secondary/10 text-secondary",
31
+ activeBorder:
32
+ "border-secondary/50 shadow-sm ring-1 ring-secondary/20 bg-gradient-to-br from-base-100 to-secondary/[0.03]",
33
+ },
34
+ success: {
35
+ icon: "bg-success/10 text-success",
36
+ activeBorder:
37
+ "border-success/50 shadow-sm ring-1 ring-success/20 bg-gradient-to-br from-base-100 to-success/[0.03]",
38
+ },
39
+ warning: {
40
+ icon: "bg-warning/10 text-warning",
41
+ activeBorder:
42
+ "border-warning/50 shadow-sm ring-1 ring-warning/20 bg-gradient-to-br from-base-100 to-warning/[0.03]",
43
+ },
44
+ info: {
45
+ icon: "bg-info/10 text-info",
46
+ activeBorder:
47
+ "border-info/50 shadow-sm ring-1 ring-info/20 bg-gradient-to-br from-base-100 to-info/[0.03]",
48
+ },
49
+ error: {
50
+ icon: "bg-error/10 text-error",
51
+ activeBorder:
52
+ "border-error/50 shadow-sm ring-1 ring-error/20 bg-gradient-to-br from-base-100 to-error/[0.03]",
53
+ },
54
+ };
55
+
56
+ export const StatsCard = ({
57
+ title,
58
+ value,
59
+ subValue,
60
+ trend,
61
+ icon,
62
+ color = "primary",
63
+ percentage,
64
+ badge,
65
+ description,
66
+ active = false,
67
+ loading = false,
68
+ onClick,
69
+ className = "",
70
+ }) => {
71
+ const styles = COLOR_STYLES[color] || COLOR_STYLES.primary;
72
+
73
+ // Support both new `subValue` prop and deprecated `percentage` prop
74
+ const displaySubValue =
75
+ subValue !== undefined
76
+ ? subValue
77
+ : percentage !== undefined && percentage !== null
78
+ ? typeof percentage === "number"
79
+ ? `${percentage}%`
80
+ : percentage
81
+ : null;
82
+
83
+ const renderBadge = () => {
84
+ if (!badge) return null;
85
+ if (React.isValidElement(badge)) return badge;
86
+ const b = badge;
87
+ return (
88
+ <span
89
+ className={`badge badge-xs font-medium ${b.variant ? `badge-${b.variant}` : "badge-ghost"}`}
90
+ >
91
+ {b.label}
92
+ </span>
93
+ );
94
+ };
95
+
96
+ const renderTrend = () => {
97
+ if (!trend) return null;
98
+ if (React.isValidElement(trend)) return trend;
99
+ const t = trend;
100
+ const isPos = t.isPositive ?? true;
101
+ return (
102
+ <span
103
+ className={`inline-flex items-center gap-0.5 text-xs font-semibold ${
104
+ isPos ? "text-success" : "text-error"
105
+ }`}
106
+ >
107
+ <span>{isPos ? "↑" : "↓"}</span>
108
+ <span>{t.value}</span>
109
+ {t.label && (
110
+ <span className="opacity-70 font-normal ml-0.5">{t.label}</span>
111
+ )}
112
+ </span>
113
+ );
114
+ };
115
+
116
+ return (
117
+ <div
118
+ onClick={onClick}
119
+ className={`card bg-base-100 border transition-all duration-200 p-4 rounded-2xl ${
120
+ onClick ? "cursor-pointer hover:shadow-sm active:scale-[0.99]" : ""
121
+ } ${
122
+ active ? styles.activeBorder : "border-base-200 hover:border-base-300"
123
+ } ${className}`}
124
+ >
125
+ {/* Top row: Title and Icon */}
126
+ <div className="flex items-center justify-between gap-2">
127
+ <span className="text-xs font-semibold uppercase tracking-wider text-base-content/60 truncate">
128
+ {title}
129
+ </span>
130
+ {icon && (
131
+ <div
132
+ className={`w-9 h-9 rounded-xl flex items-center justify-center shrink-0 ${styles.icon}`}
133
+ >
134
+ {icon}
135
+ </div>
136
+ )}
137
+ </div>
138
+
139
+ {/* Middle row: Value and optional SubValue/Trend */}
140
+ <div className="mt-2 flex items-baseline gap-2 flex-wrap">
141
+ <span className="text-2xl md:text-3xl font-bold tracking-tight text-base-content">
142
+ {loading ? (
143
+ <span className="loading loading-spinner loading-sm"></span>
144
+ ) : (
145
+ value
146
+ )}
147
+ </span>
148
+ {displaySubValue !== null && !loading && (
149
+ <span className="text-xs font-semibold opacity-80">
150
+ {displaySubValue}
151
+ </span>
152
+ )}
153
+ {!loading && renderTrend()}
154
+ </div>
155
+
156
+ {/* Bottom row: Badge and description */}
157
+ {(badge || description) && (
158
+ <div className="mt-2 flex items-center gap-1.5 text-xs text-base-content/60 min-w-0">
159
+ {renderBadge()}
160
+ {description && <span className="truncate">{description}</span>}
161
+ </div>
162
+ )}
163
+ </div>
164
+ );
165
+ };
166
+
167
+ export default StatsCard;
@@ -1,5 +1,6 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import TableSkeleton from "../skeletons/common/TableSkeleton";
1
+ import React, { useEffect, useRef } from "react";
2
+ import { Pagination } from "./Pagination";
3
+
3
4
  const Table = ({
4
5
  columns = [],
5
6
  data = [],
@@ -9,135 +10,250 @@ const Table = ({
9
10
  currentPage = 1,
10
11
  totalPages = 1,
11
12
  onPageChange = null,
13
+ wrapperClassName = "",
14
+ renderCard,
15
+ pagination = false,
16
+ header,
12
17
  }) => {
13
- if (loading) {
14
- return /* @__PURE__ */ jsx(TableSkeleton, {
15
- rows: 5,
16
- columns: columns.length || 4,
17
- });
18
- }
19
- return /* @__PURE__ */ jsxs("div", {
20
- className:
21
- "flex flex-col w-full border border-base-300 rounded-xl overflow-hidden bg-base-100 shadow",
22
- children: [
23
- /* @__PURE__ */ jsx("div", {
24
- className: "overflow-x-auto w-full",
25
- children: /* @__PURE__ */ jsxs("table", {
26
- className: "table table-hover w-full min-w-full",
27
- children: [
28
- /* @__PURE__ */ jsx("thead", {
29
- className: "bg-base-200/60 border-b border-base-300",
30
- children: /* @__PURE__ */ jsx("tr", {
31
- children: columns.map((col, index) =>
32
- /* @__PURE__ */ jsx(
33
- "th",
34
- {
35
- className:
36
- "px-6 py-4 text-left text-xs font-bold text-base-content/80 uppercase tracking-wider",
37
- children: col.header,
38
- },
39
- col.accessor || index,
40
- ),
41
- ),
42
- }),
43
- }),
44
- /* @__PURE__ */ jsx("tbody", {
45
- className: "divide-y divide-base-300 bg-base-100",
46
- children:
47
- data.length > 0
48
- ? data.map((row, rowIndex) =>
49
- /* @__PURE__ */ jsx(
50
- "tr",
51
- {
52
- onClick: () => onRowClick && onRowClick(row),
53
- className: `transition-colors duration-150 ${onRowClick ? "cursor-pointer hover:bg-base-200" : ""}`,
54
- children: columns.map((col, colIndex) => {
55
- const value = col.accessor
56
- ? row[col.accessor]
57
- : void 0;
58
- return /* @__PURE__ */ jsx(
59
- "td",
60
- {
61
- className:
62
- "px-6 py-4 text-sm text-base-content/90 whitespace-nowrap align-middle",
63
- children: col.render
64
- ? col.render(value, row, rowIndex)
65
- : value,
66
- },
67
- col.accessor || colIndex,
68
- );
69
- }),
70
- },
71
- row.id || rowIndex,
72
- ),
73
- )
74
- : /* @__PURE__ */ jsx("tr", {
75
- children: /* @__PURE__ */ jsx("td", {
76
- colSpan: columns.length,
77
- className:
78
- "px-6 py-12 text-center text-sm text-base-content/60",
79
- children: /* @__PURE__ */ jsxs("div", {
80
- className:
81
- "flex flex-col items-center justify-center gap-2",
82
- children: [
83
- /* @__PURE__ */ jsx("span", {
84
- className: "text-2xl",
85
- children: "\u{1F4C1}",
86
- }),
87
- /* @__PURE__ */ jsx("span", {
88
- children: emptyMessage,
89
- }),
90
- ],
91
- }),
92
- }),
93
- }),
94
- }),
95
- ],
96
- }),
97
- }),
98
- totalPages > 1 &&
99
- onPageChange &&
100
- /* @__PURE__ */ jsxs("div", {
101
- className:
102
- "border-t border-base-300 px-6 py-4 bg-base-200/30 flex items-center justify-between shrink-0",
103
- children: [
104
- /* @__PURE__ */ jsxs("div", {
105
- className: "text-xs text-base-content/75 font-medium",
106
- children: [
107
- "Page ",
108
- /* @__PURE__ */ jsx("span", {
109
- className: "font-bold",
110
- children: currentPage,
111
- }),
112
- " of",
113
- " ",
114
- /* @__PURE__ */ jsx("span", {
115
- className: "font-bold",
116
- children: totalPages,
117
- }),
118
- ],
119
- }),
120
- /* @__PURE__ */ jsxs("div", {
121
- className: "flex gap-2",
122
- children: [
123
- /* @__PURE__ */ jsx("button", {
124
- onClick: () => onPageChange(currentPage - 1),
125
- disabled: currentPage <= 1,
126
- className: "btn btn-sm btn-outline rounded-lg text-xs",
127
- children: "Previous",
128
- }),
129
- /* @__PURE__ */ jsx("button", {
130
- onClick: () => onPageChange(currentPage + 1),
131
- disabled: currentPage >= totalPages,
132
- className: "btn btn-sm btn-outline rounded-lg text-xs",
133
- children: "Next",
134
- }),
135
- ],
136
- }),
137
- ],
138
- }),
139
- ],
140
- });
18
+ const observerTarget = useRef(null);
19
+
20
+ useEffect(() => {
21
+ if (!pagination || !onPageChange || currentPage >= totalPages) return;
22
+
23
+ const observer = new IntersectionObserver(
24
+ (entries) => {
25
+ if (entries[0].isIntersecting) {
26
+ onPageChange(currentPage + 1);
27
+ }
28
+ },
29
+ { threshold: 0.1 },
30
+ );
31
+
32
+ if (observerTarget.current) {
33
+ observer.observe(observerTarget.current);
34
+ }
35
+
36
+ return () => observer.disconnect();
37
+ }, [pagination, onPageChange, currentPage, totalPages]);
38
+
39
+ return (
40
+ <div
41
+ className={`flex flex-col w-full h-full md:border md:border-base-300 rounded-xl overflow-hidden bg-base-100 ${wrapperClassName}`}
42
+ >
43
+ {/* Optional Custom Header Area */}
44
+ {header && (
45
+ <div className="shrink-0 bg-base-100 border-b border-base-300">
46
+ {header}
47
+ </div>
48
+ )}
49
+
50
+ {/* Desktop Table View (hidden on mobile if renderCard provided) */}
51
+ <div
52
+ className={`overflow-auto w-full flex-1 ${renderCard ? "hidden md:block" : "block"}`}
53
+ >
54
+ <table className="table table-hover w-full min-w-full relative border-collapse">
55
+ <thead className="bg-base-200 backdrop-blur sticky top-0 z-10 border-b border-base-300">
56
+ <tr>
57
+ {columns.map((col, index) => (
58
+ <th
59
+ key={col.accessor || index}
60
+ className={`px-6 py-4 text-left text-xs font-semibold text-base-content/70 uppercase tracking-wider bg-transparent border-none ${col.headerClass || ""}`}
61
+ >
62
+ {col.header}
63
+ </th>
64
+ ))}
65
+ </tr>
66
+ </thead>
67
+ <tbody className="divide-y divide-base-200/60 bg-base-100">
68
+ {loading ? (
69
+ Array.from({ length: 5 }).map((_, rowIndex) => (
70
+ <tr key={`skeleton-${rowIndex}`} className="animate-pulse">
71
+ {columns.map((col, colIndex) => {
72
+ const isRight = col.headerClass?.includes("text-right");
73
+ const isFirst = colIndex === 0;
74
+ const isLast = colIndex === columns.length - 1;
75
+ const headerText =
76
+ typeof col.header === "string"
77
+ ? col.header.toLowerCase()
78
+ : "";
79
+
80
+ return (
81
+ <td
82
+ key={colIndex}
83
+ className={`px-6 py-4 whitespace-nowrap align-middle border-none ${col.headerClass || ""}`}
84
+ >
85
+ {isRight ||
86
+ (isLast && headerText.includes("action")) ? (
87
+ <div className="flex justify-end items-center gap-2">
88
+ <div className="w-7 h-7 rounded-lg bg-base-300/80"></div>
89
+ <div className="w-7 h-7 rounded-lg bg-base-300/80"></div>
90
+ </div>
91
+ ) : headerText.includes("assigned") ? (
92
+ <div className="h-6 w-28 rounded-md bg-base-200 border border-base-300/80"></div>
93
+ ) : isFirst ? (
94
+ <div className="space-y-1.5">
95
+ <div
96
+ className="h-4 bg-base-300/80 rounded"
97
+ style={{
98
+ width: `${65 + (rowIndex % 3) * 15}%`,
99
+ maxWidth: "140px",
100
+ }}
101
+ ></div>
102
+ <div
103
+ className="h-3 bg-base-200 rounded"
104
+ style={{
105
+ width: `${40 + (rowIndex % 2) * 15}%`,
106
+ maxWidth: "90px",
107
+ }}
108
+ ></div>
109
+ </div>
110
+ ) : (
111
+ <div
112
+ className={`h-4 bg-base-300/70 rounded ${isRight ? "ml-auto" : ""}`}
113
+ style={{
114
+ width: `${50 + ((rowIndex + colIndex) % 4) * 12}%`,
115
+ maxWidth: "160px",
116
+ }}
117
+ ></div>
118
+ )}
119
+ </td>
120
+ );
121
+ })}
122
+ </tr>
123
+ ))
124
+ ) : data.length > 0 ? (
125
+ data.map((row, rowIndex) => (
126
+ <tr
127
+ key={row.id || rowIndex}
128
+ onClick={() => onRowClick && onRowClick(row)}
129
+ className={`transition-all duration-200 group ${
130
+ onRowClick
131
+ ? "cursor-pointer hover:bg-base-200/30"
132
+ : "hover:bg-base-200/10"
133
+ }`}
134
+ >
135
+ {columns.map((col, colIndex) => {
136
+ const value = col.accessor ? row[col.accessor] : undefined;
137
+ return (
138
+ <td
139
+ key={col.accessor || colIndex}
140
+ className="px-6 py-4 text-[13px] text-base-content/80 whitespace-nowrap align-middle border-none"
141
+ >
142
+ {col.render ? col.render(value, row, rowIndex) : value}
143
+ </td>
144
+ );
145
+ })}
146
+ </tr>
147
+ ))
148
+ ) : (
149
+ <tr>
150
+ <td
151
+ colSpan={columns.length}
152
+ className="px-6 py-12 text-center text-sm text-base-content/60"
153
+ >
154
+ <div className="flex flex-col items-center justify-center gap-2">
155
+ <span className="text-2xl">📁</span>
156
+ <span>{emptyMessage}</span>
157
+ </div>
158
+ </td>
159
+ </tr>
160
+ )}
161
+ </tbody>
162
+ </table>
163
+ </div>
164
+
165
+ {/* Mobile Card View (only visible if renderCard provided) */}
166
+ {renderCard && (
167
+ <div className="md:hidden flex flex-col divide-y divide-base-200 overflow-y-auto flex-1">
168
+ {loading ? (
169
+ Array.from({ length: 4 }).map((_, i) => (
170
+ <div
171
+ key={`card-skeleton-${i}`}
172
+ className="p-4 flex flex-col gap-3 animate-pulse"
173
+ >
174
+ <div className="flex justify-between items-start">
175
+ <div className="space-y-1.5 flex-1">
176
+ <div className="h-4.5 bg-base-300/80 rounded w-36"></div>
177
+ <div className="h-3 bg-base-200 rounded w-24"></div>
178
+ </div>
179
+ <div className="flex gap-2">
180
+ <div className="w-7 h-7 rounded-lg bg-base-300/80"></div>
181
+ <div className="w-7 h-7 rounded-lg bg-base-300/80"></div>
182
+ </div>
183
+ </div>
184
+ <div className="space-y-2">
185
+ <div className="h-3.5 bg-base-200 rounded w-48"></div>
186
+ <div className="h-3.5 bg-base-200 rounded w-32"></div>
187
+ </div>
188
+ <div className="flex items-center justify-between pt-2 border-t border-base-200">
189
+ <div className="h-3 bg-base-200 rounded w-20"></div>
190
+ <div className="h-6 bg-base-300/80 rounded-lg w-28"></div>
191
+ </div>
192
+ </div>
193
+ ))
194
+ ) : data.length > 0 ? (
195
+ <>
196
+ {data.map((row, index) => (
197
+ <div
198
+ key={row.id || index}
199
+ onClick={() => onRowClick && onRowClick(row)}
200
+ className={onRowClick ? "cursor-pointer" : ""}
201
+ >
202
+ {renderCard(row, index)}
203
+ </div>
204
+ ))}
205
+ {pagination && currentPage < totalPages && (
206
+ <div ref={observerTarget} className="p-6 flex justify-center">
207
+ <span className="loading loading-spinner loading-sm text-primary"></span>
208
+ </div>
209
+ )}
210
+ </>
211
+ ) : (
212
+ <div className="p-12 text-center text-sm text-base-content/60 flex flex-col items-center gap-2">
213
+ <span className="text-2xl">📁</span>
214
+ <span>{emptyMessage}</span>
215
+ </div>
216
+ )}
217
+ </div>
218
+ )}
219
+
220
+ {/* Desktop Pagination / Load More Footer */}
221
+ {pagination && onPageChange && totalPages > 1 && (
222
+ <div
223
+ className={`border-t border-base-300 px-6 py-4 bg-base-200/30 flex items-center shrink-0 ${renderCard ? "hidden md:flex justify-between" : "justify-between"}`}
224
+ >
225
+ <div className="text-xs text-base-content/75 font-medium">
226
+ Showing <span className="font-bold">{data.length}</span> items
227
+ </div>
228
+ <div className="flex gap-2">
229
+ {currentPage < totalPages && (
230
+ <button
231
+ onClick={() => onPageChange(currentPage + 1)}
232
+ className="btn btn-sm btn-outline btn-primary rounded-lg text-xs"
233
+ >
234
+ Load More
235
+ </button>
236
+ )}
237
+ </div>
238
+ </div>
239
+ )}
240
+
241
+ {/* Standard Pagination (if pagination prop is not true) */}
242
+ {!pagination && onPageChange && totalPages > 0 && (
243
+ <div className="border-t border-base-300 px-3 sm:px-6 py-2.5 sm:py-4 bg-base-200/30 flex items-center justify-between shrink-0 mt-auto gap-2">
244
+ <div className="text-xs text-base-content/75 font-medium whitespace-nowrap">
245
+ Page <span className="font-bold">{currentPage}</span> of{" "}
246
+ <span className="font-bold">{totalPages}</span>
247
+ </div>
248
+ <Pagination
249
+ currentPage={currentPage}
250
+ totalPages={totalPages}
251
+ onPageChange={onPageChange}
252
+ />
253
+ </div>
254
+ )}
255
+ </div>
256
+ );
141
257
  };
142
- var Table_default = Table;
143
- export { Table_default as default };
258
+
259
+ export default Table;