@rozaqi02/reusable-dashboard 1.0.1 → 1.1.1
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/README.md +64 -473
- package/dist/index.cjs +200 -479
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +742 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +200 -479
- package/dist/index.js.map +1 -1
- package/package.json +10 -5
package/dist/index.cjs
CHANGED
|
@@ -1102,42 +1102,20 @@ function Button({
|
|
|
1102
1102
|
children,
|
|
1103
1103
|
...rest
|
|
1104
1104
|
}) {
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
sm: "px-2.5 py-1 text-xs gap-1",
|
|
1113
|
-
md: "px-3 py-1.5 text-sm gap-1.5",
|
|
1114
|
-
lg: "px-4 py-2 text-base gap-2"
|
|
1115
|
-
};
|
|
1116
|
-
const classes = [base, variants[variant] || variants.primary, sizes[size] || sizes.md, className].filter(Boolean).join(" ");
|
|
1117
|
-
return /* @__PURE__ */ import_react3.default.createElement(
|
|
1118
|
-
"button",
|
|
1119
|
-
{
|
|
1120
|
-
type: "button",
|
|
1121
|
-
className: classes,
|
|
1122
|
-
disabled,
|
|
1123
|
-
onClick,
|
|
1124
|
-
...rest
|
|
1125
|
-
},
|
|
1126
|
-
children
|
|
1127
|
-
);
|
|
1105
|
+
const classes = [
|
|
1106
|
+
"rdb-btn",
|
|
1107
|
+
`rdb-btn-${variant}`,
|
|
1108
|
+
`rdb-btn-${size}`,
|
|
1109
|
+
className
|
|
1110
|
+
].filter(Boolean).join(" ");
|
|
1111
|
+
return /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", className: classes, disabled, onClick, ...rest }, children);
|
|
1128
1112
|
}
|
|
1129
1113
|
Button.propTypes = {
|
|
1130
|
-
/** Varian visual tombol. */
|
|
1131
1114
|
variant: import_prop_types.default.oneOf(["primary", "secondary", "ghost"]),
|
|
1132
|
-
/** Ukuran tombol. */
|
|
1133
1115
|
size: import_prop_types.default.oneOf(["sm", "md", "lg"]),
|
|
1134
|
-
/** Apakah tombol non-aktif. */
|
|
1135
1116
|
disabled: import_prop_types.default.bool,
|
|
1136
|
-
/** ClassName tambahan dari consumer. */
|
|
1137
1117
|
className: import_prop_types.default.string,
|
|
1138
|
-
/** Callback saat tombol diklik. */
|
|
1139
1118
|
onClick: import_prop_types.default.func,
|
|
1140
|
-
/** Konten tombol. */
|
|
1141
1119
|
children: import_prop_types.default.node.isRequired
|
|
1142
1120
|
};
|
|
1143
1121
|
|
|
@@ -1154,14 +1132,7 @@ function Input({
|
|
|
1154
1132
|
className = "",
|
|
1155
1133
|
...rest
|
|
1156
1134
|
}) {
|
|
1157
|
-
|
|
1158
|
-
"px-3 py-2 rounded-2xl border border-slate-300 dark:border-slate-600",
|
|
1159
|
-
"bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100",
|
|
1160
|
-
"text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500",
|
|
1161
|
-
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1162
|
-
className
|
|
1163
|
-
].filter(Boolean).join(" ");
|
|
1164
|
-
return /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex flex-col gap-1" }, label ? /* @__PURE__ */ import_react4.default.createElement("label", { className: "text-xs font-medium text-slate-500 dark:text-slate-400" }, label) : null, /* @__PURE__ */ import_react4.default.createElement(
|
|
1135
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 4 } }, label ? /* @__PURE__ */ import_react4.default.createElement("label", { className: "rdb-label" }, label) : null, /* @__PURE__ */ import_react4.default.createElement(
|
|
1165
1136
|
"input",
|
|
1166
1137
|
{
|
|
1167
1138
|
type,
|
|
@@ -1169,25 +1140,18 @@ function Input({
|
|
|
1169
1140
|
onChange,
|
|
1170
1141
|
placeholder,
|
|
1171
1142
|
disabled,
|
|
1172
|
-
className:
|
|
1143
|
+
className: ["rdb-input", className].filter(Boolean).join(" "),
|
|
1173
1144
|
...rest
|
|
1174
1145
|
}
|
|
1175
1146
|
));
|
|
1176
1147
|
}
|
|
1177
1148
|
Input.propTypes = {
|
|
1178
|
-
/** Tipe input. */
|
|
1179
1149
|
type: import_prop_types2.default.string,
|
|
1180
|
-
/** Nilai input. */
|
|
1181
1150
|
value: import_prop_types2.default.oneOfType([import_prop_types2.default.string, import_prop_types2.default.number]),
|
|
1182
|
-
/** Callback saat nilai berubah. */
|
|
1183
1151
|
onChange: import_prop_types2.default.func,
|
|
1184
|
-
/** Placeholder teks. */
|
|
1185
1152
|
placeholder: import_prop_types2.default.string,
|
|
1186
|
-
/** Label di atas input. */
|
|
1187
1153
|
label: import_prop_types2.default.string,
|
|
1188
|
-
/** Apakah input non-aktif. */
|
|
1189
1154
|
disabled: import_prop_types2.default.bool,
|
|
1190
|
-
/** ClassName tambahan. */
|
|
1191
1155
|
className: import_prop_types2.default.string
|
|
1192
1156
|
};
|
|
1193
1157
|
|
|
@@ -1230,81 +1194,83 @@ Icon.propTypes = {
|
|
|
1230
1194
|
// src/presentation/atoms/Typography.jsx
|
|
1231
1195
|
var import_react6 = __toESM(require("react"), 1);
|
|
1232
1196
|
var import_prop_types4 = __toESM(require("prop-types"), 1);
|
|
1233
|
-
var
|
|
1234
|
-
h1:
|
|
1235
|
-
h2:
|
|
1236
|
-
h3:
|
|
1237
|
-
subheading:
|
|
1238
|
-
body:
|
|
1239
|
-
caption:
|
|
1240
|
-
metric:
|
|
1197
|
+
var VARIANT_CLASS = {
|
|
1198
|
+
h1: "rdb-h1",
|
|
1199
|
+
h2: "rdb-h2",
|
|
1200
|
+
h3: "rdb-h3",
|
|
1201
|
+
subheading: "rdb-subheading",
|
|
1202
|
+
body: "rdb-body",
|
|
1203
|
+
caption: "rdb-caption",
|
|
1204
|
+
metric: "rdb-metric"
|
|
1241
1205
|
};
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1206
|
+
var VARIANT_TAG = {
|
|
1207
|
+
h1: "h1",
|
|
1208
|
+
h2: "h2",
|
|
1209
|
+
h3: "h3",
|
|
1210
|
+
subheading: "p",
|
|
1211
|
+
body: "p",
|
|
1212
|
+
caption: "span",
|
|
1213
|
+
metric: "span"
|
|
1214
|
+
};
|
|
1215
|
+
function Typography({ variant = "body", className = "", children, ...rest }) {
|
|
1216
|
+
const Tag = VARIANT_TAG[variant] || "p";
|
|
1217
|
+
const cls = [VARIANT_CLASS[variant] || "rdb-body", className].filter(Boolean).join(" ");
|
|
1218
|
+
return /* @__PURE__ */ import_react6.default.createElement(Tag, { className: cls, ...rest }, children);
|
|
1252
1219
|
}
|
|
1253
1220
|
Typography.propTypes = {
|
|
1254
|
-
/** Varian tipografi. */
|
|
1255
1221
|
variant: import_prop_types4.default.oneOf(["h1", "h2", "h3", "subheading", "body", "caption", "metric"]),
|
|
1256
|
-
/** ClassName tambahan. */
|
|
1257
1222
|
className: import_prop_types4.default.string,
|
|
1258
|
-
/** Konten teks. */
|
|
1259
1223
|
children: import_prop_types4.default.node.isRequired
|
|
1260
1224
|
};
|
|
1261
1225
|
|
|
1262
1226
|
// src/presentation/atoms/Badge.jsx
|
|
1263
1227
|
var import_react7 = __toESM(require("react"), 1);
|
|
1264
1228
|
var import_prop_types5 = __toESM(require("prop-types"), 1);
|
|
1265
|
-
var
|
|
1266
|
-
confirmed: "
|
|
1267
|
-
pending: "
|
|
1268
|
-
cancelled: "
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1229
|
+
var STATUS_CLASS = {
|
|
1230
|
+
confirmed: "rdb-badge-confirmed",
|
|
1231
|
+
pending: "rdb-badge-pending",
|
|
1232
|
+
cancelled: "rdb-badge-cancelled",
|
|
1233
|
+
success: "rdb-badge-success",
|
|
1234
|
+
info: "rdb-badge-info",
|
|
1235
|
+
default: "rdb-badge-default"
|
|
1272
1236
|
};
|
|
1273
1237
|
function Badge({ status = "default", className = "", children, ...rest }) {
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
return /* @__PURE__ */ import_react7.default.createElement("span", { className: classes, ...rest }, children);
|
|
1238
|
+
const statusClass = STATUS_CLASS[status] || STATUS_CLASS.default;
|
|
1239
|
+
return /* @__PURE__ */ import_react7.default.createElement("span", { className: ["rdb-badge", statusClass, className].filter(Boolean).join(" "), ...rest }, children);
|
|
1277
1240
|
}
|
|
1278
1241
|
Badge.propTypes = {
|
|
1279
|
-
/** Status yang menentukan warna badge. */
|
|
1280
1242
|
status: import_prop_types5.default.string,
|
|
1281
|
-
/** ClassName tambahan. */
|
|
1282
1243
|
className: import_prop_types5.default.string,
|
|
1283
|
-
/** Label teks badge. */
|
|
1284
1244
|
children: import_prop_types5.default.node.isRequired
|
|
1285
1245
|
};
|
|
1286
1246
|
|
|
1287
1247
|
// src/presentation/atoms/SkeletonLoader.jsx
|
|
1288
1248
|
var import_react8 = __toESM(require("react"), 1);
|
|
1289
1249
|
var import_prop_types6 = __toESM(require("prop-types"), 1);
|
|
1290
|
-
function SkeletonLoader({ className = "" }) {
|
|
1250
|
+
function SkeletonLoader({ className = "", style = {} }) {
|
|
1291
1251
|
return /* @__PURE__ */ import_react8.default.createElement(
|
|
1292
1252
|
"div",
|
|
1293
1253
|
{
|
|
1294
|
-
className:
|
|
1254
|
+
className: ["rdb-skeleton", className].filter(Boolean).join(" "),
|
|
1255
|
+
style: { minHeight: 20, ...style },
|
|
1295
1256
|
role: "status",
|
|
1296
1257
|
"aria-label": "Loading..."
|
|
1297
1258
|
}
|
|
1298
1259
|
);
|
|
1299
1260
|
}
|
|
1300
1261
|
SkeletonLoader.propTypes = {
|
|
1301
|
-
|
|
1302
|
-
|
|
1262
|
+
className: import_prop_types6.default.string,
|
|
1263
|
+
style: import_prop_types6.default.object
|
|
1303
1264
|
};
|
|
1304
1265
|
|
|
1305
1266
|
// src/presentation/molecules/StatCard.jsx
|
|
1306
1267
|
var import_react9 = __toESM(require("react"), 1);
|
|
1307
1268
|
var import_prop_types7 = __toESM(require("prop-types"), 1);
|
|
1269
|
+
function renderValue(format, value) {
|
|
1270
|
+
if (format === "currency") return `Rp ${formatIDR(value)}`;
|
|
1271
|
+
if (format === "percent") return `${Number(value) || 0}%`;
|
|
1272
|
+
return String(Number(value) || 0);
|
|
1273
|
+
}
|
|
1308
1274
|
function StatCard({
|
|
1309
1275
|
label,
|
|
1310
1276
|
value,
|
|
@@ -1314,29 +1280,14 @@ function StatCard({
|
|
|
1314
1280
|
accentColor = "blue",
|
|
1315
1281
|
className = ""
|
|
1316
1282
|
}) {
|
|
1317
|
-
|
|
1318
|
-
const ACCENT = {
|
|
1319
|
-
blue: { bg: "bg-blue-50 dark:bg-blue-900/20", icon: "text-blue-600 dark:text-blue-400", bar: "bg-blue-500", border: "border-l-4 border-blue-400" },
|
|
1320
|
-
green: { bg: "bg-emerald-50 dark:bg-emerald-900/20", icon: "text-emerald-600 dark:text-emerald-400", bar: "bg-emerald-500", border: "border-l-4 border-emerald-400" },
|
|
1321
|
-
violet: { bg: "bg-violet-50 dark:bg-violet-900/20", icon: "text-violet-600 dark:text-violet-400", bar: "bg-violet-500", border: "border-l-4 border-violet-400" },
|
|
1322
|
-
orange: { bg: "bg-orange-50 dark:bg-orange-900/20", icon: "text-orange-600 dark:text-orange-400", bar: "bg-orange-500", border: "border-l-4 border-orange-400" },
|
|
1323
|
-
sky: { bg: "bg-sky-50 dark:bg-sky-900/20", icon: "text-sky-600 dark:text-sky-400", bar: "bg-sky-500", border: "border-l-4 border-sky-400" },
|
|
1324
|
-
rose: { bg: "bg-rose-50 dark:bg-rose-900/20", icon: "text-rose-600 dark:text-rose-400", bar: "bg-rose-500", border: "border-l-4 border-rose-400" }
|
|
1325
|
-
};
|
|
1326
|
-
const accent = ACCENT[accentColor] || ACCENT.blue;
|
|
1327
|
-
return /* @__PURE__ */ import_react9.default.createElement("div", { className: `card p-4 flex flex-col justify-between gap-3 ${accent.border} ${className}` }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 text-slate-500 dark:text-slate-400" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: `w-8 h-8 rounded-xl flex items-center justify-center ${accent.bg} ${accent.icon} shrink-0` }, /* @__PURE__ */ import_react9.default.createElement(Icon, { name: icon, size: 16 })), /* @__PURE__ */ import_react9.default.createElement(Typography, { variant: "subheading" }, label)), trend ? /* @__PURE__ */ import_react9.default.createElement(
|
|
1283
|
+
return /* @__PURE__ */ import_react9.default.createElement("div", { className: ["rdb-card rdb-statcard", `rdb-accent-${accentColor}`, className].filter(Boolean).join(" ") }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "rdb-statcard-header" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "rdb-statcard-label-group" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "rdb-statcard-icon" }, /* @__PURE__ */ import_react9.default.createElement(Icon, { name: icon, size: 16 })), /* @__PURE__ */ import_react9.default.createElement("span", { className: "rdb-statcard-label" }, label)), trend ? /* @__PURE__ */ import_react9.default.createElement(
|
|
1328
1284
|
Icon,
|
|
1329
1285
|
{
|
|
1330
1286
|
name: trend === "up" ? "TrendingUp" : "TrendingDown",
|
|
1331
1287
|
size: 16,
|
|
1332
|
-
|
|
1288
|
+
style: { color: trend === "up" ? "#10b981" : "#ef4444" }
|
|
1333
1289
|
}
|
|
1334
|
-
) : null), /* @__PURE__ */ import_react9.default.createElement(
|
|
1335
|
-
}
|
|
1336
|
-
function renderValue(format, value) {
|
|
1337
|
-
if (format === "currency") return `Rp ${formatIDR(value)}`;
|
|
1338
|
-
if (format === "percent") return `${Number(value) || 0}%`;
|
|
1339
|
-
return String(Number(value) || 0);
|
|
1290
|
+
) : null), /* @__PURE__ */ import_react9.default.createElement("div", { className: "rdb-statcard-value" }, renderValue(format, value)));
|
|
1340
1291
|
}
|
|
1341
1292
|
StatCard.propTypes = {
|
|
1342
1293
|
label: import_prop_types7.default.string.isRequired,
|
|
@@ -1344,7 +1295,6 @@ StatCard.propTypes = {
|
|
|
1344
1295
|
icon: import_prop_types7.default.string,
|
|
1345
1296
|
format: import_prop_types7.default.oneOf(["number", "currency", "percent"]),
|
|
1346
1297
|
trend: import_prop_types7.default.oneOf(["up", "down", null]),
|
|
1347
|
-
/** Warna aksen: blue | green | violet | orange | sky | rose */
|
|
1348
1298
|
accentColor: import_prop_types7.default.string,
|
|
1349
1299
|
className: import_prop_types7.default.string
|
|
1350
1300
|
};
|
|
@@ -1362,45 +1312,33 @@ function SearchBar({
|
|
|
1362
1312
|
const [internalValue, setInternalValue] = (0, import_react10.useState)("");
|
|
1363
1313
|
const isControlled = controlledValue !== void 0;
|
|
1364
1314
|
const currentValue = isControlled ? controlledValue : internalValue;
|
|
1365
|
-
const handleChange = (0, import_react10.useCallback)(
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
);
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
if (event.key === "Enter" && onSearch) {
|
|
1377
|
-
onSearch(currentValue);
|
|
1378
|
-
}
|
|
1379
|
-
},
|
|
1380
|
-
[currentValue, onSearch]
|
|
1381
|
-
);
|
|
1382
|
-
return /* @__PURE__ */ import_react10.default.createElement("div", { className: `relative ${className}` }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "absolute inset-y-0 left-3 flex items-center pointer-events-none" }, /* @__PURE__ */ import_react10.default.createElement(Icon, { name: "Search", size: 16, className: "text-slate-400" })), /* @__PURE__ */ import_react10.default.createElement(
|
|
1383
|
-
Input,
|
|
1315
|
+
const handleChange = (0, import_react10.useCallback)((event) => {
|
|
1316
|
+
const v = event.target.value;
|
|
1317
|
+
if (!isControlled) setInternalValue(v);
|
|
1318
|
+
if (onChange) onChange(v);
|
|
1319
|
+
if (onSearch) onSearch(v);
|
|
1320
|
+
}, [isControlled, onChange, onSearch]);
|
|
1321
|
+
const handleKeyDown = (0, import_react10.useCallback)((event) => {
|
|
1322
|
+
if (event.key === "Enter" && onSearch) onSearch(currentValue);
|
|
1323
|
+
}, [currentValue, onSearch]);
|
|
1324
|
+
return /* @__PURE__ */ import_react10.default.createElement("div", { className: ["rdb-searchbar", className].filter(Boolean).join(" ") }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "rdb-searchbar-icon" }, /* @__PURE__ */ import_react10.default.createElement(Icon, { name: "Search", size: 16 })), /* @__PURE__ */ import_react10.default.createElement(
|
|
1325
|
+
"input",
|
|
1384
1326
|
{
|
|
1385
1327
|
type: "search",
|
|
1386
1328
|
value: currentValue,
|
|
1387
1329
|
onChange: handleChange,
|
|
1388
1330
|
onKeyDown: handleKeyDown,
|
|
1389
1331
|
placeholder,
|
|
1390
|
-
className: "
|
|
1332
|
+
className: "rdb-input rdb-searchbar-input",
|
|
1333
|
+
style: { width: "100%" }
|
|
1391
1334
|
}
|
|
1392
1335
|
));
|
|
1393
1336
|
}
|
|
1394
1337
|
SearchBar.propTypes = {
|
|
1395
|
-
/** Nilai pencarian saat ini (controlled). */
|
|
1396
1338
|
value: import_prop_types8.default.string,
|
|
1397
|
-
/** Callback saat nilai input berubah. */
|
|
1398
1339
|
onChange: import_prop_types8.default.func,
|
|
1399
|
-
/** Callback saat pencarian disubmit. */
|
|
1400
1340
|
onSearch: import_prop_types8.default.func,
|
|
1401
|
-
/** Placeholder teks. */
|
|
1402
1341
|
placeholder: import_prop_types8.default.string,
|
|
1403
|
-
/** ClassName tambahan. */
|
|
1404
1342
|
className: import_prop_types8.default.string
|
|
1405
1343
|
};
|
|
1406
1344
|
|
|
@@ -1410,26 +1348,11 @@ var import_prop_types9 = __toESM(require("prop-types"), 1);
|
|
|
1410
1348
|
var CURRENT_YEAR = (/* @__PURE__ */ new Date()).getFullYear();
|
|
1411
1349
|
var MIN_YEAR2 = 2020;
|
|
1412
1350
|
var MAX_YEAR = CURRENT_YEAR + 1;
|
|
1413
|
-
var MONTHS = [
|
|
1414
|
-
"Jan",
|
|
1415
|
-
"Feb",
|
|
1416
|
-
"Mar",
|
|
1417
|
-
"Apr",
|
|
1418
|
-
"May",
|
|
1419
|
-
"Jun",
|
|
1420
|
-
"Jul",
|
|
1421
|
-
"Aug",
|
|
1422
|
-
"Sep",
|
|
1423
|
-
"Oct",
|
|
1424
|
-
"Nov",
|
|
1425
|
-
"Dec"
|
|
1426
|
-
];
|
|
1351
|
+
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
1427
1352
|
function getYears() {
|
|
1428
|
-
const
|
|
1429
|
-
for (let
|
|
1430
|
-
|
|
1431
|
-
}
|
|
1432
|
-
return years;
|
|
1353
|
+
const y = [];
|
|
1354
|
+
for (let i = MAX_YEAR; i >= MIN_YEAR2; i--) y.push(i);
|
|
1355
|
+
return y;
|
|
1433
1356
|
}
|
|
1434
1357
|
function getDaysInMonth(year, month) {
|
|
1435
1358
|
return new Date(year, month, 0).getDate();
|
|
@@ -1437,68 +1360,57 @@ function getDaysInMonth(year, month) {
|
|
|
1437
1360
|
function parseDate(value) {
|
|
1438
1361
|
if (value && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1439
1362
|
const [y, m, d] = value.split("-").map(Number);
|
|
1440
|
-
if (y >= MIN_YEAR2 && y <= MAX_YEAR && m >= 1 && m <= 12)
|
|
1363
|
+
if (y >= MIN_YEAR2 && y <= MAX_YEAR && m >= 1 && m <= 12)
|
|
1441
1364
|
return { year: y, month: m, day: d };
|
|
1442
|
-
}
|
|
1443
1365
|
}
|
|
1444
1366
|
const now = /* @__PURE__ */ new Date();
|
|
1445
1367
|
return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };
|
|
1446
1368
|
}
|
|
1447
|
-
function
|
|
1369
|
+
function fmt({ year, month, day }) {
|
|
1448
1370
|
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
1449
1371
|
}
|
|
1450
1372
|
function DatePicker({ value, onChange, disabled = false, label = "" }) {
|
|
1451
|
-
const
|
|
1373
|
+
const p = parseDate(value);
|
|
1452
1374
|
const years = getYears();
|
|
1453
|
-
const maxDay = getDaysInMonth(
|
|
1454
|
-
const handleChange = (0, import_react11.useCallback)(
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
[parsed, onChange]
|
|
1462
|
-
);
|
|
1463
|
-
const selectClass = "px-2 py-1.5 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
1464
|
-
return /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex flex-col gap-1" }, label ? /* @__PURE__ */ import_react11.default.createElement(Typography, { variant: "caption", className: "font-medium" }, label) : null, /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react11.default.createElement(
|
|
1375
|
+
const maxDay = getDaysInMonth(p.year, p.month);
|
|
1376
|
+
const handleChange = (0, import_react11.useCallback)((field, val) => {
|
|
1377
|
+
const u = { ...p, [field]: Number(val) };
|
|
1378
|
+
const max = getDaysInMonth(u.year, u.month);
|
|
1379
|
+
if (u.day > max) u.day = max;
|
|
1380
|
+
onChange(fmt(u));
|
|
1381
|
+
}, [p, onChange]);
|
|
1382
|
+
return /* @__PURE__ */ import_react11.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, label ? /* @__PURE__ */ import_react11.default.createElement("span", { className: "rdb-caption rdb-label" }, label) : null, /* @__PURE__ */ import_react11.default.createElement("div", { style: { display: "flex", gap: 4, alignItems: "center" } }, /* @__PURE__ */ import_react11.default.createElement(
|
|
1465
1383
|
"select",
|
|
1466
1384
|
{
|
|
1467
|
-
className:
|
|
1468
|
-
|
|
1385
|
+
className: "rdb-select",
|
|
1386
|
+
style: { width: 52 },
|
|
1387
|
+
value: p.day,
|
|
1469
1388
|
onChange: (e) => handleChange("day", e.target.value),
|
|
1470
|
-
disabled
|
|
1471
|
-
"aria-label": `${label} day`
|
|
1389
|
+
disabled
|
|
1472
1390
|
},
|
|
1473
1391
|
Array.from({ length: maxDay }, (_, i) => i + 1).map((d) => /* @__PURE__ */ import_react11.default.createElement("option", { key: d, value: d }, String(d).padStart(2, "0")))
|
|
1474
1392
|
), /* @__PURE__ */ import_react11.default.createElement(
|
|
1475
1393
|
"select",
|
|
1476
1394
|
{
|
|
1477
|
-
className:
|
|
1478
|
-
|
|
1395
|
+
className: "rdb-select",
|
|
1396
|
+
style: { width: 62 },
|
|
1397
|
+
value: p.month,
|
|
1479
1398
|
onChange: (e) => handleChange("month", e.target.value),
|
|
1480
|
-
disabled
|
|
1481
|
-
"aria-label": `${label} month`
|
|
1399
|
+
disabled
|
|
1482
1400
|
},
|
|
1483
1401
|
MONTHS.map((name, idx) => /* @__PURE__ */ import_react11.default.createElement("option", { key: idx + 1, value: idx + 1 }, name))
|
|
1484
1402
|
), /* @__PURE__ */ import_react11.default.createElement(
|
|
1485
1403
|
"select",
|
|
1486
1404
|
{
|
|
1487
|
-
className:
|
|
1488
|
-
|
|
1405
|
+
className: "rdb-select",
|
|
1406
|
+
style: { width: 72 },
|
|
1407
|
+
value: p.year,
|
|
1489
1408
|
onChange: (e) => handleChange("year", e.target.value),
|
|
1490
|
-
disabled
|
|
1491
|
-
"aria-label": `${label} year`
|
|
1409
|
+
disabled
|
|
1492
1410
|
},
|
|
1493
1411
|
years.map((y) => /* @__PURE__ */ import_react11.default.createElement("option", { key: y, value: y }, y))
|
|
1494
1412
|
)));
|
|
1495
1413
|
}
|
|
1496
|
-
DatePicker.propTypes = {
|
|
1497
|
-
value: import_prop_types9.default.string.isRequired,
|
|
1498
|
-
onChange: import_prop_types9.default.func.isRequired,
|
|
1499
|
-
disabled: import_prop_types9.default.bool,
|
|
1500
|
-
label: import_prop_types9.default.string
|
|
1501
|
-
};
|
|
1502
1414
|
function DateRangeFilter({
|
|
1503
1415
|
dateFrom,
|
|
1504
1416
|
dateTo,
|
|
@@ -1508,31 +1420,9 @@ function DateRangeFilter({
|
|
|
1508
1420
|
labelTo = "To",
|
|
1509
1421
|
className = ""
|
|
1510
1422
|
}) {
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
);
|
|
1515
|
-
const handleToChange = (0, import_react11.useCallback)(
|
|
1516
|
-
(newDate) => onRangeChange == null ? void 0 : onRangeChange({ dateFrom, dateTo: newDate }),
|
|
1517
|
-
[dateFrom, onRangeChange]
|
|
1518
|
-
);
|
|
1519
|
-
return /* @__PURE__ */ import_react11.default.createElement("div", { className: `flex flex-wrap items-end gap-3 ${className}` }, /* @__PURE__ */ import_react11.default.createElement(
|
|
1520
|
-
DatePicker,
|
|
1521
|
-
{
|
|
1522
|
-
value: dateFrom,
|
|
1523
|
-
onChange: handleFromChange,
|
|
1524
|
-
disabled,
|
|
1525
|
-
label: labelFrom
|
|
1526
|
-
}
|
|
1527
|
-
), /* @__PURE__ */ import_react11.default.createElement(
|
|
1528
|
-
DatePicker,
|
|
1529
|
-
{
|
|
1530
|
-
value: dateTo,
|
|
1531
|
-
onChange: handleToChange,
|
|
1532
|
-
disabled,
|
|
1533
|
-
label: labelTo
|
|
1534
|
-
}
|
|
1535
|
-
));
|
|
1423
|
+
const handleFrom = (0, import_react11.useCallback)((v) => onRangeChange == null ? void 0 : onRangeChange({ dateFrom: v, dateTo }), [dateTo, onRangeChange]);
|
|
1424
|
+
const handleTo = (0, import_react11.useCallback)((v) => onRangeChange == null ? void 0 : onRangeChange({ dateFrom, dateTo: v }), [dateFrom, onRangeChange]);
|
|
1425
|
+
return /* @__PURE__ */ import_react11.default.createElement("div", { className: ["rdb-daterange", className].filter(Boolean).join(" ") }, /* @__PURE__ */ import_react11.default.createElement(DatePicker, { value: dateFrom, onChange: handleFrom, disabled, label: labelFrom }), /* @__PURE__ */ import_react11.default.createElement(DatePicker, { value: dateTo, onChange: handleTo, disabled, label: labelTo }));
|
|
1536
1426
|
}
|
|
1537
1427
|
DateRangeFilter.propTypes = {
|
|
1538
1428
|
dateFrom: import_prop_types9.default.string.isRequired,
|
|
@@ -1547,22 +1437,13 @@ DateRangeFilter.propTypes = {
|
|
|
1547
1437
|
// src/presentation/molecules/ChartHeader.jsx
|
|
1548
1438
|
var import_react12 = __toESM(require("react"), 1);
|
|
1549
1439
|
var import_prop_types10 = __toESM(require("prop-types"), 1);
|
|
1550
|
-
function ChartHeader({
|
|
1551
|
-
title,
|
|
1552
|
-
icon = "BarChart3",
|
|
1553
|
-
actions = null,
|
|
1554
|
-
className = ""
|
|
1555
|
-
}) {
|
|
1556
|
-
return /* @__PURE__ */ import_react12.default.createElement("div", { className: `flex items-center justify-between mb-2 ${className}` }, /* @__PURE__ */ import_react12.default.createElement(Typography, { variant: "h3", className: "flex items-center gap-2" }, /* @__PURE__ */ import_react12.default.createElement(Icon, { name: icon, size: 18 }), title), actions);
|
|
1440
|
+
function ChartHeader({ title, icon = "BarChart3", actions = null, className = "" }) {
|
|
1441
|
+
return /* @__PURE__ */ import_react12.default.createElement("div", { className: ["rdb-chart-title-wrap", className].filter(Boolean).join(" ") }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "rdb-chart-title" }, /* @__PURE__ */ import_react12.default.createElement(Icon, { name: icon, size: 18 }), title), actions);
|
|
1557
1442
|
}
|
|
1558
1443
|
ChartHeader.propTypes = {
|
|
1559
|
-
/** Judul grafik. */
|
|
1560
1444
|
title: import_prop_types10.default.string.isRequired,
|
|
1561
|
-
/** Nama ikon header. */
|
|
1562
1445
|
icon: import_prop_types10.default.string,
|
|
1563
|
-
/** Elemen aksi tambahan di sisi kanan. */
|
|
1564
1446
|
actions: import_prop_types10.default.node,
|
|
1565
|
-
/** ClassName tambahan. */
|
|
1566
1447
|
className: import_prop_types10.default.string
|
|
1567
1448
|
};
|
|
1568
1449
|
|
|
@@ -1587,28 +1468,24 @@ function DataTable({
|
|
|
1587
1468
|
const [currentPage, setCurrentPage] = (0, import_react13.useState)(1);
|
|
1588
1469
|
const filteredData = (0, import_react13.useMemo)(() => {
|
|
1589
1470
|
if (!searchQuery.trim()) return data;
|
|
1590
|
-
const
|
|
1471
|
+
const q = searchQuery.toLowerCase();
|
|
1591
1472
|
return data.filter(
|
|
1592
1473
|
(row) => columns.some((col) => {
|
|
1593
|
-
const
|
|
1594
|
-
return
|
|
1474
|
+
const v = row[col.accessor];
|
|
1475
|
+
return v != null && String(v).toLowerCase().includes(q);
|
|
1595
1476
|
})
|
|
1596
1477
|
);
|
|
1597
1478
|
}, [data, searchQuery, columns]);
|
|
1598
1479
|
const sortedData = (0, import_react13.useMemo)(() => {
|
|
1599
1480
|
if (!sortField) return filteredData;
|
|
1600
|
-
|
|
1601
|
-
const
|
|
1602
|
-
const
|
|
1603
|
-
if (typeof
|
|
1604
|
-
return sortDirection === "asc" ?
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
numeric: true
|
|
1608
|
-
});
|
|
1609
|
-
return sortDirection === "asc" ? comparison : -comparison;
|
|
1481
|
+
return [...filteredData].sort((a, b) => {
|
|
1482
|
+
const aV = a[sortField] ?? "";
|
|
1483
|
+
const bV = b[sortField] ?? "";
|
|
1484
|
+
if (typeof aV === "number" && typeof bV === "number")
|
|
1485
|
+
return sortDirection === "asc" ? aV - bV : bV - aV;
|
|
1486
|
+
const cmp = String(aV).localeCompare(String(bV), void 0, { numeric: true });
|
|
1487
|
+
return sortDirection === "asc" ? cmp : -cmp;
|
|
1610
1488
|
});
|
|
1611
|
-
return sorted;
|
|
1612
1489
|
}, [filteredData, sortField, sortDirection]);
|
|
1613
1490
|
const totalPages = Math.max(1, Math.ceil(sortedData.length / pageSize));
|
|
1614
1491
|
const safePage = Math.min(currentPage, totalPages);
|
|
@@ -1616,90 +1493,29 @@ function DataTable({
|
|
|
1616
1493
|
const start = (safePage - 1) * pageSize;
|
|
1617
1494
|
return sortedData.slice(start, start + pageSize);
|
|
1618
1495
|
}, [sortedData, safePage, pageSize]);
|
|
1619
|
-
const handleSearch = (0, import_react13.useCallback)((
|
|
1620
|
-
setSearchQuery(
|
|
1496
|
+
const handleSearch = (0, import_react13.useCallback)((v) => {
|
|
1497
|
+
setSearchQuery(v);
|
|
1621
1498
|
setCurrentPage(1);
|
|
1622
1499
|
}, []);
|
|
1623
|
-
const handleSort = (0, import_react13.useCallback)(
|
|
1624
|
-
(
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
setSortField(accessor);
|
|
1630
|
-
setSortDirection("asc");
|
|
1631
|
-
}
|
|
1632
|
-
setCurrentPage(1);
|
|
1633
|
-
},
|
|
1634
|
-
[sortable, sortField]
|
|
1635
|
-
);
|
|
1636
|
-
const handlePrevPage = (0, import_react13.useCallback)(() => {
|
|
1637
|
-
setCurrentPage((prev) => Math.max(1, prev - 1));
|
|
1638
|
-
}, []);
|
|
1639
|
-
const handleNextPage = (0, import_react13.useCallback)(() => {
|
|
1640
|
-
setCurrentPage((prev) => Math.min(totalPages, prev + 1));
|
|
1641
|
-
}, [totalPages]);
|
|
1642
|
-
return /* @__PURE__ */ import_react13.default.createElement("div", { className: `space-y-3 ${className}` }, searchable ? /* @__PURE__ */ import_react13.default.createElement(
|
|
1643
|
-
SearchBar,
|
|
1644
|
-
{
|
|
1645
|
-
value: searchQuery,
|
|
1646
|
-
onSearch: handleSearch,
|
|
1647
|
-
placeholder: searchPlaceholder,
|
|
1648
|
-
className: "max-w-sm"
|
|
1500
|
+
const handleSort = (0, import_react13.useCallback)((acc) => {
|
|
1501
|
+
if (!sortable) return;
|
|
1502
|
+
if (sortField === acc) setSortDirection((p) => p === "asc" ? "desc" : "asc");
|
|
1503
|
+
else {
|
|
1504
|
+
setSortField(acc);
|
|
1505
|
+
setSortDirection("asc");
|
|
1649
1506
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
sortable ? "cursor-pointer select-none hover:bg-slate-200 dark:hover:bg-slate-700 transition-colors" : ""
|
|
1657
|
-
].join(" "),
|
|
1658
|
-
onClick: () => handleSort(column.accessor)
|
|
1659
|
-
},
|
|
1660
|
-
/* @__PURE__ */ import_react13.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react13.default.createElement("span", null, labels[column.label] || column.label), sortable && sortField === column.accessor ? /* @__PURE__ */ import_react13.default.createElement(
|
|
1661
|
-
Icon,
|
|
1662
|
-
{
|
|
1663
|
-
name: sortDirection === "asc" ? "ArrowUp" : "ArrowDown",
|
|
1664
|
-
size: 14,
|
|
1665
|
-
className: "text-blue-500"
|
|
1666
|
-
}
|
|
1667
|
-
) : null)
|
|
1668
|
-
)))), /* @__PURE__ */ import_react13.default.createElement("tbody", null, paginatedData.map((row) => /* @__PURE__ */ import_react13.default.createElement(
|
|
1669
|
-
"tr",
|
|
1670
|
-
{
|
|
1671
|
-
key: row.id,
|
|
1672
|
-
className: "border-b border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800/50"
|
|
1673
|
-
},
|
|
1674
|
-
columns.map((column) => /* @__PURE__ */ import_react13.default.createElement("td", { key: `${row.id}-${column.id}`, className: "p-3" }, renderCell(column, row, dateLocale)))
|
|
1675
|
-
)), paginatedData.length === 0 ? /* @__PURE__ */ import_react13.default.createElement("tr", null, /* @__PURE__ */ import_react13.default.createElement(
|
|
1676
|
-
"td",
|
|
1677
|
-
{
|
|
1678
|
-
className: "p-6 text-center text-slate-500",
|
|
1679
|
-
colSpan: columns.length
|
|
1680
|
-
},
|
|
1681
|
-
emptyLabel
|
|
1682
|
-
)) : null))), sortedData.length > pageSize ? /* @__PURE__ */ import_react13.default.createElement("div", { className: "flex items-center justify-between px-1" }, /* @__PURE__ */ import_react13.default.createElement("span", { className: "text-xs text-slate-500" }, (safePage - 1) * pageSize + 1, "\u2013", Math.min(safePage * pageSize, sortedData.length), " of", " ", sortedData.length), /* @__PURE__ */ import_react13.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react13.default.createElement(
|
|
1683
|
-
"button",
|
|
1684
|
-
{
|
|
1685
|
-
type: "button",
|
|
1686
|
-
className: "p-1.5 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800 disabled:opacity-40 disabled:cursor-not-allowed transition-colors",
|
|
1687
|
-
onClick: handlePrevPage,
|
|
1688
|
-
disabled: safePage <= 1,
|
|
1689
|
-
"aria-label": "Previous page"
|
|
1690
|
-
},
|
|
1691
|
-
/* @__PURE__ */ import_react13.default.createElement(Icon, { name: "ChevronLeft", size: 16 })
|
|
1692
|
-
), /* @__PURE__ */ import_react13.default.createElement("span", { className: "text-xs text-slate-600 dark:text-slate-300 min-w-[60px] text-center" }, safePage, " / ", totalPages), /* @__PURE__ */ import_react13.default.createElement(
|
|
1693
|
-
"button",
|
|
1507
|
+
setCurrentPage(1);
|
|
1508
|
+
}, [sortable, sortField]);
|
|
1509
|
+
const handlePrev = (0, import_react13.useCallback)(() => setCurrentPage((p) => Math.max(1, p - 1)), []);
|
|
1510
|
+
const handleNext = (0, import_react13.useCallback)(() => setCurrentPage((p) => Math.min(totalPages, p + 1)), [totalPages]);
|
|
1511
|
+
return /* @__PURE__ */ import_react13.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, className }, searchable ? /* @__PURE__ */ import_react13.default.createElement(SearchBar, { value: searchQuery, onSearch: handleSearch, placeholder: searchPlaceholder }) : null, /* @__PURE__ */ import_react13.default.createElement("div", { className: "rdb-table-wrapper" }, /* @__PURE__ */ import_react13.default.createElement("table", { className: "rdb-table" }, /* @__PURE__ */ import_react13.default.createElement("thead", null, /* @__PURE__ */ import_react13.default.createElement("tr", null, columns.map((col) => /* @__PURE__ */ import_react13.default.createElement("th", { key: col.id, onClick: () => handleSort(col.accessor) }, /* @__PURE__ */ import_react13.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 4 } }, /* @__PURE__ */ import_react13.default.createElement("span", null, labels[col.label] || col.label), sortable && sortField === col.accessor ? /* @__PURE__ */ import_react13.default.createElement(
|
|
1512
|
+
Icon,
|
|
1694
1513
|
{
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
},
|
|
1701
|
-
/* @__PURE__ */ import_react13.default.createElement(Icon, { name: "ChevronRight", size: 16 })
|
|
1702
|
-
))) : null);
|
|
1514
|
+
name: sortDirection === "asc" ? "ArrowUp" : "ArrowDown",
|
|
1515
|
+
size: 13,
|
|
1516
|
+
style: { color: "var(--rdb-blue-500)" }
|
|
1517
|
+
}
|
|
1518
|
+
) : null))))), /* @__PURE__ */ import_react13.default.createElement("tbody", null, paginatedData.map((row) => /* @__PURE__ */ import_react13.default.createElement("tr", { key: row.id }, columns.map((col) => /* @__PURE__ */ import_react13.default.createElement("td", { key: `${row.id}-${col.id}` }, renderCell(col, row, dateLocale))))), paginatedData.length === 0 ? /* @__PURE__ */ import_react13.default.createElement("tr", null, /* @__PURE__ */ import_react13.default.createElement("td", { className: "rdb-table-empty", colSpan: columns.length }, emptyLabel)) : null))), sortedData.length > pageSize ? /* @__PURE__ */ import_react13.default.createElement("div", { className: "rdb-pagination" }, /* @__PURE__ */ import_react13.default.createElement("span", { className: "rdb-pagination-info" }, (safePage - 1) * pageSize + 1, "\u2013", Math.min(safePage * pageSize, sortedData.length), " of ", sortedData.length), /* @__PURE__ */ import_react13.default.createElement("div", { className: "rdb-pagination-controls" }, /* @__PURE__ */ import_react13.default.createElement("button", { type: "button", className: "rdb-page-btn", onClick: handlePrev, disabled: safePage <= 1, "aria-label": "Previous" }, /* @__PURE__ */ import_react13.default.createElement(Icon, { name: "ChevronLeft", size: 16 })), /* @__PURE__ */ import_react13.default.createElement("span", { className: "rdb-pagination-page" }, safePage, " / ", totalPages), /* @__PURE__ */ import_react13.default.createElement("button", { type: "button", className: "rdb-page-btn", onClick: handleNext, disabled: safePage >= totalPages, "aria-label": "Next" }, /* @__PURE__ */ import_react13.default.createElement(Icon, { name: "ChevronRight", size: 16 })))) : null);
|
|
1703
1519
|
}
|
|
1704
1520
|
function renderCell(column, row, dateLocale) {
|
|
1705
1521
|
const value = row[column.accessor];
|
|
@@ -1712,33 +1528,21 @@ function renderCell(column, row, dateLocale) {
|
|
|
1712
1528
|
return value || "-";
|
|
1713
1529
|
}
|
|
1714
1530
|
DataTable.propTypes = {
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
import_prop_types11.default.
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
statusAccessor: import_prop_types11.default.string
|
|
1723
|
-
})
|
|
1724
|
-
).isRequired,
|
|
1725
|
-
/** Array data baris. */
|
|
1531
|
+
columns: import_prop_types11.default.arrayOf(import_prop_types11.default.shape({
|
|
1532
|
+
id: import_prop_types11.default.string.isRequired,
|
|
1533
|
+
label: import_prop_types11.default.string.isRequired,
|
|
1534
|
+
accessor: import_prop_types11.default.string.isRequired,
|
|
1535
|
+
type: import_prop_types11.default.oneOf(["date", "currency", "statusBadge"]),
|
|
1536
|
+
statusAccessor: import_prop_types11.default.string
|
|
1537
|
+
})).isRequired,
|
|
1726
1538
|
data: import_prop_types11.default.arrayOf(import_prop_types11.default.object),
|
|
1727
|
-
/** Objek label i18n. */
|
|
1728
1539
|
labels: import_prop_types11.default.object,
|
|
1729
|
-
/** Locale untuk format tanggal. */
|
|
1730
1540
|
dateLocale: import_prop_types11.default.string,
|
|
1731
|
-
/** Apakah tabel mendukung pencarian. */
|
|
1732
1541
|
searchable: import_prop_types11.default.bool,
|
|
1733
|
-
/** Apakah tabel mendukung pengurutan. */
|
|
1734
1542
|
sortable: import_prop_types11.default.bool,
|
|
1735
|
-
/** Jumlah baris per halaman. */
|
|
1736
1543
|
pageSize: import_prop_types11.default.number,
|
|
1737
|
-
/** Label saat data kosong. */
|
|
1738
1544
|
emptyLabel: import_prop_types11.default.string,
|
|
1739
|
-
/** Placeholder pencarian. */
|
|
1740
1545
|
searchPlaceholder: import_prop_types11.default.string,
|
|
1741
|
-
/** ClassName tambahan. */
|
|
1742
1546
|
className: import_prop_types11.default.string
|
|
1743
1547
|
};
|
|
1744
1548
|
|
|
@@ -1892,44 +1696,38 @@ var AUDIENCE_OPTIONS = ["", "domestic", "foreign"];
|
|
|
1892
1696
|
var DAY_PRESETS = [7, 30, 90, 0];
|
|
1893
1697
|
var SORT_BY_OPTIONS = ["bookings", "revenue"];
|
|
1894
1698
|
var SORT_DIR_OPTIONS = ["desc", "asc"];
|
|
1895
|
-
function FilterPanel({
|
|
1896
|
-
|
|
1897
|
-
labels,
|
|
1898
|
-
onFilterChange,
|
|
1899
|
-
onResetFilters,
|
|
1900
|
-
className = ""
|
|
1901
|
-
}) {
|
|
1902
|
-
return /* @__PURE__ */ import_react15.default.createElement("div", { className: `flex flex-col gap-2 ${className}` }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "grid grid-cols-1 lg:grid-cols-6 gap-2" }, /* @__PURE__ */ import_react15.default.createElement(
|
|
1699
|
+
function FilterPanel({ filters, labels, onFilterChange, onResetFilters, className = "" }) {
|
|
1700
|
+
return /* @__PURE__ */ import_react15.default.createElement("div", { className: ["rdb-filter-panel", className].filter(Boolean).join(" ") }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "rdb-filter-row" }, /* @__PURE__ */ import_react15.default.createElement(
|
|
1903
1701
|
"select",
|
|
1904
1702
|
{
|
|
1905
|
-
className: "
|
|
1703
|
+
className: "rdb-select",
|
|
1906
1704
|
value: filters.statusScope,
|
|
1907
|
-
onChange: (
|
|
1705
|
+
onChange: (e) => onFilterChange("statusScope", e.target.value)
|
|
1908
1706
|
},
|
|
1909
|
-
STATUS_OPTIONS.map((
|
|
1910
|
-
), /* @__PURE__ */ import_react15.default.createElement("label", { className: "
|
|
1911
|
-
|
|
1707
|
+
STATUS_OPTIONS.map((s) => /* @__PURE__ */ import_react15.default.createElement("option", { key: s, value: s }, s === "confirmed" ? labels.confirmedOnly : s === "pending" ? labels.pendingOnly : labels.allStatus))
|
|
1708
|
+
), /* @__PURE__ */ import_react15.default.createElement("label", { className: "rdb-filter-overlay-label" }, /* @__PURE__ */ import_react15.default.createElement(
|
|
1709
|
+
"input",
|
|
1912
1710
|
{
|
|
1913
1711
|
type: "checkbox",
|
|
1914
1712
|
checked: filters.includePendingOverlay,
|
|
1915
|
-
onChange: (
|
|
1713
|
+
onChange: (e) => onFilterChange("includePendingOverlay", e.target.checked)
|
|
1916
1714
|
}
|
|
1917
|
-
), /* @__PURE__ */ import_react15.default.createElement(
|
|
1715
|
+
), /* @__PURE__ */ import_react15.default.createElement("span", { className: "rdb-body" }, labels.showPendingOverlay)), /* @__PURE__ */ import_react15.default.createElement(
|
|
1918
1716
|
"select",
|
|
1919
1717
|
{
|
|
1920
|
-
className: "
|
|
1718
|
+
className: "rdb-select",
|
|
1921
1719
|
value: filters.audience,
|
|
1922
|
-
onChange: (
|
|
1720
|
+
onChange: (e) => onFilterChange("audience", e.target.value)
|
|
1923
1721
|
},
|
|
1924
|
-
AUDIENCE_OPTIONS.map((
|
|
1722
|
+
AUDIENCE_OPTIONS.map((a) => /* @__PURE__ */ import_react15.default.createElement("option", { key: a || "all", value: a }, a === "domestic" ? labels.audienceDomestic : a === "foreign" ? labels.audienceForeign : labels.allAudience))
|
|
1925
1723
|
), /* @__PURE__ */ import_react15.default.createElement(
|
|
1926
1724
|
"select",
|
|
1927
1725
|
{
|
|
1928
|
-
className: "
|
|
1726
|
+
className: "rdb-select",
|
|
1929
1727
|
value: String(filters.daysPreset),
|
|
1930
|
-
onChange: (
|
|
1728
|
+
onChange: (e) => onFilterChange("daysPreset", Number(e.target.value))
|
|
1931
1729
|
},
|
|
1932
|
-
DAY_PRESETS.map((
|
|
1730
|
+
DAY_PRESETS.map((d) => /* @__PURE__ */ import_react15.default.createElement("option", { key: String(d), value: String(d) }, d === 0 ? labels.customDate : labels.dayLabel(d)))
|
|
1933
1731
|
), /* @__PURE__ */ import_react15.default.createElement(
|
|
1934
1732
|
DateRangeFilter,
|
|
1935
1733
|
{
|
|
@@ -1940,160 +1738,95 @@ function FilterPanel({
|
|
|
1940
1738
|
if (dateTo !== filters.dateTo) onFilterChange("dateTo", dateTo);
|
|
1941
1739
|
},
|
|
1942
1740
|
disabled: filters.daysPreset !== 0,
|
|
1943
|
-
className: "
|
|
1741
|
+
className: "rdb-filter-date"
|
|
1944
1742
|
}
|
|
1945
|
-
)), /* @__PURE__ */ import_react15.default.createElement("div", { className: "
|
|
1743
|
+
)), /* @__PURE__ */ import_react15.default.createElement("div", { className: "rdb-filter-actions" }, /* @__PURE__ */ import_react15.default.createElement(Button, { variant: "secondary", size: "sm", onClick: onResetFilters }, labels.reset), /* @__PURE__ */ import_react15.default.createElement("div", { className: "rdb-filter-sort" }, /* @__PURE__ */ import_react15.default.createElement("span", { className: "rdb-caption" }, labels.topSort, ":"), /* @__PURE__ */ import_react15.default.createElement(
|
|
1946
1744
|
"select",
|
|
1947
1745
|
{
|
|
1948
|
-
className: "
|
|
1746
|
+
className: "rdb-select",
|
|
1747
|
+
style: { width: "auto" },
|
|
1949
1748
|
value: filters.sortPkgBy,
|
|
1950
|
-
onChange: (
|
|
1749
|
+
onChange: (e) => onFilterChange("sortPkgBy", e.target.value)
|
|
1951
1750
|
},
|
|
1952
|
-
SORT_BY_OPTIONS.map((
|
|
1751
|
+
SORT_BY_OPTIONS.map((s) => /* @__PURE__ */ import_react15.default.createElement("option", { key: s, value: s }, s === "bookings" ? labels.sortBookings : labels.sortRevenue))
|
|
1953
1752
|
), /* @__PURE__ */ import_react15.default.createElement(
|
|
1954
1753
|
"select",
|
|
1955
1754
|
{
|
|
1956
|
-
className: "
|
|
1755
|
+
className: "rdb-select",
|
|
1756
|
+
style: { width: "auto" },
|
|
1957
1757
|
value: filters.sortPkgDir,
|
|
1958
|
-
onChange: (
|
|
1758
|
+
onChange: (e) => onFilterChange("sortPkgDir", e.target.value)
|
|
1959
1759
|
},
|
|
1960
|
-
SORT_DIR_OPTIONS.map((
|
|
1760
|
+
SORT_DIR_OPTIONS.map((d) => /* @__PURE__ */ import_react15.default.createElement("option", { key: d, value: d }, d === "desc" ? labels.sortDesc : labels.sortAsc))
|
|
1961
1761
|
))));
|
|
1962
1762
|
}
|
|
1963
1763
|
FilterPanel.propTypes = {
|
|
1964
|
-
|
|
1965
|
-
filters: import_prop_types13.default.shape({
|
|
1966
|
-
statusScope: import_prop_types13.default.string,
|
|
1967
|
-
includePendingOverlay: import_prop_types13.default.bool,
|
|
1968
|
-
audience: import_prop_types13.default.string,
|
|
1969
|
-
daysPreset: import_prop_types13.default.number,
|
|
1970
|
-
dateFrom: import_prop_types13.default.string,
|
|
1971
|
-
dateTo: import_prop_types13.default.string,
|
|
1972
|
-
sortPkgBy: import_prop_types13.default.string,
|
|
1973
|
-
sortPkgDir: import_prop_types13.default.string
|
|
1974
|
-
}).isRequired,
|
|
1975
|
-
/** Objek label i18n. */
|
|
1764
|
+
filters: import_prop_types13.default.object.isRequired,
|
|
1976
1765
|
labels: import_prop_types13.default.object.isRequired,
|
|
1977
|
-
/** Callback saat filter berubah (field, value). */
|
|
1978
1766
|
onFilterChange: import_prop_types13.default.func.isRequired,
|
|
1979
|
-
/** Callback saat filter di-reset. */
|
|
1980
1767
|
onResetFilters: import_prop_types13.default.func.isRequired,
|
|
1981
|
-
/** ClassName tambahan. */
|
|
1982
1768
|
className: import_prop_types13.default.string
|
|
1983
1769
|
};
|
|
1984
1770
|
|
|
1985
1771
|
// src/presentation/organisms/SidebarNavigation.jsx
|
|
1986
1772
|
var import_react16 = __toESM(require("react"), 1);
|
|
1987
1773
|
var import_prop_types14 = __toESM(require("prop-types"), 1);
|
|
1988
|
-
function SidebarNavigation({
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
const isActive = item.id === activeItem;
|
|
2003
|
-
return /* @__PURE__ */ import_react16.default.createElement(
|
|
2004
|
-
"button",
|
|
1774
|
+
function SidebarNavigation({ items = [], activeItem = "", onItemClick, logo = null, className = "" }) {
|
|
1775
|
+
return /* @__PURE__ */ import_react16.default.createElement("aside", { className: ["rdb-sidebar", className].filter(Boolean).join(" ") }, logo ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "rdb-sidebar-logo" }, logo) : null, /* @__PURE__ */ import_react16.default.createElement("nav", { className: "rdb-sidebar-nav", "aria-label": "Sidebar navigation" }, items.map((item) => {
|
|
1776
|
+
const isActive = item.id === activeItem;
|
|
1777
|
+
return /* @__PURE__ */ import_react16.default.createElement(
|
|
1778
|
+
"button",
|
|
1779
|
+
{
|
|
1780
|
+
key: item.id,
|
|
1781
|
+
type: "button",
|
|
1782
|
+
onClick: () => onItemClick == null ? void 0 : onItemClick(item.id),
|
|
1783
|
+
"aria-current": isActive ? "page" : void 0,
|
|
1784
|
+
className: ["rdb-nav-item", isActive ? "rdb-nav-item-active" : ""].filter(Boolean).join(" ")
|
|
1785
|
+
},
|
|
1786
|
+
item.icon ? /* @__PURE__ */ import_react16.default.createElement(
|
|
1787
|
+
Icon,
|
|
2005
1788
|
{
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
},
|
|
2016
|
-
item.icon ? /* @__PURE__ */ import_react16.default.createElement(
|
|
2017
|
-
Icon,
|
|
2018
|
-
{
|
|
2019
|
-
name: item.icon,
|
|
2020
|
-
size: 18,
|
|
2021
|
-
className: isActive ? "text-blue-600 dark:text-blue-400" : "text-slate-400 dark:text-slate-500"
|
|
2022
|
-
}
|
|
2023
|
-
) : null,
|
|
2024
|
-
/* @__PURE__ */ import_react16.default.createElement("span", null, item.label),
|
|
2025
|
-
isActive ? /* @__PURE__ */ import_react16.default.createElement("span", { className: "ml-auto w-1.5 h-1.5 rounded-full bg-blue-500" }) : null
|
|
2026
|
-
);
|
|
2027
|
-
}))
|
|
2028
|
-
);
|
|
1789
|
+
name: item.icon,
|
|
1790
|
+
size: 18,
|
|
1791
|
+
style: { color: isActive ? "var(--rdb-blue-600)" : "var(--rdb-text-subtle)" }
|
|
1792
|
+
}
|
|
1793
|
+
) : null,
|
|
1794
|
+
/* @__PURE__ */ import_react16.default.createElement("span", null, item.label),
|
|
1795
|
+
isActive ? /* @__PURE__ */ import_react16.default.createElement("span", { className: "rdb-nav-dot" }) : null
|
|
1796
|
+
);
|
|
1797
|
+
})));
|
|
2029
1798
|
}
|
|
2030
1799
|
SidebarNavigation.propTypes = {
|
|
2031
|
-
|
|
2032
|
-
items: import_prop_types14.default.arrayOf(
|
|
2033
|
-
import_prop_types14.default.shape({
|
|
2034
|
-
id: import_prop_types14.default.string.isRequired,
|
|
2035
|
-
label: import_prop_types14.default.string.isRequired,
|
|
2036
|
-
icon: import_prop_types14.default.string
|
|
2037
|
-
})
|
|
2038
|
-
),
|
|
2039
|
-
/** ID item yang sedang aktif. */
|
|
1800
|
+
items: import_prop_types14.default.arrayOf(import_prop_types14.default.shape({ id: import_prop_types14.default.string.isRequired, label: import_prop_types14.default.string.isRequired, icon: import_prop_types14.default.string })),
|
|
2040
1801
|
activeItem: import_prop_types14.default.string,
|
|
2041
|
-
/** Callback saat item diklik, menerima id item sebagai argumen. */
|
|
2042
1802
|
onItemClick: import_prop_types14.default.func,
|
|
2043
|
-
/** Konten logo/brand di atas sidebar. */
|
|
2044
1803
|
logo: import_prop_types14.default.node,
|
|
2045
|
-
/** ClassName tambahan. */
|
|
2046
1804
|
className: import_prop_types14.default.string
|
|
2047
1805
|
};
|
|
2048
1806
|
|
|
2049
1807
|
// src/presentation/organisms/TopbarHeader.jsx
|
|
2050
1808
|
var import_react17 = __toESM(require("react"), 1);
|
|
2051
1809
|
var import_prop_types15 = __toESM(require("prop-types"), 1);
|
|
2052
|
-
function TopbarHeader({
|
|
2053
|
-
title
|
|
2054
|
-
actions = null,
|
|
2055
|
-
leading = null,
|
|
2056
|
-
className = ""
|
|
2057
|
-
}) {
|
|
2058
|
-
return /* @__PURE__ */ import_react17.default.createElement(
|
|
2059
|
-
"header",
|
|
2060
|
-
{
|
|
2061
|
-
className: `sticky top-0 z-30 flex items-center justify-between h-14 px-4 bg-white/80 dark:bg-slate-900/80 backdrop-blur-md border-b border-slate-200 dark:border-slate-800 ${className}`
|
|
2062
|
-
},
|
|
2063
|
-
/* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-3" }, leading, title ? /* @__PURE__ */ import_react17.default.createElement(Typography, { variant: "h2", className: "truncate" }, title) : null),
|
|
2064
|
-
actions ? /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2" }, actions) : null
|
|
2065
|
-
);
|
|
1810
|
+
function TopbarHeader({ title = "", actions = null, leading = null, className = "" }) {
|
|
1811
|
+
return /* @__PURE__ */ import_react17.default.createElement("header", { className: ["rdb-topbar", className].filter(Boolean).join(" ") }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "rdb-topbar-left" }, leading, title ? /* @__PURE__ */ import_react17.default.createElement("span", { className: "rdb-h2", style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, title) : null), actions ? /* @__PURE__ */ import_react17.default.createElement("div", { className: "rdb-topbar-right" }, actions) : null);
|
|
2066
1812
|
}
|
|
2067
1813
|
TopbarHeader.propTypes = {
|
|
2068
|
-
/** Judul halaman yang ditampilkan. */
|
|
2069
1814
|
title: import_prop_types15.default.string,
|
|
2070
|
-
/** Elemen aksi di sisi kanan (tombol, avatar, dsb.). */
|
|
2071
1815
|
actions: import_prop_types15.default.node,
|
|
2072
|
-
/** Elemen di sisi kiri sebelum judul (misal: hamburger menu). */
|
|
2073
1816
|
leading: import_prop_types15.default.node,
|
|
2074
|
-
/** ClassName tambahan. */
|
|
2075
1817
|
className: import_prop_types15.default.string
|
|
2076
1818
|
};
|
|
2077
1819
|
|
|
2078
1820
|
// src/presentation/templates/DashboardLayout.jsx
|
|
2079
1821
|
var import_react18 = __toESM(require("react"), 1);
|
|
2080
1822
|
var import_prop_types16 = __toESM(require("prop-types"), 1);
|
|
2081
|
-
function DashboardLayout({
|
|
2082
|
-
header
|
|
2083
|
-
sidebar = null,
|
|
2084
|
-
children,
|
|
2085
|
-
className = ""
|
|
2086
|
-
}) {
|
|
2087
|
-
return /* @__PURE__ */ import_react18.default.createElement("div", { className: `min-h-screen bg-slate-50 dark:bg-slate-900 ${className}` }, header, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex" }, sidebar, /* @__PURE__ */ import_react18.default.createElement("main", { className: "flex-1 container mx-auto px-4 py-3 space-y-4" }, children)));
|
|
1823
|
+
function DashboardLayout({ header = null, sidebar = null, children, className = "" }) {
|
|
1824
|
+
return /* @__PURE__ */ import_react18.default.createElement("div", { className: ["rdb-layout", className].filter(Boolean).join(" ") }, header, /* @__PURE__ */ import_react18.default.createElement("div", { className: "rdb-layout-body" }, sidebar, /* @__PURE__ */ import_react18.default.createElement("main", { className: "rdb-layout-main" }, children)));
|
|
2088
1825
|
}
|
|
2089
1826
|
DashboardLayout.propTypes = {
|
|
2090
|
-
/** Konten header atas. */
|
|
2091
1827
|
header: import_prop_types16.default.node,
|
|
2092
|
-
/** Konten sidebar. */
|
|
2093
1828
|
sidebar: import_prop_types16.default.node,
|
|
2094
|
-
/** Konten utama dashboard. */
|
|
2095
1829
|
children: import_prop_types16.default.node.isRequired,
|
|
2096
|
-
/** ClassName tambahan. */
|
|
2097
1830
|
className: import_prop_types16.default.string
|
|
2098
1831
|
};
|
|
2099
1832
|
|
|
@@ -2113,16 +1846,7 @@ function ReusableDashboardView({
|
|
|
2113
1846
|
dateLocale,
|
|
2114
1847
|
liveUpdateEnabled
|
|
2115
1848
|
}) {
|
|
2116
|
-
return /* @__PURE__ */ import_react19.default.createElement("div", { className: "
|
|
2117
|
-
Button,
|
|
2118
|
-
{
|
|
2119
|
-
variant: "secondary",
|
|
2120
|
-
size: "sm",
|
|
2121
|
-
onClick: () => onRefresh(),
|
|
2122
|
-
title: labels.refresh
|
|
2123
|
-
},
|
|
2124
|
-
/* @__PURE__ */ import_react19.default.createElement(Icon, { name: "RotateCcw", size: 16 })
|
|
2125
|
-
)), error ? /* @__PURE__ */ import_react19.default.createElement("div", { className: "rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex items-center justify-between gap-3" }, /* @__PURE__ */ import_react19.default.createElement("span", null, error), /* @__PURE__ */ import_react19.default.createElement(Button, { variant: "secondary", size: "sm", onClick: () => onRefresh() }, labels.retry))) : null, /* @__PURE__ */ import_react19.default.createElement(
|
|
1849
|
+
return /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-view" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-view-container" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-header-panel" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-header-top" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-header-title-group" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-h1" }, labels.title), liveUpdateEnabled ? /* @__PURE__ */ import_react19.default.createElement(Badge, { status: "success" }, labels.liveUpdate) : null), /* @__PURE__ */ import_react19.default.createElement(Button, { variant: "secondary", size: "sm", onClick: () => onRefresh(), title: labels.refresh }, /* @__PURE__ */ import_react19.default.createElement(Icon, { name: "RotateCcw", size: 16 }))), error ? /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-error-banner" }, /* @__PURE__ */ import_react19.default.createElement("span", null, error), /* @__PURE__ */ import_react19.default.createElement(Button, { variant: "secondary", size: "sm", onClick: () => onRefresh() }, labels.retry)) : null, /* @__PURE__ */ import_react19.default.createElement(
|
|
2126
1850
|
FilterPanel,
|
|
2127
1851
|
{
|
|
2128
1852
|
filters,
|
|
@@ -2130,7 +1854,7 @@ function ReusableDashboardView({
|
|
|
2130
1854
|
onFilterChange,
|
|
2131
1855
|
onResetFilters
|
|
2132
1856
|
}
|
|
2133
|
-
))
|
|
1857
|
+
)), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-stats-grid" }, loading ? Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ import_react19.default.createElement(SkeletonLoader, { key: i, style: { height: 112 } })) : config.widgets.stats.map((widget) => /* @__PURE__ */ import_react19.default.createElement(
|
|
2134
1858
|
StatCard,
|
|
2135
1859
|
{
|
|
2136
1860
|
key: widget.id,
|
|
@@ -2140,7 +1864,7 @@ function ReusableDashboardView({
|
|
|
2140
1864
|
format: widget.format,
|
|
2141
1865
|
accentColor: widget.accentColor
|
|
2142
1866
|
}
|
|
2143
|
-
))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "
|
|
1867
|
+
))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-charts-grid" }, config.widgets.charts.map((widget) => /* @__PURE__ */ import_react19.default.createElement(
|
|
2144
1868
|
ChartCard,
|
|
2145
1869
|
{
|
|
2146
1870
|
key: widget.id,
|
|
@@ -2150,7 +1874,15 @@ function ReusableDashboardView({
|
|
|
2150
1874
|
filters,
|
|
2151
1875
|
chartData: data.charts
|
|
2152
1876
|
}
|
|
2153
|
-
))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "card
|
|
1877
|
+
))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-card", style: { padding: 16 } }, /* @__PURE__ */ import_react19.default.createElement(
|
|
1878
|
+
"div",
|
|
1879
|
+
{
|
|
1880
|
+
style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 12 },
|
|
1881
|
+
className: "rdb-h3"
|
|
1882
|
+
},
|
|
1883
|
+
/* @__PURE__ */ import_react19.default.createElement(Icon, { name: config.widgets.table.icon, size: 18 }),
|
|
1884
|
+
labels[config.widgets.table.label] || config.widgets.table.label
|
|
1885
|
+
), loading ? /* @__PURE__ */ import_react19.default.createElement(SkeletonLoader, { style: { height: 192 } }) : /* @__PURE__ */ import_react19.default.createElement(
|
|
2154
1886
|
DataTable,
|
|
2155
1887
|
{
|
|
2156
1888
|
columns: config.widgets.table.columns,
|
|
@@ -2162,30 +1894,19 @@ function ReusableDashboardView({
|
|
|
2162
1894
|
sortable: true,
|
|
2163
1895
|
pageSize: 10
|
|
2164
1896
|
}
|
|
2165
|
-
)));
|
|
1897
|
+
))));
|
|
2166
1898
|
}
|
|
2167
1899
|
ReusableDashboardView.propTypes = {
|
|
2168
|
-
/** Konfigurasi widget dashboard. */
|
|
2169
1900
|
config: import_prop_types17.default.object.isRequired,
|
|
2170
|
-
/** Objek label i18n. */
|
|
2171
1901
|
labels: import_prop_types17.default.object.isRequired,
|
|
2172
|
-
/** Status loading data. */
|
|
2173
1902
|
loading: import_prop_types17.default.bool,
|
|
2174
|
-
/** Pesan error jika ada. */
|
|
2175
1903
|
error: import_prop_types17.default.string,
|
|
2176
|
-
/** State filter dashboard. */
|
|
2177
1904
|
filters: import_prop_types17.default.object,
|
|
2178
|
-
/** Callback perubahan filter. */
|
|
2179
1905
|
onFilterChange: import_prop_types17.default.func.isRequired,
|
|
2180
|
-
/** Callback reset filter. */
|
|
2181
1906
|
onResetFilters: import_prop_types17.default.func.isRequired,
|
|
2182
|
-
/** Callback refresh data. */
|
|
2183
1907
|
onRefresh: import_prop_types17.default.func.isRequired,
|
|
2184
|
-
/** Data dashboard. */
|
|
2185
1908
|
data: import_prop_types17.default.object,
|
|
2186
|
-
/** Locale untuk format tanggal. */
|
|
2187
1909
|
dateLocale: import_prop_types17.default.string,
|
|
2188
|
-
/** Apakah live update aktif. */
|
|
2189
1910
|
liveUpdateEnabled: import_prop_types17.default.bool
|
|
2190
1911
|
};
|
|
2191
1912
|
|