@underverse-ui/underverse 0.2.42 → 0.2.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4630,7 +4630,7 @@ var DropdownMenu_default = DropdownMenu;
4630
4630
 
4631
4631
  // ../../components/ui/Pagination.tsx
4632
4632
  import * as React21 from "react";
4633
- import { ChevronLeft, ChevronRight as ChevronRight2, MoreHorizontal as MoreHorizontal2, ChevronsLeft, ChevronsRight } from "lucide-react";
4633
+ import { ChevronLeft, ChevronRight as ChevronRight2, ChevronsLeft, ChevronsRight } from "lucide-react";
4634
4634
 
4635
4635
  // ../../components/ui/Combobox.tsx
4636
4636
  import * as React20 from "react";
@@ -4946,13 +4946,11 @@ var Pagination = ({
4946
4946
  onChange,
4947
4947
  className,
4948
4948
  size = "md",
4949
- variant = "outline",
4950
4949
  showFirstLast = true,
4951
4950
  showPrevNext = true,
4952
4951
  showPageNumbers = true,
4953
4952
  showInfo = false,
4954
4953
  disabled = false,
4955
- alignment = "left",
4956
4954
  pageSize,
4957
4955
  pageSizeOptions,
4958
4956
  onPageSizeChange,
@@ -4960,35 +4958,6 @@ var Pagination = ({
4960
4958
  labels
4961
4959
  }) => {
4962
4960
  const t = useTranslations("Pagination");
4963
- const getTextAlignmentClass = (align) => {
4964
- switch (align) {
4965
- case "left":
4966
- return "text-left";
4967
- case "center":
4968
- return "text-center";
4969
- case "right":
4970
- return "text-right";
4971
- }
4972
- };
4973
- const textAlignmentClass = getTextAlignmentClass(alignment);
4974
- const createPageArray = () => {
4975
- const delta = 2;
4976
- const range = [];
4977
- const left = Math.max(2, page - delta);
4978
- const right = Math.min(totalPages - 1, page + delta);
4979
- range.push(1);
4980
- if (left > 2) range.push("...");
4981
- for (let i = left; i <= right; i++) {
4982
- range.push(i);
4983
- }
4984
- if (right < totalPages - 1) range.push("...");
4985
- if (totalPages > 1) range.push(totalPages);
4986
- return range;
4987
- };
4988
- const getButtonVariant = (isActive) => {
4989
- if (isActive) return "primary";
4990
- return variant === "default" ? "default" : variant;
4991
- };
4992
4961
  React21.useEffect(() => {
4993
4962
  if (disabled) return;
4994
4963
  const handleKey = (e) => {
@@ -5022,116 +4991,135 @@ var Pagination = ({
5022
4991
  }
5023
4992
  };
5024
4993
  if (totalPages <= 1) return null;
5025
- return /* @__PURE__ */ jsxs23("nav", { className: cn("flex flex-col gap-4", className), "aria-label": labels?.navigationLabel || t("navigationLabel"), children: [
5026
- showInfo && totalItems && /* @__PURE__ */ jsx26("div", { className: cn("text-sm text-muted-foreground", textAlignmentClass), children: labels?.showingResults ? labels.showingResults({ startItem: startItem || 0, endItem: endItem || 0, totalItems }) : t("showingResults", { startItem: startItem || 0, endItem: endItem || 0, totalItems }) }),
5027
- /* @__PURE__ */ jsxs23(
5028
- "div",
5029
- {
5030
- className: cn("flex items-center justify-between", {
5031
- "flex-row-reverse": alignment === "right" || alignment === "center"
5032
- }),
5033
- children: [
5034
- /* @__PURE__ */ jsxs23("div", { className: cn("flex items-center gap-1"), children: [
5035
- showFirstLast && /* @__PURE__ */ jsx26(
5036
- Button_default,
5037
- {
5038
- variant: getButtonVariant(false),
5039
- size,
5040
- icon: ChevronsLeft,
5041
- onClick: () => onChange(1),
5042
- disabled: disabled || page === 1,
5043
- className: "hidden sm:flex",
5044
- title: labels?.firstPage || t("firstPage"),
5045
- "aria-label": labels?.firstPage || t("firstPage"),
5046
- "aria-disabled": disabled || page === 1
5047
- }
5048
- ),
5049
- showPrevNext && /* @__PURE__ */ jsx26(
5050
- Button_default,
5051
- {
5052
- variant: getButtonVariant(false),
5053
- size,
5054
- icon: ChevronLeft,
5055
- onClick: () => onChange(Math.max(1, page - 1)),
5056
- disabled: disabled || page === 1,
5057
- title: labels?.previousPage || t("previousPage"),
5058
- "aria-label": labels?.previousPage || t("previousPage"),
5059
- "aria-disabled": disabled || page === 1,
5060
- children: /* @__PURE__ */ jsx26("span", { className: "hidden sm:inline", children: labels?.previous || t("previous") })
5061
- }
5062
- ),
5063
- showPageNumbers && createPageArray().map((p, i) => {
5064
- if (p === "...") {
5065
- return /* @__PURE__ */ jsx26(Button_default, { variant: "ghost", size, disabled: true, icon: MoreHorizontal2, className: "cursor-default" }, i);
5066
- }
5067
- const pageNumber = p;
5068
- const isActive = page === pageNumber;
5069
- return /* @__PURE__ */ jsx26(
5070
- Button_default,
5071
- {
5072
- variant: getButtonVariant(isActive),
5073
- size,
5074
- onClick: () => onChange(pageNumber),
5075
- disabled,
5076
- className: cn("min-w-10", isActive && "font-semibold"),
5077
- "aria-label": labels?.pageNumber ? labels.pageNumber(pageNumber) : t("pageNumber", { page: pageNumber }),
5078
- "aria-current": isActive ? "page" : void 0,
5079
- children: pageNumber
5080
- },
5081
- i
5082
- );
5083
- }),
5084
- showPrevNext && /* @__PURE__ */ jsx26(
5085
- Button_default,
5086
- {
5087
- variant: getButtonVariant(false),
5088
- size,
5089
- iconRight: ChevronRight2,
5090
- onClick: () => onChange(Math.min(totalPages, page + 1)),
5091
- disabled: disabled || page === totalPages,
5092
- title: labels?.nextPage || t("nextPage"),
5093
- "aria-label": labels?.nextPage || t("nextPage"),
5094
- "aria-disabled": disabled || page === totalPages,
5095
- children: /* @__PURE__ */ jsx26("span", { className: "hidden sm:inline", children: labels?.next || t("next") })
5096
- }
5097
- ),
5098
- showFirstLast && /* @__PURE__ */ jsx26(
5099
- Button_default,
4994
+ const createCompactPageArray = () => {
4995
+ const pages = [];
4996
+ if (totalPages <= 5) {
4997
+ for (let i = 1; i <= totalPages; i++) pages.push(i);
4998
+ } else {
4999
+ pages.push(1);
5000
+ if (page > 3) pages.push("...");
5001
+ const start = Math.max(2, page - 1);
5002
+ const end = Math.min(totalPages - 1, page + 1);
5003
+ for (let i = start; i <= end; i++) pages.push(i);
5004
+ if (page < totalPages - 2) pages.push("...");
5005
+ pages.push(totalPages);
5006
+ }
5007
+ return pages;
5008
+ };
5009
+ const sizeClasses2 = {
5010
+ sm: { btn: "h-7 w-7", text: "text-xs", page: "h-7 min-w-7 px-1.5" },
5011
+ md: { btn: "h-8 w-8", text: "text-sm", page: "h-8 min-w-8 px-2" },
5012
+ lg: { btn: "h-9 w-9", text: "text-base", page: "h-9 min-w-9 px-2.5" }
5013
+ };
5014
+ const sizeClass = sizeClasses2[size];
5015
+ return /* @__PURE__ */ jsxs23(
5016
+ "nav",
5017
+ {
5018
+ className: cn("flex items-center justify-between gap-2", sizeClass.text, "text-muted-foreground", className),
5019
+ "aria-label": labels?.navigationLabel || t("navigationLabel"),
5020
+ children: [
5021
+ showInfo && totalItems && startItem && endItem ? /* @__PURE__ */ jsxs23("div", { className: "tabular-nums shrink-0", children: [
5022
+ startItem,
5023
+ "-",
5024
+ endItem,
5025
+ "/",
5026
+ totalItems
5027
+ ] }) : /* @__PURE__ */ jsx26("div", {}),
5028
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-0.5", children: [
5029
+ showFirstLast && /* @__PURE__ */ jsx26(
5030
+ "button",
5031
+ {
5032
+ onClick: () => onChange(1),
5033
+ disabled: disabled || page === 1,
5034
+ className: cn(
5035
+ sizeClass.btn,
5036
+ "p-0 rounded transition-colors hidden sm:flex items-center justify-center",
5037
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5038
+ ),
5039
+ title: labels?.firstPage || t("firstPage"),
5040
+ "aria-label": labels?.firstPage || t("firstPage"),
5041
+ children: /* @__PURE__ */ jsx26(ChevronsLeft, { className: "h-4 w-4" })
5042
+ }
5043
+ ),
5044
+ showPrevNext && /* @__PURE__ */ jsx26(
5045
+ "button",
5046
+ {
5047
+ onClick: () => onChange(Math.max(1, page - 1)),
5048
+ disabled: disabled || page === 1,
5049
+ className: cn(
5050
+ sizeClass.btn,
5051
+ "p-0 rounded transition-colors flex items-center justify-center",
5052
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5053
+ ),
5054
+ title: labels?.previousPage || t("previousPage"),
5055
+ "aria-label": labels?.previousPage || t("previousPage"),
5056
+ children: /* @__PURE__ */ jsx26(ChevronLeft, { className: "h-4 w-4" })
5057
+ }
5058
+ ),
5059
+ showPageNumbers && createCompactPageArray().map(
5060
+ (p, i) => p === "..." ? /* @__PURE__ */ jsx26("span", { className: "px-1 text-muted-foreground/60", children: "\u2026" }, `dots-${i}`) : /* @__PURE__ */ jsx26(
5061
+ "button",
5100
5062
  {
5101
- variant: getButtonVariant(false),
5102
- size,
5103
- icon: ChevronsRight,
5104
- onClick: () => onChange(totalPages),
5105
- disabled: disabled || page === totalPages,
5106
- className: "hidden sm:flex",
5107
- title: labels?.lastPage || t("lastPage"),
5108
- "aria-label": labels?.lastPage || t("lastPage"),
5109
- "aria-disabled": disabled || page === totalPages
5110
- }
5063
+ onClick: () => onChange(p),
5064
+ disabled,
5065
+ className: cn(
5066
+ sizeClass.page,
5067
+ "rounded font-medium transition-colors",
5068
+ page === p ? "bg-primary text-primary-foreground" : "hover:bg-accent hover:text-accent-foreground",
5069
+ disabled && "opacity-40 cursor-not-allowed"
5070
+ ),
5071
+ "aria-label": labels?.pageNumber ? labels.pageNumber(p) : t("pageNumber", { page: p }),
5072
+ "aria-current": page === p ? "page" : void 0,
5073
+ children: p
5074
+ },
5075
+ p
5111
5076
  )
5112
- ] }),
5113
- pageSizeOptions && onPageSizeChange && /* @__PURE__ */ jsxs23("div", { className: cn("flex items-center gap-2 text-sm"), children: [
5114
- /* @__PURE__ */ jsxs23("span", { className: "text-muted-foreground", children: [
5115
- labels?.itemsPerPage || t("itemsPerPage"),
5116
- ":"
5117
- ] }),
5118
- /* @__PURE__ */ jsx26("div", { className: "w-20", children: /* @__PURE__ */ jsx26(
5119
- Combobox,
5120
- {
5121
- options: pageSizeOptionsStrings,
5122
- value: pageSize?.toString() || "10",
5123
- onChange: handlePageSizeChange,
5124
- placeholder: "10",
5125
- searchPlaceholder: labels?.search || t("search"),
5126
- emptyText: labels?.noOptions || t("noOptions"),
5127
- disabled
5128
- }
5129
- ) })
5130
- ] })
5131
- ]
5132
- }
5133
- )
5134
- ] });
5077
+ ),
5078
+ showPrevNext && /* @__PURE__ */ jsx26(
5079
+ "button",
5080
+ {
5081
+ onClick: () => onChange(Math.min(totalPages, page + 1)),
5082
+ disabled: disabled || page === totalPages,
5083
+ className: cn(
5084
+ sizeClass.btn,
5085
+ "p-0 rounded transition-colors flex items-center justify-center",
5086
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5087
+ ),
5088
+ title: labels?.nextPage || t("nextPage"),
5089
+ "aria-label": labels?.nextPage || t("nextPage"),
5090
+ children: /* @__PURE__ */ jsx26(ChevronRight2, { className: "h-4 w-4" })
5091
+ }
5092
+ ),
5093
+ showFirstLast && /* @__PURE__ */ jsx26(
5094
+ "button",
5095
+ {
5096
+ onClick: () => onChange(totalPages),
5097
+ disabled: disabled || page === totalPages,
5098
+ className: cn(
5099
+ sizeClass.btn,
5100
+ "p-0 rounded transition-colors hidden sm:flex items-center justify-center",
5101
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5102
+ ),
5103
+ title: labels?.lastPage || t("lastPage"),
5104
+ "aria-label": labels?.lastPage || t("lastPage"),
5105
+ children: /* @__PURE__ */ jsx26(ChevronsRight, { className: "h-4 w-4" })
5106
+ }
5107
+ )
5108
+ ] }),
5109
+ pageSizeOptions && onPageSizeChange ? /* @__PURE__ */ jsx26(
5110
+ Combobox,
5111
+ {
5112
+ options: pageSizeOptionsStrings,
5113
+ value: pageSize?.toString() || "10",
5114
+ onChange: handlePageSizeChange,
5115
+ size: "sm",
5116
+ className: "w-14",
5117
+ disabled
5118
+ }
5119
+ ) : /* @__PURE__ */ jsx26("div", {})
5120
+ ]
5121
+ }
5122
+ );
5135
5123
  };
5136
5124
  var SimplePagination = ({
5137
5125
  page,
@@ -5139,103 +5127,128 @@ var SimplePagination = ({
5139
5127
  onChange,
5140
5128
  className,
5141
5129
  size = "md",
5142
- variant = "outline",
5143
5130
  disabled = false,
5144
5131
  showInfo = false,
5145
5132
  totalItems,
5146
5133
  pageSize = 10
5147
5134
  }) => {
5148
5135
  if (totalPages <= 1) return null;
5149
- return /* @__PURE__ */ jsxs23("div", { className: cn("flex flex-col gap-2", className), children: [
5150
- showInfo && totalItems && /* @__PURE__ */ jsxs23("div", { className: "text-sm text-muted-foreground text-center", children: [
5151
- "Page ",
5152
- page,
5153
- " of ",
5154
- totalPages,
5155
- " (",
5156
- totalItems,
5157
- " total items)"
5158
- ] }),
5159
- /* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-between", children: [
5160
- /* @__PURE__ */ jsx26(Button_default, { variant, size, icon: ChevronLeft, onClick: () => onChange(Math.max(1, page - 1)), disabled: disabled || page === 1, children: "Previous" }),
5161
- /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
5162
- /* @__PURE__ */ jsx26("span", { children: "Page" }),
5163
- /* @__PURE__ */ jsx26("span", { className: "font-medium text-foreground", children: page }),
5164
- /* @__PURE__ */ jsx26("span", { children: "of" }),
5165
- /* @__PURE__ */ jsx26("span", { className: "font-medium text-foreground", children: totalPages })
5136
+ const sizeClasses2 = {
5137
+ sm: { btn: "h-7 w-7", text: "text-xs" },
5138
+ md: { btn: "h-8 w-8", text: "text-sm" },
5139
+ lg: { btn: "h-9 w-9", text: "text-base" }
5140
+ };
5141
+ const sizeClass = sizeClasses2[size];
5142
+ const startItem = totalItems ? (page - 1) * pageSize + 1 : null;
5143
+ const endItem = totalItems ? Math.min(page * pageSize, totalItems) : null;
5144
+ return /* @__PURE__ */ jsxs23("div", { className: cn("flex items-center justify-between gap-2", sizeClass.text, "text-muted-foreground", className), children: [
5145
+ showInfo && totalItems && startItem && endItem ? /* @__PURE__ */ jsxs23("div", { className: "tabular-nums", children: [
5146
+ startItem,
5147
+ "-",
5148
+ endItem,
5149
+ "/",
5150
+ totalItems
5151
+ ] }) : /* @__PURE__ */ jsx26("div", {}),
5152
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-0.5", children: [
5153
+ /* @__PURE__ */ jsx26(
5154
+ "button",
5155
+ {
5156
+ onClick: () => onChange(Math.max(1, page - 1)),
5157
+ disabled: disabled || page === 1,
5158
+ className: cn(
5159
+ sizeClass.btn,
5160
+ "p-0 rounded transition-colors flex items-center justify-center",
5161
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5162
+ ),
5163
+ "aria-label": "Previous page",
5164
+ children: /* @__PURE__ */ jsx26(ChevronLeft, { className: "h-4 w-4" })
5165
+ }
5166
+ ),
5167
+ /* @__PURE__ */ jsxs23("span", { className: "px-2 tabular-nums font-medium text-foreground", children: [
5168
+ page,
5169
+ "/",
5170
+ totalPages
5166
5171
  ] }),
5167
5172
  /* @__PURE__ */ jsx26(
5168
- Button_default,
5173
+ "button",
5169
5174
  {
5170
- variant,
5171
- size,
5172
- iconRight: ChevronRight2,
5173
5175
  onClick: () => onChange(Math.min(totalPages, page + 1)),
5174
5176
  disabled: disabled || page === totalPages,
5175
- children: "Next"
5177
+ className: cn(
5178
+ sizeClass.btn,
5179
+ "p-0 rounded transition-colors flex items-center justify-center",
5180
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5181
+ ),
5182
+ "aria-label": "Next page",
5183
+ children: /* @__PURE__ */ jsx26(ChevronRight2, { className: "h-4 w-4" })
5176
5184
  }
5177
5185
  )
5178
- ] })
5186
+ ] }),
5187
+ /* @__PURE__ */ jsx26("div", {})
5179
5188
  ] });
5180
5189
  };
5181
- var CompactPagination = ({
5182
- page,
5183
- totalPages,
5184
- onChange,
5185
- className,
5186
- variant = "outline",
5187
- disabled = false
5188
- }) => {
5190
+ var CompactPagination = ({ page, totalPages, onChange, className, disabled = false }) => {
5189
5191
  if (totalPages <= 1) return null;
5190
- return /* @__PURE__ */ jsxs23("nav", { className: cn("flex items-center gap-1", className), "aria-label": "Compact Pagination", children: [
5192
+ return /* @__PURE__ */ jsxs23("nav", { className: cn("flex items-center gap-0.5 text-xs text-muted-foreground", className), "aria-label": "Compact Pagination", children: [
5191
5193
  /* @__PURE__ */ jsx26(
5192
- Button_default,
5194
+ "button",
5193
5195
  {
5194
- variant,
5195
- size: "icon",
5196
- icon: ChevronsLeft,
5197
5196
  onClick: () => onChange(1),
5198
5197
  disabled: disabled || page === 1,
5198
+ className: cn(
5199
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5200
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5201
+ ),
5199
5202
  title: "First page",
5200
- "aria-label": "First page"
5203
+ "aria-label": "First page",
5204
+ children: /* @__PURE__ */ jsx26(ChevronsLeft, { className: "h-3.5 w-3.5" })
5201
5205
  }
5202
5206
  ),
5203
5207
  /* @__PURE__ */ jsx26(
5204
- Button_default,
5208
+ "button",
5205
5209
  {
5206
- variant,
5207
- size: "icon",
5208
- icon: ChevronLeft,
5209
5210
  onClick: () => onChange(Math.max(1, page - 1)),
5210
5211
  disabled: disabled || page === 1,
5211
- title: "Previous page"
5212
+ className: cn(
5213
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5214
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5215
+ ),
5216
+ title: "Previous page",
5217
+ "aria-label": "Previous page",
5218
+ children: /* @__PURE__ */ jsx26(ChevronLeft, { className: "h-3.5 w-3.5" })
5212
5219
  }
5213
5220
  ),
5214
- /* @__PURE__ */ jsxs23("div", { className: "px-2 py-1 text-sm text-muted-foreground min-w-16 text-center", children: [
5221
+ /* @__PURE__ */ jsxs23("span", { className: "px-1.5 tabular-nums", children: [
5215
5222
  page,
5216
- " / ",
5223
+ "/",
5217
5224
  totalPages
5218
5225
  ] }),
5219
5226
  /* @__PURE__ */ jsx26(
5220
- Button_default,
5227
+ "button",
5221
5228
  {
5222
- variant,
5223
- size: "icon",
5224
- icon: ChevronRight2,
5225
5229
  onClick: () => onChange(Math.min(totalPages, page + 1)),
5226
5230
  disabled: disabled || page === totalPages,
5227
- title: "Next page"
5231
+ className: cn(
5232
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5233
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5234
+ ),
5235
+ title: "Next page",
5236
+ "aria-label": "Next page",
5237
+ children: /* @__PURE__ */ jsx26(ChevronRight2, { className: "h-3.5 w-3.5" })
5228
5238
  }
5229
5239
  ),
5230
5240
  /* @__PURE__ */ jsx26(
5231
- Button_default,
5241
+ "button",
5232
5242
  {
5233
- variant,
5234
- size: "icon",
5235
- icon: ChevronsRight,
5236
5243
  onClick: () => onChange(totalPages),
5237
5244
  disabled: disabled || page === totalPages,
5238
- title: "Last page"
5245
+ className: cn(
5246
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5247
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5248
+ ),
5249
+ title: "Last page",
5250
+ "aria-label": "Last page",
5251
+ children: /* @__PURE__ */ jsx26(ChevronsRight, { className: "h-3.5 w-3.5" })
5239
5252
  }
5240
5253
  )
5241
5254
  ] });
@@ -5344,7 +5357,8 @@ var DatePicker = ({
5344
5357
  required,
5345
5358
  todayLabel,
5346
5359
  clearLabel,
5347
- weekdayLabels
5360
+ weekdayLabels,
5361
+ disablePastDates = false
5348
5362
  }) => {
5349
5363
  const t = useTranslations("DatePicker");
5350
5364
  const locale = useLocale();
@@ -5448,17 +5462,21 @@ var DatePicker = ({
5448
5462
  for (let i = 0; i < firstDayOfMonth; i++) {
5449
5463
  days.push(/* @__PURE__ */ jsx29("div", { className: "w-8 h-8" }, `empty-${i}`));
5450
5464
  }
5465
+ const today = /* @__PURE__ */ new Date();
5466
+ today.setHours(0, 0, 0, 0);
5451
5467
  for (let day = 1; day <= daysInMonth; day++) {
5452
5468
  const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), day);
5453
5469
  const isSelected = value && date.getDate() === value.getDate() && date.getMonth() === value.getMonth() && date.getFullYear() === value.getFullYear();
5454
5470
  const isToday2 = date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
5471
+ const isPastDate = disablePastDates && date < today;
5455
5472
  const totalDaysFromStart = firstDayOfMonth + day - 1;
5456
5473
  const rowIndex = Math.floor(totalDaysFromStart / 7);
5457
5474
  days.push(
5458
5475
  /* @__PURE__ */ jsx29(
5459
5476
  "button",
5460
5477
  {
5461
- onClick: () => handleDateSelect(date),
5478
+ onClick: () => !isPastDate && handleDateSelect(date),
5479
+ disabled: isPastDate,
5462
5480
  style: {
5463
5481
  animationDelay: isOpen ? `${rowIndex * 50}ms` : "0ms"
5464
5482
  },
@@ -5466,7 +5484,8 @@ var DatePicker = ({
5466
5484
  size === "sm" ? "w-7 h-7 text-[12px]" : "w-8 h-8 text-sm",
5467
5485
  "datepicker-day rounded-md focus:outline-none",
5468
5486
  "transition-colors",
5469
- isSelected ? "bg-primary! text-primary-foreground font-bold ring-2 ring-primary-foreground/60 shadow-lg scale-105 z-10 hover:bg-primary! focus:bg-primary! focus:text-primary-foreground" : "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
5487
+ isPastDate && "opacity-40 cursor-not-allowed text-muted-foreground",
5488
+ isSelected ? "bg-primary! text-primary-foreground font-bold ring-2 ring-primary-foreground/60 shadow-lg scale-105 z-10 hover:bg-primary! focus:bg-primary! focus:text-primary-foreground" : !isPastDate && "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
5470
5489
  isToday2 && !isSelected && "bg-accent text-accent-foreground font-semibold"
5471
5490
  ),
5472
5491
  children: day
@@ -5617,7 +5636,7 @@ var DatePicker = ({
5617
5636
  isOpen && dropdownPosition && typeof window !== "undefined" && createPortal7(datePickerContent, document.body)
5618
5637
  ] });
5619
5638
  };
5620
- var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select date range...", className }) => {
5639
+ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select date range...", className, disablePastDates = false }) => {
5621
5640
  const locale = useLocale();
5622
5641
  const [isOpen, setIsOpen] = React23.useState(false);
5623
5642
  const [dropdownPosition, setDropdownPosition] = React23.useState(null);
@@ -5678,16 +5697,17 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5678
5697
  const getDaysInMonth = (d) => new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
5679
5698
  const getFirstDayOfMonth = (d) => new Date(d.getFullYear(), d.getMonth(), 1).getDay();
5680
5699
  const handleSelect = (date) => {
5700
+ const localDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
5681
5701
  if (!tempStart || tempStart && tempEnd) {
5682
- setTempStart(date);
5702
+ setTempStart(localDate);
5683
5703
  setTempEnd(null);
5684
5704
  setHoveredDate(null);
5685
5705
  } else if (tempStart && !tempEnd) {
5686
- if (date < tempStart) {
5687
- setTempStart(date);
5706
+ if (localDate < tempStart) {
5707
+ setTempStart(localDate);
5688
5708
  } else {
5689
- setTempEnd(date);
5690
- onChange(tempStart, date);
5709
+ setTempEnd(localDate);
5710
+ onChange(tempStart, localDate);
5691
5711
  setIsOpen(false);
5692
5712
  }
5693
5713
  }
@@ -5696,9 +5716,12 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5696
5716
  const nodes = [];
5697
5717
  const daysInMonth = getDaysInMonth(viewDate);
5698
5718
  const firstDay = getFirstDayOfMonth(viewDate);
5719
+ const today = /* @__PURE__ */ new Date();
5720
+ today.setHours(0, 0, 0, 0);
5699
5721
  for (let i = 0; i < firstDay; i++) nodes.push(/* @__PURE__ */ jsx29("div", { className: "w-8 h-8" }, `e-${i}`));
5700
5722
  for (let d = 1; d <= daysInMonth; d++) {
5701
5723
  const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), d);
5724
+ const isPastDate = disablePastDates && date < today;
5702
5725
  const isSelectedStart = isSameDay2(date, tempStart);
5703
5726
  const isSelectedEnd = isSameDay2(date, tempEnd);
5704
5727
  const isHovering = hoveredDate && tempStart && !tempEnd;
@@ -5724,13 +5747,16 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5724
5747
  /* @__PURE__ */ jsx29(
5725
5748
  "button",
5726
5749
  {
5727
- onClick: () => handleSelect(date),
5728
- onMouseEnter: () => tempStart && !tempEnd && setHoveredDate(date),
5750
+ onClick: () => !isPastDate && handleSelect(date),
5751
+ disabled: isPastDate,
5752
+ onMouseEnter: () => !isPastDate && tempStart && !tempEnd && setHoveredDate(date),
5729
5753
  onMouseLeave: () => tempStart && !tempEnd && setHoveredDate(null),
5730
5754
  className: cn(
5731
5755
  "w-8 h-8 text-sm transition-all duration-200 focus:outline-none relative font-medium",
5756
+ // Disabled/past date state
5757
+ isPastDate && "opacity-40 cursor-not-allowed text-muted-foreground",
5732
5758
  // Default state
5733
- !isInRange && !isRangeStart && !isRangeEnd && "hover:bg-accent hover:text-accent-foreground rounded-md",
5759
+ !isPastDate && !isInRange && !isRangeStart && !isRangeEnd && "hover:bg-accent hover:text-accent-foreground rounded-md",
5734
5760
  // Range selection styling - smooth continuous background
5735
5761
  isInRange && "bg-primary/15 text-foreground shadow-sm",
5736
5762
  (isRangeStart || isRangeEnd) && "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm",
@@ -5740,8 +5766,8 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5740
5766
  isRangeStart && isRangeEnd && "rounded-md",
5741
5767
  // Single day selection
5742
5768
  // Hover effects for range
5743
- isInRange && "hover:bg-primary/25",
5744
- "focus:bg-accent focus:text-accent-foreground focus:z-10 focus:shadow-md"
5769
+ isInRange && !isPastDate && "hover:bg-primary/25",
5770
+ !isPastDate && "focus:bg-accent focus:text-accent-foreground focus:z-10 focus:shadow-md"
5745
5771
  ),
5746
5772
  children: d
5747
5773
  },
@@ -7918,12 +7944,26 @@ function OverlayControls({
7918
7944
  }
7919
7945
 
7920
7946
  // ../../components/ui/CategoryTreeSelect.tsx
7921
- import { useState as useState25 } from "react";
7947
+ import { useState as useState25, useEffect as useEffect15 } from "react";
7922
7948
  import { ChevronRight as ChevronRight5, ChevronDown as ChevronDown3, Check as Check6 } from "lucide-react";
7923
7949
  import { Fragment as Fragment11, jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
7924
- function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1ECDn danh m\u1EE5c", disabled }) {
7950
+ var defaultLabels = {
7951
+ emptyText: "No categories",
7952
+ selectedText: (count) => `${count} selected`
7953
+ };
7954
+ function CategoryTreeSelect({
7955
+ categories,
7956
+ value = [],
7957
+ onChange,
7958
+ placeholder = "Select category",
7959
+ disabled,
7960
+ viewOnly = false,
7961
+ defaultExpanded = false,
7962
+ labels
7963
+ }) {
7925
7964
  const [isOpen, setIsOpen] = useState25(false);
7926
7965
  const [expandedNodes, setExpandedNodes] = useState25(/* @__PURE__ */ new Set());
7966
+ const mergedLabels = { ...defaultLabels, ...labels };
7927
7967
  const parentCategories = categories.filter((c) => !c.parent_id);
7928
7968
  const childrenMap = /* @__PURE__ */ new Map();
7929
7969
  categories.forEach((cat) => {
@@ -7934,6 +7974,12 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
7934
7974
  childrenMap.get(cat.parent_id).push(cat);
7935
7975
  }
7936
7976
  });
7977
+ useEffect15(() => {
7978
+ if (viewOnly && defaultExpanded) {
7979
+ const allParentIds = categories.filter((c) => childrenMap.has(c.id)).map((c) => c.id);
7980
+ setExpandedNodes(new Set(allParentIds));
7981
+ }
7982
+ }, [viewOnly, defaultExpanded, categories]);
7937
7983
  const toggleExpand = (id) => {
7938
7984
  const newExpanded = new Set(expandedNodes);
7939
7985
  if (newExpanded.has(id)) {
@@ -7944,6 +7990,7 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
7944
7990
  setExpandedNodes(newExpanded);
7945
7991
  };
7946
7992
  const handleSelect = (categoryId, category) => {
7993
+ if (viewOnly || !onChange) return;
7947
7994
  const newSelected = new Set(value);
7948
7995
  if (newSelected.has(categoryId)) {
7949
7996
  newSelected.delete(categoryId);
@@ -7967,20 +8014,14 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
7967
8014
  "div",
7968
8015
  {
7969
8016
  className: cn(
7970
- "relative flex items-center gap-2 px-3 py-2 cursor-pointer rounded-md transition-colors",
7971
- "hover:bg-accent",
7972
- // Selected state: subtle bg + square left indicator, avoid left rounding
7973
- isSelected && "bg-primary/10 rounded-r-md"
8017
+ "relative flex items-center gap-2 px-3 py-2 rounded-md transition-colors",
8018
+ !viewOnly && "cursor-pointer hover:bg-accent",
8019
+ // Selected state: subtle bg + square left indicator (only in select mode)
8020
+ !viewOnly && isSelected && "bg-primary/10 rounded-r-md"
7974
8021
  ),
7975
8022
  style: { paddingLeft: `${level * 1.5 + 0.75}rem` },
7976
8023
  children: [
7977
- isSelected && /* @__PURE__ */ jsx36(
7978
- "span",
7979
- {
7980
- "aria-hidden": true,
7981
- className: "absolute left-0 top-0 bottom-0 w-1 bg-primary"
7982
- }
7983
- ),
8024
+ !viewOnly && isSelected && /* @__PURE__ */ jsx36("span", { "aria-hidden": true, className: "absolute left-0 top-0 bottom-0 w-1 bg-primary" }),
7984
8025
  hasChildren ? /* @__PURE__ */ jsx36(
7985
8026
  "button",
7986
8027
  {
@@ -7993,25 +8034,24 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
7993
8034
  children: isExpanded ? /* @__PURE__ */ jsx36(ChevronDown3, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx36(ChevronRight5, { className: "w-4 h-4" })
7994
8035
  }
7995
8036
  ) : /* @__PURE__ */ jsx36("span", { className: "w-5" }),
7996
- /* @__PURE__ */ jsxs31(
7997
- "div",
7998
- {
7999
- onClick: () => handleSelect(category.id, category),
8000
- className: "flex items-center gap-2 flex-1",
8001
- children: [
8002
- /* @__PURE__ */ jsx36(
8003
- "div",
8004
- {
8005
- className: cn(
8006
- "w-4 h-4 border-2 rounded flex items-center justify-center transition-colors",
8007
- isSelected ? "bg-primary border-primary" : "border-muted-foreground/30"
8008
- ),
8009
- children: isSelected && /* @__PURE__ */ jsx36(Check6, { className: "w-3 h-3 text-primary-foreground" })
8010
- }
8011
- ),
8012
- /* @__PURE__ */ jsx36("span", { className: cn("text-sm", isSelected && "font-medium text-primary"), children: category.name })
8013
- ]
8014
- }
8037
+ viewOnly ? (
8038
+ // View-only mode: just display the name
8039
+ /* @__PURE__ */ jsx36("span", { className: "text-sm", children: category.name })
8040
+ ) : (
8041
+ // Select mode: clickable with checkbox
8042
+ /* @__PURE__ */ jsxs31("div", { onClick: () => handleSelect(category.id, category), className: "flex items-center gap-2 flex-1", children: [
8043
+ /* @__PURE__ */ jsx36(
8044
+ "div",
8045
+ {
8046
+ className: cn(
8047
+ "w-4 h-4 border-2 rounded flex items-center justify-center transition-colors",
8048
+ isSelected ? "bg-primary border-primary" : "border-muted-foreground/30"
8049
+ ),
8050
+ children: isSelected && /* @__PURE__ */ jsx36(Check6, { className: "w-3 h-3 text-primary-foreground" })
8051
+ }
8052
+ ),
8053
+ /* @__PURE__ */ jsx36("span", { className: cn("text-sm", isSelected && "font-medium text-primary"), children: category.name })
8054
+ ] })
8015
8055
  )
8016
8056
  ]
8017
8057
  }
@@ -8019,8 +8059,11 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8019
8059
  hasChildren && isExpanded && /* @__PURE__ */ jsx36("div", { children: children.map((child) => renderCategory(child, level + 1)) })
8020
8060
  ] }, category.id);
8021
8061
  };
8062
+ if (viewOnly) {
8063
+ return /* @__PURE__ */ jsx36("div", { className: cn("rounded-md border bg-background p-2", disabled && "opacity-50"), children: parentCategories.length === 0 ? /* @__PURE__ */ jsx36("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: mergedLabels.emptyText }) : parentCategories.map((cat) => renderCategory(cat)) });
8064
+ }
8022
8065
  const selectedCount = value.length;
8023
- const displayText = selectedCount > 0 ? `\u0110\xE3 ch\u1ECDn ${selectedCount} danh m\u1EE5c` : placeholder;
8066
+ const displayText = selectedCount > 0 ? mergedLabels.selectedText(selectedCount) : placeholder;
8024
8067
  return /* @__PURE__ */ jsxs31("div", { className: "relative", children: [
8025
8068
  /* @__PURE__ */ jsxs31(
8026
8069
  "button",
@@ -8053,7 +8096,7 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8053
8096
  "rounded-md border bg-popover text-popover-foreground shadow-md",
8054
8097
  "backdrop-blur-sm bg-popover/95 border-border/60"
8055
8098
  ),
8056
- children: /* @__PURE__ */ jsx36("div", { className: "p-1", children: parentCategories.length === 0 ? /* @__PURE__ */ jsx36("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "Kh\xF4ng c\xF3 danh m\u1EE5c n\xE0o" }) : parentCategories.map((cat) => renderCategory(cat)) })
8099
+ children: /* @__PURE__ */ jsx36("div", { className: "p-1", children: parentCategories.length === 0 ? /* @__PURE__ */ jsx36("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: mergedLabels.emptyText }) : parentCategories.map((cat) => renderCategory(cat)) })
8057
8100
  }
8058
8101
  )
8059
8102
  ] })
@@ -10278,16 +10321,16 @@ var Grid_default = Grid;
10278
10321
  import { useMemo as useMemo6, useState as useState33, useRef as useRef14 } from "react";
10279
10322
 
10280
10323
  // ../../components/ui/ChartTooltip.tsx
10281
- import { useEffect as useEffect18, useState as useState32 } from "react";
10324
+ import { useEffect as useEffect19, useState as useState32 } from "react";
10282
10325
  import { createPortal as createPortal10 } from "react-dom";
10283
10326
  import { Fragment as Fragment16, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
10284
10327
  function ChartTooltip({ x, y, visible, label, value, color, secondaryLabel, secondaryValue, items, containerRef }) {
10285
10328
  const [isMounted, setIsMounted] = useState32(false);
10286
10329
  const [position, setPosition] = useState32(null);
10287
- useEffect18(() => {
10330
+ useEffect19(() => {
10288
10331
  setIsMounted(true);
10289
10332
  }, []);
10290
- useEffect18(() => {
10333
+ useEffect19(() => {
10291
10334
  if (visible && containerRef?.current) {
10292
10335
  const rect = containerRef.current.getBoundingClientRect();
10293
10336
  setPosition({
@@ -11639,11 +11682,11 @@ function GaugeChart({
11639
11682
  }
11640
11683
 
11641
11684
  // ../../components/ui/ClientOnly.tsx
11642
- import { useEffect as useEffect19, useState as useState38 } from "react";
11685
+ import { useEffect as useEffect20, useState as useState38 } from "react";
11643
11686
  import { Fragment as Fragment19, jsx as jsx53 } from "react/jsx-runtime";
11644
11687
  function ClientOnly({ children, fallback = null }) {
11645
11688
  const [hasMounted, setHasMounted] = useState38(false);
11646
- useEffect19(() => {
11689
+ useEffect20(() => {
11647
11690
  setHasMounted(true);
11648
11691
  }, []);
11649
11692
  if (!hasMounted) {
@@ -12214,23 +12257,71 @@ function DataTable({
12214
12257
  ]
12215
12258
  }
12216
12259
  ) }),
12217
- totalItems > 0 && /* @__PURE__ */ jsx56("div", { className: "border-t bg-muted/30 p-4 rounded-b-md", children: /* @__PURE__ */ jsx56(
12218
- Pagination,
12219
- {
12220
- page: curPage,
12221
- totalPages: Math.ceil(totalItems / curPageSize),
12222
- onChange: (p) => setCurPage(p),
12223
- className: "",
12224
- showInfo: true,
12225
- totalItems,
12226
- pageSize: curPageSize,
12227
- pageSizeOptions,
12228
- onPageSizeChange: (s) => {
12229
- setCurPage(1);
12230
- setCurPageSize(s);
12260
+ totalItems > 0 && Math.ceil(totalItems / curPageSize) > 1 && /* @__PURE__ */ jsxs50("div", { className: "flex items-center justify-between gap-2 px-1 pt-3 text-xs text-muted-foreground", children: [
12261
+ /* @__PURE__ */ jsxs50("div", { className: "tabular-nums", children: [
12262
+ (curPage - 1) * curPageSize + 1,
12263
+ "-",
12264
+ Math.min(curPage * curPageSize, totalItems),
12265
+ "/",
12266
+ totalItems
12267
+ ] }),
12268
+ /* @__PURE__ */ jsxs50("div", { className: "flex items-center gap-0.5", children: [
12269
+ /* @__PURE__ */ jsx56(Button_default, { variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setCurPage(Math.max(1, curPage - 1)), disabled: curPage === 1, children: /* @__PURE__ */ jsx56("svg", { className: "h-4 w-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx56("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }),
12270
+ (() => {
12271
+ const totalPages = Math.ceil(totalItems / curPageSize);
12272
+ const pages = [];
12273
+ if (totalPages <= 5) {
12274
+ for (let i = 1; i <= totalPages; i++) pages.push(i);
12275
+ } else {
12276
+ pages.push(1);
12277
+ if (curPage > 3) pages.push("...");
12278
+ const start = Math.max(2, curPage - 1);
12279
+ const end = Math.min(totalPages - 1, curPage + 1);
12280
+ for (let i = start; i <= end; i++) pages.push(i);
12281
+ if (curPage < totalPages - 2) pages.push("...");
12282
+ pages.push(totalPages);
12283
+ }
12284
+ return pages.map(
12285
+ (p, i) => p === "..." ? /* @__PURE__ */ jsx56("span", { className: "px-1 text-muted-foreground/60", children: "\u2026" }, `dots-${i}`) : /* @__PURE__ */ jsx56(
12286
+ "button",
12287
+ {
12288
+ onClick: () => setCurPage(p),
12289
+ className: cn(
12290
+ "h-7 min-w-7 px-1.5 rounded text-xs font-medium transition-colors",
12291
+ curPage === p ? "bg-primary text-primary-foreground" : "hover:bg-accent hover:text-accent-foreground"
12292
+ ),
12293
+ children: p
12294
+ },
12295
+ p
12296
+ )
12297
+ );
12298
+ })(),
12299
+ /* @__PURE__ */ jsx56(
12300
+ Button_default,
12301
+ {
12302
+ variant: "ghost",
12303
+ size: "sm",
12304
+ className: "h-7 w-7 p-0",
12305
+ onClick: () => setCurPage(Math.min(Math.ceil(totalItems / curPageSize), curPage + 1)),
12306
+ disabled: curPage === Math.ceil(totalItems / curPageSize),
12307
+ children: /* @__PURE__ */ jsx56("svg", { className: "h-4 w-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx56("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
12308
+ }
12309
+ )
12310
+ ] }),
12311
+ pageSizeOptions && /* @__PURE__ */ jsx56(
12312
+ Combobox,
12313
+ {
12314
+ options: pageSizeOptions.map(String),
12315
+ value: String(curPageSize),
12316
+ onChange: (v) => {
12317
+ setCurPage(1);
12318
+ setCurPageSize(Number(v));
12319
+ },
12320
+ size: "sm",
12321
+ className: "w-14 h-7"
12231
12322
  }
12232
- }
12233
- ) })
12323
+ )
12324
+ ] })
12234
12325
  ] });
12235
12326
  }
12236
12327
  var DataTable_default = DataTable;
@@ -12548,7 +12639,7 @@ function AccessDenied({
12548
12639
 
12549
12640
  // ../../components/ui/ThemeToggleHeadless.tsx
12550
12641
  import { Moon, Sun, Monitor } from "lucide-react";
12551
- import { useEffect as useEffect21, useRef as useRef19, useState as useState39 } from "react";
12642
+ import { useEffect as useEffect22, useRef as useRef19, useState as useState39 } from "react";
12552
12643
  import { createPortal as createPortal11 } from "react-dom";
12553
12644
  import { Fragment as Fragment21, jsx as jsx61, jsxs as jsxs55 } from "react/jsx-runtime";
12554
12645
  function ThemeToggleHeadless({
@@ -12561,7 +12652,7 @@ function ThemeToggleHeadless({
12561
12652
  const [mounted, setMounted] = useState39(false);
12562
12653
  const triggerRef = useRef19(null);
12563
12654
  const [dropdownPosition, setDropdownPosition] = useState39(null);
12564
- useEffect21(() => setMounted(true), []);
12655
+ useEffect22(() => setMounted(true), []);
12565
12656
  const themes = [
12566
12657
  { value: "light", label: labels?.light ?? "Light", icon: Sun },
12567
12658
  { value: "dark", label: labels?.dark ?? "Dark", icon: Moon },