@juv/codego-react-ui 3.1.8 → 3.2.2

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.cjs CHANGED
@@ -1914,6 +1914,153 @@ function BulletinDeleteConfirm({
1914
1914
  document.body
1915
1915
  );
1916
1916
  }
1917
+ function BulletinEditModal({
1918
+ item,
1919
+ baseUrl,
1920
+ idKey,
1921
+ method,
1922
+ fields,
1923
+ onClose,
1924
+ onSuccess
1925
+ }) {
1926
+ const [loading, setLoading] = React8.useState(false);
1927
+ const [error, setError] = React8.useState(null);
1928
+ const [formData, setFormData] = React8.useState(() => {
1929
+ const initial = {};
1930
+ fields.forEach((f) => {
1931
+ initial[f.key] = item[f.key] ?? "";
1932
+ });
1933
+ return initial;
1934
+ });
1935
+ const id = item[idKey ?? "id"];
1936
+ const handleChange = (key, value) => {
1937
+ setFormData((prev) => ({ ...prev, [key]: value }));
1938
+ };
1939
+ const handleSubmit = async (e) => {
1940
+ e.preventDefault();
1941
+ setLoading(true);
1942
+ setError(null);
1943
+ try {
1944
+ const response = await (0, import_axios2.default)({
1945
+ method: method ?? "PUT",
1946
+ url: `${baseUrl}/${id}`,
1947
+ data: formData
1948
+ });
1949
+ onSuccess?.(response.data);
1950
+ onClose();
1951
+ } catch (err) {
1952
+ setError(err?.response?.data?.message ?? err.message ?? "Update failed");
1953
+ } finally {
1954
+ setLoading(false);
1955
+ }
1956
+ };
1957
+ return (0, import_react_dom.createPortal)(
1958
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1959
+ "div",
1960
+ {
1961
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
1962
+ style: { background: "rgba(0,0,0,0.55)" },
1963
+ onMouseDown: (e) => {
1964
+ if (e.target === e.currentTarget) onClose();
1965
+ },
1966
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl p-6 space-y-4 max-h-[90vh] overflow-y-auto", children: [
1967
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "text-base font-semibold", children: "Edit Post" }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1970
+ "button",
1971
+ {
1972
+ type: "button",
1973
+ onClick: onClose,
1974
+ className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground hover:bg-accent hover:text-foreground transition-colors",
1975
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.X, { className: "h-4 w-4" })
1976
+ }
1977
+ )
1978
+ ] }),
1979
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
1980
+ fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-1.5", children: [
1981
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
1982
+ field.type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1983
+ "textarea",
1984
+ {
1985
+ value: formData[field.key] ?? "",
1986
+ onChange: (e) => handleChange(field.key, e.target.value),
1987
+ placeholder: field.placeholder,
1988
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring resize-none",
1989
+ rows: 4
1990
+ }
1991
+ ) : field.type === "select" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1992
+ "select",
1993
+ {
1994
+ value: formData[field.key] ?? "",
1995
+ onChange: (e) => handleChange(field.key, e.target.value),
1996
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring",
1997
+ children: [
1998
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: "Select..." }),
1999
+ field.options?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: opt, children: opt }, opt))
2000
+ ]
2001
+ }
2002
+ ) : field.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
2003
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2004
+ "input",
2005
+ {
2006
+ type: "checkbox",
2007
+ checked: formData[field.key] ?? false,
2008
+ onChange: (e) => handleChange(field.key, e.target.checked),
2009
+ className: "h-4 w-4 rounded border-border"
2010
+ }
2011
+ ),
2012
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-sm text-muted-foreground", children: field.placeholder })
2013
+ ] }) : field.type === "date" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2014
+ "input",
2015
+ {
2016
+ type: "date",
2017
+ value: formData[field.key] ?? "",
2018
+ onChange: (e) => handleChange(field.key, e.target.value),
2019
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring"
2020
+ }
2021
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2022
+ "input",
2023
+ {
2024
+ type: "text",
2025
+ value: formData[field.key] ?? "",
2026
+ onChange: (e) => handleChange(field.key, e.target.value),
2027
+ placeholder: field.placeholder,
2028
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring"
2029
+ }
2030
+ )
2031
+ ] }, field.key)),
2032
+ error && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-xs text-danger", children: error }),
2033
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-end gap-2 pt-2", children: [
2034
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2035
+ "button",
2036
+ {
2037
+ type: "button",
2038
+ onClick: onClose,
2039
+ disabled: loading,
2040
+ className: "px-4 py-1.5 text-sm rounded-xl border border-border hover:bg-accent transition-colors",
2041
+ children: "Cancel"
2042
+ }
2043
+ ),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2045
+ "button",
2046
+ {
2047
+ type: "submit",
2048
+ disabled: loading,
2049
+ className: "px-4 py-1.5 text-sm rounded-xl bg-primary text-primary-foreground hover:bg-primary/90 transition-colors flex items-center gap-1.5",
2050
+ children: [
2051
+ loading && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
2052
+ loading ? "Saving\u2026" : "Save"
2053
+ ]
2054
+ }
2055
+ )
2056
+ ] })
2057
+ ] })
2058
+ ] })
2059
+ }
2060
+ ),
2061
+ document.body
2062
+ );
2063
+ }
1917
2064
  function ActionMenu({ item, actions }) {
1918
2065
  const [open, setOpen] = React8.useState(false);
1919
2066
  const ref = React8.useRef(null);
@@ -2063,12 +2210,17 @@ function BulletinBoard({
2063
2210
  onEdit,
2064
2211
  onDelete,
2065
2212
  preview = false,
2213
+ editBaseUrl,
2214
+ editMethod,
2215
+ editIdKey,
2216
+ editFields,
2066
2217
  deleteBaseUrl,
2067
2218
  deleteIdKey = "id",
2068
2219
  serverPagination,
2069
2220
  className
2070
2221
  }) {
2071
2222
  const [previewItem, setPreviewItem] = React8.useState(null);
2223
+ const [editItem, setEditItem] = React8.useState(null);
2072
2224
  const [deleteItem, setDeleteItem] = React8.useState(null);
2073
2225
  const [search, setSearch] = React8.useState("");
2074
2226
  const [category, setCategory] = React8.useState(null);
@@ -2214,7 +2366,10 @@ function BulletinBoard({
2214
2366
  onView: onView ? (item) => {
2215
2367
  onView(item);
2216
2368
  } : void 0,
2217
- onEdit: onEdit ? (item) => {
2369
+ onEdit: editBaseUrl && editFields ? (item) => {
2370
+ setPreviewItem(null);
2371
+ setEditItem(item);
2372
+ } : onEdit ? (item) => {
2218
2373
  onEdit(item);
2219
2374
  } : void 0,
2220
2375
  onDelete: deleteBaseUrl ? (item) => {
@@ -2225,6 +2380,20 @@ function BulletinBoard({
2225
2380
  } : void 0
2226
2381
  }
2227
2382
  ),
2383
+ editItem && editBaseUrl && editFields && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2384
+ BulletinEditModal,
2385
+ {
2386
+ item: editItem,
2387
+ baseUrl: editBaseUrl,
2388
+ idKey: editIdKey,
2389
+ method: editMethod,
2390
+ fields: editFields,
2391
+ onClose: () => setEditItem(null),
2392
+ onSuccess: (updated) => {
2393
+ onEdit?.(updated);
2394
+ }
2395
+ }
2396
+ ),
2228
2397
  deleteItem && deleteBaseUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2229
2398
  BulletinDeleteConfirm,
2230
2399
  {
@@ -6414,7 +6583,6 @@ function DGEditModal({
6414
6583
  onSuccess,
6415
6584
  notif
6416
6585
  }) {
6417
- const { toast } = useToast();
6418
6586
  const [form, setForm] = React28.useState(() => {
6419
6587
  const init = {};
6420
6588
  fields.forEach((f) => {
@@ -6431,15 +6599,14 @@ function DGEditModal({
6431
6599
  setError(null);
6432
6600
  try {
6433
6601
  await import_axios3.default.put(`${baseUrl}/${itemId}/update`, form);
6434
- if (notif) {
6435
- if ((notif.type ?? "toast") === "toast") {
6436
- toast({ variant: notif.editVariant ?? "success", title: notif.editTitle ?? "Record updated", description: notif.editBody, position: notif.toastPosition });
6437
- } else {
6438
- setBanner(true);
6439
- }
6602
+ const updated = { ...item, ...form };
6603
+ if (notif && (notif.type ?? "toast") === "notification") {
6604
+ setBanner(true);
6605
+ onSuccess?.(updated);
6606
+ } else {
6607
+ onSuccess?.(updated);
6608
+ onClose();
6440
6609
  }
6441
- onSuccess?.({ ...item, ...form });
6442
- onClose();
6443
6610
  } catch (err) {
6444
6611
  setError(err?.response?.data?.message ?? err.message ?? "Update failed");
6445
6612
  } finally {
@@ -6491,7 +6658,6 @@ function DGDeleteModal({
6491
6658
  onSuccess,
6492
6659
  notif
6493
6660
  }) {
6494
- const { toast } = useToast();
6495
6661
  const [loading, setLoading] = React28.useState(false);
6496
6662
  const [error, setError] = React28.useState(null);
6497
6663
  const handleDelete = async () => {
@@ -6499,11 +6665,6 @@ function DGDeleteModal({
6499
6665
  setError(null);
6500
6666
  try {
6501
6667
  await import_axios3.default.delete(`${baseUrl}/${itemId}/delete`);
6502
- if (notif) {
6503
- if ((notif.type ?? "toast") === "toast") {
6504
- toast({ variant: notif.deleteVariant ?? "success", title: notif.deleteTitle ?? "Record deleted", description: notif.deleteBody, position: notif.toastPosition });
6505
- }
6506
- }
6507
6668
  onSuccess?.(item);
6508
6669
  onClose();
6509
6670
  } catch (err) {
@@ -6548,6 +6709,7 @@ function DataGrid({
6548
6709
  defaultActions,
6549
6710
  serverPagination
6550
6711
  }) {
6712
+ const { toast } = useToast();
6551
6713
  const [sortKey, setSortKey] = React28.useState(null);
6552
6714
  const [sortDir, setSortDir] = React28.useState(null);
6553
6715
  const [filters, setFilters] = React28.useState({});
@@ -6871,6 +7033,10 @@ function DataGrid({
6871
7033
  onSuccess: (updated) => {
6872
7034
  setTableData((prev) => prev.map((r) => String(r[actionIdKey]) === String(updated[actionIdKey]) ? updated : r));
6873
7035
  defaultActions.onSuccess?.("edit", updated);
7036
+ const notif = defaultActions.onSuccessNotif;
7037
+ if (notif && (notif.type ?? "toast") === "toast") {
7038
+ toast({ variant: notif.editVariant ?? "success", title: notif.editTitle ?? "Record updated", description: notif.editBody, position: notif.toastPosition });
7039
+ }
6874
7040
  }
6875
7041
  }
6876
7042
  ),
@@ -6885,6 +7051,10 @@ function DataGrid({
6885
7051
  onSuccess: (deleted) => {
6886
7052
  setTableData((prev) => prev.filter((r) => String(r[actionIdKey]) !== String(deleted[actionIdKey])));
6887
7053
  defaultActions.onSuccess?.("delete", deleted);
7054
+ const notif = defaultActions.onSuccessNotif;
7055
+ if (notif && (notif.type ?? "toast") === "toast") {
7056
+ toast({ variant: notif.deleteVariant ?? "success", title: notif.deleteTitle ?? "Record deleted", description: notif.deleteBody, position: notif.toastPosition });
7057
+ }
6888
7058
  }
6889
7059
  }
6890
7060
  )
@@ -10818,7 +10988,6 @@ function EditModal({
10818
10988
  onSuccess,
10819
10989
  notif
10820
10990
  }) {
10821
- const { toast } = useToast();
10822
10991
  const [form, setForm] = React46.useState(() => {
10823
10992
  const init = {};
10824
10993
  fields.forEach((f) => {
@@ -10835,20 +11004,14 @@ function EditModal({
10835
11004
  setError(null);
10836
11005
  try {
10837
11006
  await import_axios4.default.put(`${baseUrl}/${itemId}/update`, form);
10838
- if (notif) {
10839
- if ((notif.type ?? "toast") === "toast") {
10840
- toast({
10841
- variant: notif.editVariant ?? "success",
10842
- title: notif.editTitle ?? "Record updated",
10843
- description: notif.editBody,
10844
- position: notif.toastPosition
10845
- });
10846
- } else {
10847
- setBanner(true);
10848
- }
11007
+ const updated = { ...item, ...form };
11008
+ if (notif && (notif.type ?? "toast") === "notification") {
11009
+ setBanner(true);
11010
+ onSuccess?.(updated);
11011
+ } else {
11012
+ onSuccess?.(updated);
11013
+ onClose();
10849
11014
  }
10850
- onSuccess?.({ ...item, ...form });
10851
- onClose();
10852
11015
  } catch (err) {
10853
11016
  setError(err?.response?.data?.message ?? err.message ?? "Update failed");
10854
11017
  } finally {
@@ -10907,7 +11070,6 @@ function DeleteModal({
10907
11070
  onSuccess,
10908
11071
  notif
10909
11072
  }) {
10910
- const { toast } = useToast();
10911
11073
  const [loading, setLoading] = React46.useState(false);
10912
11074
  const [error, setError] = React46.useState(null);
10913
11075
  const handleDelete = async () => {
@@ -10915,16 +11077,6 @@ function DeleteModal({
10915
11077
  setError(null);
10916
11078
  try {
10917
11079
  await import_axios4.default.delete(`${baseUrl}/${itemId}/delete`);
10918
- if (notif) {
10919
- if ((notif.type ?? "toast") === "toast") {
10920
- toast({
10921
- variant: notif.deleteVariant ?? "success",
10922
- title: notif.deleteTitle ?? "Record deleted",
10923
- description: notif.deleteBody,
10924
- position: notif.toastPosition
10925
- });
10926
- }
10927
- }
10928
11080
  onSuccess?.(item);
10929
11081
  onClose();
10930
11082
  } catch (err) {
@@ -11010,6 +11162,7 @@ function Table({
11010
11162
  serverPagination,
11011
11163
  className
11012
11164
  }) {
11165
+ const { toast } = useToast();
11013
11166
  const [search, setSearch] = React46.useState("");
11014
11167
  const [currentPage, setCurrentPage] = React46.useState(1);
11015
11168
  const [selectedIds, setSelectedIds] = React46.useState([]);
@@ -11431,6 +11584,15 @@ function Table({
11431
11584
  (prev) => prev.map((r) => String(r[actionIdKey]) === String(updated[actionIdKey]) ? updated : r)
11432
11585
  );
11433
11586
  defaultActions.onSuccess?.("edit", updated);
11587
+ const notif = defaultActions.onSuccessNotif;
11588
+ if (notif && (notif.type ?? "toast") === "toast") {
11589
+ toast({
11590
+ variant: notif.editVariant ?? "success",
11591
+ title: notif.editTitle ?? "Record updated",
11592
+ description: notif.editBody,
11593
+ position: notif.toastPosition
11594
+ });
11595
+ }
11434
11596
  }
11435
11597
  }
11436
11598
  ),
@@ -11447,6 +11609,15 @@ function Table({
11447
11609
  (prev) => prev.filter((r) => String(r[actionIdKey]) !== String(deleted[actionIdKey]))
11448
11610
  );
11449
11611
  defaultActions.onSuccess?.("delete", deleted);
11612
+ const notif = defaultActions.onSuccessNotif;
11613
+ if (notif && (notif.type ?? "toast") === "toast") {
11614
+ toast({
11615
+ variant: notif.deleteVariant ?? "success",
11616
+ title: notif.deleteTitle ?? "Record deleted",
11617
+ description: notif.deleteBody,
11618
+ position: notif.toastPosition
11619
+ });
11620
+ }
11450
11621
  }
11451
11622
  }
11452
11623
  )
package/dist/index.d.cts CHANGED
@@ -468,6 +468,13 @@ interface BulletinPreviewProps {
468
468
  customActions?: BulletinAction[];
469
469
  }
470
470
  declare function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions }: BulletinPreviewProps): React.ReactPortal;
471
+ interface BulletinEditField {
472
+ key: keyof BulletinItem | string;
473
+ label: string;
474
+ type: "text" | "textarea" | "select" | "checkbox" | "date";
475
+ options?: string[];
476
+ placeholder?: string;
477
+ }
471
478
  interface BulletinBoardProps {
472
479
  /** Array of bulletin post items. */
473
480
  items: BulletinItem[];
@@ -509,6 +516,8 @@ interface BulletinBoardProps {
509
516
  editMethod?: "PUT" | "PATCH" | "POST";
510
517
  /** Item key used as the id segment in the edit URL. */
511
518
  editIdKey?: string;
519
+ /** Edit form fields configuration. */
520
+ editFields?: BulletinEditField[];
512
521
  /** Base URL for built-in DELETE {baseUrl}/{id}/delete request. */
513
522
  deleteBaseUrl?: string;
514
523
  /** Item key used as the id segment in the delete URL. */
@@ -520,7 +529,7 @@ interface BulletinBoardProps {
520
529
  /** Additional CSS classes on the outer wrapper. */
521
530
  className?: string;
522
531
  }
523
- declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, deleteBaseUrl, deleteIdKey, serverPagination, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
532
+ declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, editBaseUrl, editMethod, editIdKey, editFields, deleteBaseUrl, deleteIdKey, serverPagination, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
524
533
 
525
534
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
526
535
  variant?: "primary" | "secondary" | "outline" | "ghost" | "link" | "danger" | "success" | "destructive";
@@ -2018,4 +2027,4 @@ interface WizardProps {
2018
2027
  }
2019
2028
  declare function Wizard({ steps, step: controlledStep, defaultStep, onStepChange, onFinish, onClose, layout, variant, size, isOpen, showClose, unchange, title, description, hideHeader, footer, renderActions, backLabel, nextLabel, finishLabel, cancelLabel, showCancel, showBackOnFirst, loading, clickableSteps, className, contentClassName, }: WizardProps): react_jsx_runtime.JSX.Element;
2020
2029
 
2021
- export { Accordion, type AccordionItem, type AccordionProps, type AccordionVariant, type ActionField, type ActionFieldType, type AuthField, type AuthVariant, type AuthView, Authentication, type AuthenticationProps, AvatarStack, type AvatarStackProps, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BulletinAction, BulletinBoard, type BulletinBoardProps, type BulletinColumns, type BulletinItem, type BulletinLayout, BulletinPreview, type BulletinPreviewProps, type BulletinPriority, type BulletinServerPaginationProp, type BulletinVariant, Button, type ButtonProps, COLOR_PALETTE, Calendar, CalendarDateRangePicker, type CalendarDateRangePickerProps, type CalendarDateRangeVariant, type CalendarEvent, type CalendarProps, type CalendarView, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChartDataPoint, ChartWidget, type ChartWidgetProps, Checkbox, type CheckboxProps, CircularProgress, type CircularProgressProps, type ClusterVariant, ColorPicker, type ColorPickerProps, type Column, Combobox, type ComboboxOption, type ComboboxProps, type CommandItem, CommandPalette, type CommandPaletteProps, ComposableWidget, type ComposableWidgetProps, type ConfirmVariant, ContextMenu, type ContextMenuItem, type ContextMenuProps, DataGrid, type DataGridColumn, type DataGridProps, DatePickerPopup, type DateRange, DateRangePicker, type DateRangePickerProps, type DefaultActionsConfig, DocsLayout, Drawer, type DrawerProps, type DrawerSide, Dropdown, DropdownItem, DropdownLabel, type DropdownProps, DropdownSeparator, EVENT_COLORS, type FileTypeValidation, FileUpload, type FileUploadProps, type FlexAlign, type FlexDirection, type FlexGap, FlexItem, type FlexItemProps, type FlexJustify, FlexLayout, type FlexLayoutProps, type FlexWrap, type FlyToOptions, type FormField, type FormFieldType, type GridAlign, type GridCols, type GridGap, GridItem, type GridItemProps, GridLayout, type GridLayoutProps, GroupNavigation, type GroupNavigationProps, type ImageEditorMode, type ImageEditorOptions, Input, type InputProps, KanbanBoard, type KanbanBoardProps, type KanbanCard, type KanbanColumn, Label, LeafletMap, type LeafletMapProps, LeftSidebar, type LeftSidebarProps, type MapLibreClusterVariant, MapLibreMap, type MapLibreMarker, type MapLibreProps, type MapLibreRoute, type MapLibreRouteType, type MapLibreStyle, type MapMarker, type MapRoute, type MarkerColor, type MetricItem, MetricRow, type MetricRowProps, Modal, ModalConfirmation, type ModalConfirmationProps, type ModalProps, ModalUnchange, type ModalUnchangeProps, ModalWithForms, type ModalWithFormsProps, type NavGroup, type NavItem, Navigation, type NavigationProps, NotificationBanner, type NotificationBannerProps, type NotificationItem, NotificationPanel, type NotificationPanelProps, type NotificationVariant, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Panel, type PanelProps, PanelSettings, type PanelSettingsProps, type PanelSettingsTab, PanelSidebarGroup, PanelSidebarItem, Popover, type PopoverPlacement, type PopoverProps, Progress, type ProgressProps, type ProgressSize, type ProgressVariant, type PropRow, PropsTable, RadioGroup, type RadioGroupProps, type RadioOption, type RadioSize, type RadioVariant, RangeSlider, type RangeSliderProps, Repeater, type RepeaterProps, ResizablePanels, type ResizablePanelsProps, RichTextEditor, type RichTextEditorProps, RightSidebar, type RightSidebarProps, type RouteType, ScrollArea, type ScrollAreaProps, Section, SectionBlock, type SectionProps, type SectionVariant, Select, type SelectOption, type SelectProps, type SemanticColor, type ServerDataGridProp, type ServerPagination, type ServerPaginationLink, type ServerPaginationProp, type ServerTableResponse, Skeleton, Slider, type SliderProps, type SortDir, StatCard, type StatCardProps, type StatTrend, StatsWidget, type StatsWidgetProps, type Step, type StepStatus, Stepper, type StepperProps, type TabItem, type TabSize, type TabVariant, Table, TableOfContents, type TableProps, TableWidget, type TableWidgetProps, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ThemeColors, ThemeProvider, type ThemeSettings, Timeline, type TimelineItem, type TimelineProps, type TimelineVariant, type ToastItem, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, type TocItem, TocProvider, ToggleSwitch, type ToggleSwitchProps, Tooltip, type TooltipProps, Topbar, type TopbarProps, type TreeNode, TreeView, type TreeViewProps, type TrendDir, type UseServerBulletinOptions, type UseServerBulletinReturn, type UseServerDataGridOptions, type UseServerDataGridReturn, type UseServerTableOptions, type UseServerTableReturn, Widget, type WidgetProps, Wizard, type WizardActionProps, type WizardLayout, type WizardProps, type WizardSize, type WizardStep, type WizardVariant, useServerBulletin, useServerDataGrid, useServerTable, useTheme, useToast, useToc };
2030
+ export { Accordion, type AccordionItem, type AccordionProps, type AccordionVariant, type ActionField, type ActionFieldType, type AuthField, type AuthVariant, type AuthView, Authentication, type AuthenticationProps, AvatarStack, type AvatarStackProps, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BulletinAction, BulletinBoard, type BulletinBoardProps, type BulletinColumns, type BulletinEditField, type BulletinItem, type BulletinLayout, BulletinPreview, type BulletinPreviewProps, type BulletinPriority, type BulletinServerPaginationProp, type BulletinVariant, Button, type ButtonProps, COLOR_PALETTE, Calendar, CalendarDateRangePicker, type CalendarDateRangePickerProps, type CalendarDateRangeVariant, type CalendarEvent, type CalendarProps, type CalendarView, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChartDataPoint, ChartWidget, type ChartWidgetProps, Checkbox, type CheckboxProps, CircularProgress, type CircularProgressProps, type ClusterVariant, ColorPicker, type ColorPickerProps, type Column, Combobox, type ComboboxOption, type ComboboxProps, type CommandItem, CommandPalette, type CommandPaletteProps, ComposableWidget, type ComposableWidgetProps, type ConfirmVariant, ContextMenu, type ContextMenuItem, type ContextMenuProps, DataGrid, type DataGridColumn, type DataGridProps, DatePickerPopup, type DateRange, DateRangePicker, type DateRangePickerProps, type DefaultActionsConfig, DocsLayout, Drawer, type DrawerProps, type DrawerSide, Dropdown, DropdownItem, DropdownLabel, type DropdownProps, DropdownSeparator, EVENT_COLORS, type FileTypeValidation, FileUpload, type FileUploadProps, type FlexAlign, type FlexDirection, type FlexGap, FlexItem, type FlexItemProps, type FlexJustify, FlexLayout, type FlexLayoutProps, type FlexWrap, type FlyToOptions, type FormField, type FormFieldType, type GridAlign, type GridCols, type GridGap, GridItem, type GridItemProps, GridLayout, type GridLayoutProps, GroupNavigation, type GroupNavigationProps, type ImageEditorMode, type ImageEditorOptions, Input, type InputProps, KanbanBoard, type KanbanBoardProps, type KanbanCard, type KanbanColumn, Label, LeafletMap, type LeafletMapProps, LeftSidebar, type LeftSidebarProps, type MapLibreClusterVariant, MapLibreMap, type MapLibreMarker, type MapLibreProps, type MapLibreRoute, type MapLibreRouteType, type MapLibreStyle, type MapMarker, type MapRoute, type MarkerColor, type MetricItem, MetricRow, type MetricRowProps, Modal, ModalConfirmation, type ModalConfirmationProps, type ModalProps, ModalUnchange, type ModalUnchangeProps, ModalWithForms, type ModalWithFormsProps, type NavGroup, type NavItem, Navigation, type NavigationProps, NotificationBanner, type NotificationBannerProps, type NotificationItem, NotificationPanel, type NotificationPanelProps, type NotificationVariant, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Panel, type PanelProps, PanelSettings, type PanelSettingsProps, type PanelSettingsTab, PanelSidebarGroup, PanelSidebarItem, Popover, type PopoverPlacement, type PopoverProps, Progress, type ProgressProps, type ProgressSize, type ProgressVariant, type PropRow, PropsTable, RadioGroup, type RadioGroupProps, type RadioOption, type RadioSize, type RadioVariant, RangeSlider, type RangeSliderProps, Repeater, type RepeaterProps, ResizablePanels, type ResizablePanelsProps, RichTextEditor, type RichTextEditorProps, RightSidebar, type RightSidebarProps, type RouteType, ScrollArea, type ScrollAreaProps, Section, SectionBlock, type SectionProps, type SectionVariant, Select, type SelectOption, type SelectProps, type SemanticColor, type ServerDataGridProp, type ServerPagination, type ServerPaginationLink, type ServerPaginationProp, type ServerTableResponse, Skeleton, Slider, type SliderProps, type SortDir, StatCard, type StatCardProps, type StatTrend, StatsWidget, type StatsWidgetProps, type Step, type StepStatus, Stepper, type StepperProps, type TabItem, type TabSize, type TabVariant, Table, TableOfContents, type TableProps, TableWidget, type TableWidgetProps, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ThemeColors, ThemeProvider, type ThemeSettings, Timeline, type TimelineItem, type TimelineProps, type TimelineVariant, type ToastItem, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, type TocItem, TocProvider, ToggleSwitch, type ToggleSwitchProps, Tooltip, type TooltipProps, Topbar, type TopbarProps, type TreeNode, TreeView, type TreeViewProps, type TrendDir, type UseServerBulletinOptions, type UseServerBulletinReturn, type UseServerDataGridOptions, type UseServerDataGridReturn, type UseServerTableOptions, type UseServerTableReturn, Widget, type WidgetProps, Wizard, type WizardActionProps, type WizardLayout, type WizardProps, type WizardSize, type WizardStep, type WizardVariant, useServerBulletin, useServerDataGrid, useServerTable, useTheme, useToast, useToc };
package/dist/index.d.ts CHANGED
@@ -468,6 +468,13 @@ interface BulletinPreviewProps {
468
468
  customActions?: BulletinAction[];
469
469
  }
470
470
  declare function BulletinPreview({ item, onClose, onEdit, onDelete, onView, customActions }: BulletinPreviewProps): React.ReactPortal;
471
+ interface BulletinEditField {
472
+ key: keyof BulletinItem | string;
473
+ label: string;
474
+ type: "text" | "textarea" | "select" | "checkbox" | "date";
475
+ options?: string[];
476
+ placeholder?: string;
477
+ }
471
478
  interface BulletinBoardProps {
472
479
  /** Array of bulletin post items. */
473
480
  items: BulletinItem[];
@@ -509,6 +516,8 @@ interface BulletinBoardProps {
509
516
  editMethod?: "PUT" | "PATCH" | "POST";
510
517
  /** Item key used as the id segment in the edit URL. */
511
518
  editIdKey?: string;
519
+ /** Edit form fields configuration. */
520
+ editFields?: BulletinEditField[];
512
521
  /** Base URL for built-in DELETE {baseUrl}/{id}/delete request. */
513
522
  deleteBaseUrl?: string;
514
523
  /** Item key used as the id segment in the delete URL. */
@@ -520,7 +529,7 @@ interface BulletinBoardProps {
520
529
  /** Additional CSS classes on the outer wrapper. */
521
530
  className?: string;
522
531
  }
523
- declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, deleteBaseUrl, deleteIdKey, serverPagination, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
532
+ declare function BulletinBoard({ items, layout, columns, variant, searchable, filterable, categories: categoriesProp, title, headerAction, showHeader, emptyMessage, loading, loadingCount, onItemClick, onView, onEdit, onDelete, preview, editBaseUrl, editMethod, editIdKey, editFields, deleteBaseUrl, deleteIdKey, serverPagination, className, }: BulletinBoardProps): react_jsx_runtime.JSX.Element;
524
533
 
525
534
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
526
535
  variant?: "primary" | "secondary" | "outline" | "ghost" | "link" | "danger" | "success" | "destructive";
@@ -2018,4 +2027,4 @@ interface WizardProps {
2018
2027
  }
2019
2028
  declare function Wizard({ steps, step: controlledStep, defaultStep, onStepChange, onFinish, onClose, layout, variant, size, isOpen, showClose, unchange, title, description, hideHeader, footer, renderActions, backLabel, nextLabel, finishLabel, cancelLabel, showCancel, showBackOnFirst, loading, clickableSteps, className, contentClassName, }: WizardProps): react_jsx_runtime.JSX.Element;
2020
2029
 
2021
- export { Accordion, type AccordionItem, type AccordionProps, type AccordionVariant, type ActionField, type ActionFieldType, type AuthField, type AuthVariant, type AuthView, Authentication, type AuthenticationProps, AvatarStack, type AvatarStackProps, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BulletinAction, BulletinBoard, type BulletinBoardProps, type BulletinColumns, type BulletinItem, type BulletinLayout, BulletinPreview, type BulletinPreviewProps, type BulletinPriority, type BulletinServerPaginationProp, type BulletinVariant, Button, type ButtonProps, COLOR_PALETTE, Calendar, CalendarDateRangePicker, type CalendarDateRangePickerProps, type CalendarDateRangeVariant, type CalendarEvent, type CalendarProps, type CalendarView, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChartDataPoint, ChartWidget, type ChartWidgetProps, Checkbox, type CheckboxProps, CircularProgress, type CircularProgressProps, type ClusterVariant, ColorPicker, type ColorPickerProps, type Column, Combobox, type ComboboxOption, type ComboboxProps, type CommandItem, CommandPalette, type CommandPaletteProps, ComposableWidget, type ComposableWidgetProps, type ConfirmVariant, ContextMenu, type ContextMenuItem, type ContextMenuProps, DataGrid, type DataGridColumn, type DataGridProps, DatePickerPopup, type DateRange, DateRangePicker, type DateRangePickerProps, type DefaultActionsConfig, DocsLayout, Drawer, type DrawerProps, type DrawerSide, Dropdown, DropdownItem, DropdownLabel, type DropdownProps, DropdownSeparator, EVENT_COLORS, type FileTypeValidation, FileUpload, type FileUploadProps, type FlexAlign, type FlexDirection, type FlexGap, FlexItem, type FlexItemProps, type FlexJustify, FlexLayout, type FlexLayoutProps, type FlexWrap, type FlyToOptions, type FormField, type FormFieldType, type GridAlign, type GridCols, type GridGap, GridItem, type GridItemProps, GridLayout, type GridLayoutProps, GroupNavigation, type GroupNavigationProps, type ImageEditorMode, type ImageEditorOptions, Input, type InputProps, KanbanBoard, type KanbanBoardProps, type KanbanCard, type KanbanColumn, Label, LeafletMap, type LeafletMapProps, LeftSidebar, type LeftSidebarProps, type MapLibreClusterVariant, MapLibreMap, type MapLibreMarker, type MapLibreProps, type MapLibreRoute, type MapLibreRouteType, type MapLibreStyle, type MapMarker, type MapRoute, type MarkerColor, type MetricItem, MetricRow, type MetricRowProps, Modal, ModalConfirmation, type ModalConfirmationProps, type ModalProps, ModalUnchange, type ModalUnchangeProps, ModalWithForms, type ModalWithFormsProps, type NavGroup, type NavItem, Navigation, type NavigationProps, NotificationBanner, type NotificationBannerProps, type NotificationItem, NotificationPanel, type NotificationPanelProps, type NotificationVariant, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Panel, type PanelProps, PanelSettings, type PanelSettingsProps, type PanelSettingsTab, PanelSidebarGroup, PanelSidebarItem, Popover, type PopoverPlacement, type PopoverProps, Progress, type ProgressProps, type ProgressSize, type ProgressVariant, type PropRow, PropsTable, RadioGroup, type RadioGroupProps, type RadioOption, type RadioSize, type RadioVariant, RangeSlider, type RangeSliderProps, Repeater, type RepeaterProps, ResizablePanels, type ResizablePanelsProps, RichTextEditor, type RichTextEditorProps, RightSidebar, type RightSidebarProps, type RouteType, ScrollArea, type ScrollAreaProps, Section, SectionBlock, type SectionProps, type SectionVariant, Select, type SelectOption, type SelectProps, type SemanticColor, type ServerDataGridProp, type ServerPagination, type ServerPaginationLink, type ServerPaginationProp, type ServerTableResponse, Skeleton, Slider, type SliderProps, type SortDir, StatCard, type StatCardProps, type StatTrend, StatsWidget, type StatsWidgetProps, type Step, type StepStatus, Stepper, type StepperProps, type TabItem, type TabSize, type TabVariant, Table, TableOfContents, type TableProps, TableWidget, type TableWidgetProps, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ThemeColors, ThemeProvider, type ThemeSettings, Timeline, type TimelineItem, type TimelineProps, type TimelineVariant, type ToastItem, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, type TocItem, TocProvider, ToggleSwitch, type ToggleSwitchProps, Tooltip, type TooltipProps, Topbar, type TopbarProps, type TreeNode, TreeView, type TreeViewProps, type TrendDir, type UseServerBulletinOptions, type UseServerBulletinReturn, type UseServerDataGridOptions, type UseServerDataGridReturn, type UseServerTableOptions, type UseServerTableReturn, Widget, type WidgetProps, Wizard, type WizardActionProps, type WizardLayout, type WizardProps, type WizardSize, type WizardStep, type WizardVariant, useServerBulletin, useServerDataGrid, useServerTable, useTheme, useToast, useToc };
2030
+ export { Accordion, type AccordionItem, type AccordionProps, type AccordionVariant, type ActionField, type ActionFieldType, type AuthField, type AuthVariant, type AuthView, Authentication, type AuthenticationProps, AvatarStack, type AvatarStackProps, Badge, type BadgeProps, type BadgeSize, type BadgeVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BulletinAction, BulletinBoard, type BulletinBoardProps, type BulletinColumns, type BulletinEditField, type BulletinItem, type BulletinLayout, BulletinPreview, type BulletinPreviewProps, type BulletinPriority, type BulletinServerPaginationProp, type BulletinVariant, Button, type ButtonProps, COLOR_PALETTE, Calendar, CalendarDateRangePicker, type CalendarDateRangePickerProps, type CalendarDateRangeVariant, type CalendarEvent, type CalendarProps, type CalendarView, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChartDataPoint, ChartWidget, type ChartWidgetProps, Checkbox, type CheckboxProps, CircularProgress, type CircularProgressProps, type ClusterVariant, ColorPicker, type ColorPickerProps, type Column, Combobox, type ComboboxOption, type ComboboxProps, type CommandItem, CommandPalette, type CommandPaletteProps, ComposableWidget, type ComposableWidgetProps, type ConfirmVariant, ContextMenu, type ContextMenuItem, type ContextMenuProps, DataGrid, type DataGridColumn, type DataGridProps, DatePickerPopup, type DateRange, DateRangePicker, type DateRangePickerProps, type DefaultActionsConfig, DocsLayout, Drawer, type DrawerProps, type DrawerSide, Dropdown, DropdownItem, DropdownLabel, type DropdownProps, DropdownSeparator, EVENT_COLORS, type FileTypeValidation, FileUpload, type FileUploadProps, type FlexAlign, type FlexDirection, type FlexGap, FlexItem, type FlexItemProps, type FlexJustify, FlexLayout, type FlexLayoutProps, type FlexWrap, type FlyToOptions, type FormField, type FormFieldType, type GridAlign, type GridCols, type GridGap, GridItem, type GridItemProps, GridLayout, type GridLayoutProps, GroupNavigation, type GroupNavigationProps, type ImageEditorMode, type ImageEditorOptions, Input, type InputProps, KanbanBoard, type KanbanBoardProps, type KanbanCard, type KanbanColumn, Label, LeafletMap, type LeafletMapProps, LeftSidebar, type LeftSidebarProps, type MapLibreClusterVariant, MapLibreMap, type MapLibreMarker, type MapLibreProps, type MapLibreRoute, type MapLibreRouteType, type MapLibreStyle, type MapMarker, type MapRoute, type MarkerColor, type MetricItem, MetricRow, type MetricRowProps, Modal, ModalConfirmation, type ModalConfirmationProps, type ModalProps, ModalUnchange, type ModalUnchangeProps, ModalWithForms, type ModalWithFormsProps, type NavGroup, type NavItem, Navigation, type NavigationProps, NotificationBanner, type NotificationBannerProps, type NotificationItem, NotificationPanel, type NotificationPanelProps, type NotificationVariant, OtpInput, type OtpInputProps, Pagination, type PaginationProps, Panel, type PanelProps, PanelSettings, type PanelSettingsProps, type PanelSettingsTab, PanelSidebarGroup, PanelSidebarItem, Popover, type PopoverPlacement, type PopoverProps, Progress, type ProgressProps, type ProgressSize, type ProgressVariant, type PropRow, PropsTable, RadioGroup, type RadioGroupProps, type RadioOption, type RadioSize, type RadioVariant, RangeSlider, type RangeSliderProps, Repeater, type RepeaterProps, ResizablePanels, type ResizablePanelsProps, RichTextEditor, type RichTextEditorProps, RightSidebar, type RightSidebarProps, type RouteType, ScrollArea, type ScrollAreaProps, Section, SectionBlock, type SectionProps, type SectionVariant, Select, type SelectOption, type SelectProps, type SemanticColor, type ServerDataGridProp, type ServerPagination, type ServerPaginationLink, type ServerPaginationProp, type ServerTableResponse, Skeleton, Slider, type SliderProps, type SortDir, StatCard, type StatCardProps, type StatTrend, StatsWidget, type StatsWidgetProps, type Step, type StepStatus, Stepper, type StepperProps, type TabItem, type TabSize, type TabVariant, Table, TableOfContents, type TableProps, TableWidget, type TableWidgetProps, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ThemeColors, ThemeProvider, type ThemeSettings, Timeline, type TimelineItem, type TimelineProps, type TimelineVariant, type ToastItem, type ToastPosition, ToastProvider, type ToastProviderProps, type ToastVariant, type TocItem, TocProvider, ToggleSwitch, type ToggleSwitchProps, Tooltip, type TooltipProps, Topbar, type TopbarProps, type TreeNode, TreeView, type TreeViewProps, type TrendDir, type UseServerBulletinOptions, type UseServerBulletinReturn, type UseServerDataGridOptions, type UseServerDataGridReturn, type UseServerTableOptions, type UseServerTableReturn, Widget, type WidgetProps, Wizard, type WizardActionProps, type WizardLayout, type WizardProps, type WizardSize, type WizardStep, type WizardVariant, useServerBulletin, useServerDataGrid, useServerTable, useTheme, useToast, useToc };
@@ -65076,6 +65076,153 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
65076
65076
  document.body
65077
65077
  );
65078
65078
  }
65079
+ function BulletinEditModal({
65080
+ item,
65081
+ baseUrl,
65082
+ idKey,
65083
+ method,
65084
+ fields,
65085
+ onClose,
65086
+ onSuccess
65087
+ }) {
65088
+ const [loading, setLoading] = React8.useState(false);
65089
+ const [error, setError] = React8.useState(null);
65090
+ const [formData, setFormData] = React8.useState(() => {
65091
+ const initial = {};
65092
+ fields.forEach((f) => {
65093
+ initial[f.key] = item[f.key] ?? "";
65094
+ });
65095
+ return initial;
65096
+ });
65097
+ const id = item[idKey ?? "id"];
65098
+ const handleChange = (key, value) => {
65099
+ setFormData((prev) => ({ ...prev, [key]: value }));
65100
+ };
65101
+ const handleSubmit = async (e) => {
65102
+ e.preventDefault();
65103
+ setLoading(true);
65104
+ setError(null);
65105
+ try {
65106
+ const response = await axios_default({
65107
+ method: method ?? "PUT",
65108
+ url: `${baseUrl}/${id}`,
65109
+ data: formData
65110
+ });
65111
+ onSuccess?.(response.data);
65112
+ onClose();
65113
+ } catch (err) {
65114
+ setError(err?.response?.data?.message ?? err.message ?? "Update failed");
65115
+ } finally {
65116
+ setLoading(false);
65117
+ }
65118
+ };
65119
+ return (0, import_react_dom.createPortal)(
65120
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65121
+ "div",
65122
+ {
65123
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
65124
+ style: { background: "rgba(0,0,0,0.55)" },
65125
+ onMouseDown: (e) => {
65126
+ if (e.target === e.currentTarget) onClose();
65127
+ },
65128
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl p-6 space-y-4 max-h-[90vh] overflow-y-auto", children: [
65129
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
65130
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "text-base font-semibold", children: "Edit Post" }),
65131
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65132
+ "button",
65133
+ {
65134
+ type: "button",
65135
+ onClick: onClose,
65136
+ className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground hover:bg-accent hover:text-foreground transition-colors",
65137
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(X, { className: "h-4 w-4" })
65138
+ }
65139
+ )
65140
+ ] }),
65141
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
65142
+ fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-1.5", children: [
65143
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
65144
+ field.type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65145
+ "textarea",
65146
+ {
65147
+ value: formData[field.key] ?? "",
65148
+ onChange: (e) => handleChange(field.key, e.target.value),
65149
+ placeholder: field.placeholder,
65150
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring resize-none",
65151
+ rows: 4
65152
+ }
65153
+ ) : field.type === "select" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
65154
+ "select",
65155
+ {
65156
+ value: formData[field.key] ?? "",
65157
+ onChange: (e) => handleChange(field.key, e.target.value),
65158
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring",
65159
+ children: [
65160
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: "Select..." }),
65161
+ field.options?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: opt, children: opt }, opt))
65162
+ ]
65163
+ }
65164
+ ) : field.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
65165
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65166
+ "input",
65167
+ {
65168
+ type: "checkbox",
65169
+ checked: formData[field.key] ?? false,
65170
+ onChange: (e) => handleChange(field.key, e.target.checked),
65171
+ className: "h-4 w-4 rounded border-border"
65172
+ }
65173
+ ),
65174
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-sm text-muted-foreground", children: field.placeholder })
65175
+ ] }) : field.type === "date" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65176
+ "input",
65177
+ {
65178
+ type: "date",
65179
+ value: formData[field.key] ?? "",
65180
+ onChange: (e) => handleChange(field.key, e.target.value),
65181
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring"
65182
+ }
65183
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65184
+ "input",
65185
+ {
65186
+ type: "text",
65187
+ value: formData[field.key] ?? "",
65188
+ onChange: (e) => handleChange(field.key, e.target.value),
65189
+ placeholder: field.placeholder,
65190
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring"
65191
+ }
65192
+ )
65193
+ ] }, field.key)),
65194
+ error && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-xs text-danger", children: error }),
65195
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-end gap-2 pt-2", children: [
65196
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65197
+ "button",
65198
+ {
65199
+ type: "button",
65200
+ onClick: onClose,
65201
+ disabled: loading,
65202
+ className: "px-4 py-1.5 text-sm rounded-xl border border-border hover:bg-accent transition-colors",
65203
+ children: "Cancel"
65204
+ }
65205
+ ),
65206
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
65207
+ "button",
65208
+ {
65209
+ type: "submit",
65210
+ disabled: loading,
65211
+ className: "px-4 py-1.5 text-sm rounded-xl bg-primary text-primary-foreground hover:bg-primary/90 transition-colors flex items-center gap-1.5",
65212
+ children: [
65213
+ loading && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }),
65214
+ loading ? "Saving\u2026" : "Save"
65215
+ ]
65216
+ }
65217
+ )
65218
+ ] })
65219
+ ] })
65220
+ ] })
65221
+ }
65222
+ ),
65223
+ document.body
65224
+ );
65225
+ }
65079
65226
  function ActionMenu({ item, actions }) {
65080
65227
  const [open, setOpen] = React8.useState(false);
65081
65228
  const ref = React8.useRef(null);
@@ -65225,12 +65372,17 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
65225
65372
  onEdit,
65226
65373
  onDelete,
65227
65374
  preview = false,
65375
+ editBaseUrl,
65376
+ editMethod,
65377
+ editIdKey,
65378
+ editFields,
65228
65379
  deleteBaseUrl,
65229
65380
  deleteIdKey = "id",
65230
65381
  serverPagination,
65231
65382
  className
65232
65383
  }) {
65233
65384
  const [previewItem, setPreviewItem] = React8.useState(null);
65385
+ const [editItem, setEditItem] = React8.useState(null);
65234
65386
  const [deleteItem, setDeleteItem] = React8.useState(null);
65235
65387
  const [search, setSearch] = React8.useState("");
65236
65388
  const [category, setCategory] = React8.useState(null);
@@ -65376,7 +65528,10 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
65376
65528
  onView: onView ? (item) => {
65377
65529
  onView(item);
65378
65530
  } : void 0,
65379
- onEdit: onEdit ? (item) => {
65531
+ onEdit: editBaseUrl && editFields ? (item) => {
65532
+ setPreviewItem(null);
65533
+ setEditItem(item);
65534
+ } : onEdit ? (item) => {
65380
65535
  onEdit(item);
65381
65536
  } : void 0,
65382
65537
  onDelete: deleteBaseUrl ? (item) => {
@@ -65387,6 +65542,20 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
65387
65542
  } : void 0
65388
65543
  }
65389
65544
  ),
65545
+ editItem && editBaseUrl && editFields && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65546
+ BulletinEditModal,
65547
+ {
65548
+ item: editItem,
65549
+ baseUrl: editBaseUrl,
65550
+ idKey: editIdKey,
65551
+ method: editMethod,
65552
+ fields: editFields,
65553
+ onClose: () => setEditItem(null),
65554
+ onSuccess: (updated) => {
65555
+ onEdit?.(updated);
65556
+ }
65557
+ }
65558
+ ),
65390
65559
  deleteItem && deleteBaseUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
65391
65560
  BulletinDeleteConfirm,
65392
65561
  {
@@ -69562,7 +69731,6 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
69562
69731
  onSuccess,
69563
69732
  notif
69564
69733
  }) {
69565
- const { toast } = useToast();
69566
69734
  const [form, setForm] = React28.useState(() => {
69567
69735
  const init = {};
69568
69736
  fields.forEach((f) => {
@@ -69579,15 +69747,14 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
69579
69747
  setError(null);
69580
69748
  try {
69581
69749
  await axios_default.put(`${baseUrl}/${itemId}/update`, form);
69582
- if (notif) {
69583
- if ((notif.type ?? "toast") === "toast") {
69584
- toast({ variant: notif.editVariant ?? "success", title: notif.editTitle ?? "Record updated", description: notif.editBody, position: notif.toastPosition });
69585
- } else {
69586
- setBanner(true);
69587
- }
69750
+ const updated = { ...item, ...form };
69751
+ if (notif && (notif.type ?? "toast") === "notification") {
69752
+ setBanner(true);
69753
+ onSuccess?.(updated);
69754
+ } else {
69755
+ onSuccess?.(updated);
69756
+ onClose();
69588
69757
  }
69589
- onSuccess?.({ ...item, ...form });
69590
- onClose();
69591
69758
  } catch (err) {
69592
69759
  setError(err?.response?.data?.message ?? err.message ?? "Update failed");
69593
69760
  } finally {
@@ -69639,7 +69806,6 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
69639
69806
  onSuccess,
69640
69807
  notif
69641
69808
  }) {
69642
- const { toast } = useToast();
69643
69809
  const [loading, setLoading] = React28.useState(false);
69644
69810
  const [error, setError] = React28.useState(null);
69645
69811
  const handleDelete = async () => {
@@ -69647,11 +69813,6 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
69647
69813
  setError(null);
69648
69814
  try {
69649
69815
  await axios_default.delete(`${baseUrl}/${itemId}/delete`);
69650
- if (notif) {
69651
- if ((notif.type ?? "toast") === "toast") {
69652
- toast({ variant: notif.deleteVariant ?? "success", title: notif.deleteTitle ?? "Record deleted", description: notif.deleteBody, position: notif.toastPosition });
69653
- }
69654
- }
69655
69816
  onSuccess?.(item);
69656
69817
  onClose();
69657
69818
  } catch (err) {
@@ -69696,6 +69857,7 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
69696
69857
  defaultActions,
69697
69858
  serverPagination
69698
69859
  }) {
69860
+ const { toast } = useToast();
69699
69861
  const [sortKey, setSortKey] = React28.useState(null);
69700
69862
  const [sortDir, setSortDir] = React28.useState(null);
69701
69863
  const [filters, setFilters] = React28.useState({});
@@ -70019,6 +70181,10 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
70019
70181
  onSuccess: (updated) => {
70020
70182
  setTableData((prev) => prev.map((r2) => String(r2[actionIdKey]) === String(updated[actionIdKey]) ? updated : r2));
70021
70183
  defaultActions.onSuccess?.("edit", updated);
70184
+ const notif = defaultActions.onSuccessNotif;
70185
+ if (notif && (notif.type ?? "toast") === "toast") {
70186
+ toast({ variant: notif.editVariant ?? "success", title: notif.editTitle ?? "Record updated", description: notif.editBody, position: notif.toastPosition });
70187
+ }
70022
70188
  }
70023
70189
  }
70024
70190
  ),
@@ -70033,6 +70199,10 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
70033
70199
  onSuccess: (deleted) => {
70034
70200
  setTableData((prev) => prev.filter((r2) => String(r2[actionIdKey]) !== String(deleted[actionIdKey])));
70035
70201
  defaultActions.onSuccess?.("delete", deleted);
70202
+ const notif = defaultActions.onSuccessNotif;
70203
+ if (notif && (notif.type ?? "toast") === "toast") {
70204
+ toast({ variant: notif.deleteVariant ?? "success", title: notif.deleteTitle ?? "Record deleted", description: notif.deleteBody, position: notif.toastPosition });
70205
+ }
70036
70206
  }
70037
70207
  }
70038
70208
  )
@@ -74378,7 +74548,6 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74378
74548
  onSuccess,
74379
74549
  notif
74380
74550
  }) {
74381
- const { toast } = useToast();
74382
74551
  const [form, setForm] = React48.useState(() => {
74383
74552
  const init = {};
74384
74553
  fields.forEach((f) => {
@@ -74395,20 +74564,14 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74395
74564
  setError(null);
74396
74565
  try {
74397
74566
  await axios_default.put(`${baseUrl}/${itemId}/update`, form);
74398
- if (notif) {
74399
- if ((notif.type ?? "toast") === "toast") {
74400
- toast({
74401
- variant: notif.editVariant ?? "success",
74402
- title: notif.editTitle ?? "Record updated",
74403
- description: notif.editBody,
74404
- position: notif.toastPosition
74405
- });
74406
- } else {
74407
- setBanner(true);
74408
- }
74567
+ const updated = { ...item, ...form };
74568
+ if (notif && (notif.type ?? "toast") === "notification") {
74569
+ setBanner(true);
74570
+ onSuccess?.(updated);
74571
+ } else {
74572
+ onSuccess?.(updated);
74573
+ onClose();
74409
74574
  }
74410
- onSuccess?.({ ...item, ...form });
74411
- onClose();
74412
74575
  } catch (err) {
74413
74576
  setError(err?.response?.data?.message ?? err.message ?? "Update failed");
74414
74577
  } finally {
@@ -74467,7 +74630,6 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74467
74630
  onSuccess,
74468
74631
  notif
74469
74632
  }) {
74470
- const { toast } = useToast();
74471
74633
  const [loading, setLoading] = React48.useState(false);
74472
74634
  const [error, setError] = React48.useState(null);
74473
74635
  const handleDelete = async () => {
@@ -74475,16 +74637,6 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74475
74637
  setError(null);
74476
74638
  try {
74477
74639
  await axios_default.delete(`${baseUrl}/${itemId}/delete`);
74478
- if (notif) {
74479
- if ((notif.type ?? "toast") === "toast") {
74480
- toast({
74481
- variant: notif.deleteVariant ?? "success",
74482
- title: notif.deleteTitle ?? "Record deleted",
74483
- description: notif.deleteBody,
74484
- position: notif.toastPosition
74485
- });
74486
- }
74487
- }
74488
74640
  onSuccess?.(item);
74489
74641
  onClose();
74490
74642
  } catch (err) {
@@ -74570,6 +74722,7 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74570
74722
  serverPagination,
74571
74723
  className
74572
74724
  }) {
74725
+ const { toast } = useToast();
74573
74726
  const [search, setSearch] = React48.useState("");
74574
74727
  const [currentPage, setCurrentPage] = React48.useState(1);
74575
74728
  const [selectedIds, setSelectedIds] = React48.useState([]);
@@ -74991,6 +75144,15 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
74991
75144
  (prev) => prev.map((r2) => String(r2[actionIdKey]) === String(updated[actionIdKey]) ? updated : r2)
74992
75145
  );
74993
75146
  defaultActions.onSuccess?.("edit", updated);
75147
+ const notif = defaultActions.onSuccessNotif;
75148
+ if (notif && (notif.type ?? "toast") === "toast") {
75149
+ toast({
75150
+ variant: notif.editVariant ?? "success",
75151
+ title: notif.editTitle ?? "Record updated",
75152
+ description: notif.editBody,
75153
+ position: notif.toastPosition
75154
+ });
75155
+ }
74994
75156
  }
74995
75157
  }
74996
75158
  ),
@@ -75007,6 +75169,15 @@ ${n2.shaderPreludeCode.vertexSource}`, define: n2.shaderDefine }, defaultProject
75007
75169
  (prev) => prev.filter((r2) => String(r2[actionIdKey]) !== String(deleted[actionIdKey]))
75008
75170
  );
75009
75171
  defaultActions.onSuccess?.("delete", deleted);
75172
+ const notif = defaultActions.onSuccessNotif;
75173
+ if (notif && (notif.type ?? "toast") === "toast") {
75174
+ toast({
75175
+ variant: notif.deleteVariant ?? "success",
75176
+ title: notif.deleteTitle ?? "Record deleted",
75177
+ description: notif.deleteBody,
75178
+ position: notif.toastPosition
75179
+ });
75180
+ }
75010
75181
  }
75011
75182
  }
75012
75183
  )
package/dist/index.js CHANGED
@@ -1796,6 +1796,153 @@ function BulletinDeleteConfirm({
1796
1796
  document.body
1797
1797
  );
1798
1798
  }
1799
+ function BulletinEditModal({
1800
+ item,
1801
+ baseUrl,
1802
+ idKey,
1803
+ method,
1804
+ fields,
1805
+ onClose,
1806
+ onSuccess
1807
+ }) {
1808
+ const [loading, setLoading] = React8.useState(false);
1809
+ const [error, setError] = React8.useState(null);
1810
+ const [formData, setFormData] = React8.useState(() => {
1811
+ const initial = {};
1812
+ fields.forEach((f) => {
1813
+ initial[f.key] = item[f.key] ?? "";
1814
+ });
1815
+ return initial;
1816
+ });
1817
+ const id = item[idKey ?? "id"];
1818
+ const handleChange = (key, value) => {
1819
+ setFormData((prev) => ({ ...prev, [key]: value }));
1820
+ };
1821
+ const handleSubmit = async (e) => {
1822
+ e.preventDefault();
1823
+ setLoading(true);
1824
+ setError(null);
1825
+ try {
1826
+ const response = await axios2({
1827
+ method: method ?? "PUT",
1828
+ url: `${baseUrl}/${id}`,
1829
+ data: formData
1830
+ });
1831
+ onSuccess?.(response.data);
1832
+ onClose();
1833
+ } catch (err) {
1834
+ setError(err?.response?.data?.message ?? err.message ?? "Update failed");
1835
+ } finally {
1836
+ setLoading(false);
1837
+ }
1838
+ };
1839
+ return createPortal2(
1840
+ /* @__PURE__ */ jsx10(
1841
+ "div",
1842
+ {
1843
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
1844
+ style: { background: "rgba(0,0,0,0.55)" },
1845
+ onMouseDown: (e) => {
1846
+ if (e.target === e.currentTarget) onClose();
1847
+ },
1848
+ children: /* @__PURE__ */ jsxs9("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl p-6 space-y-4 max-h-[90vh] overflow-y-auto", children: [
1849
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
1850
+ /* @__PURE__ */ jsx10("h2", { className: "text-base font-semibold", children: "Edit Post" }),
1851
+ /* @__PURE__ */ jsx10(
1852
+ "button",
1853
+ {
1854
+ type: "button",
1855
+ onClick: onClose,
1856
+ className: "flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground hover:bg-accent hover:text-foreground transition-colors",
1857
+ children: /* @__PURE__ */ jsx10(X, { className: "h-4 w-4" })
1858
+ }
1859
+ )
1860
+ ] }),
1861
+ /* @__PURE__ */ jsxs9("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
1862
+ fields.map((field) => /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
1863
+ /* @__PURE__ */ jsx10("label", { className: "text-xs font-medium text-muted-foreground", children: field.label }),
1864
+ field.type === "textarea" ? /* @__PURE__ */ jsx10(
1865
+ "textarea",
1866
+ {
1867
+ value: formData[field.key] ?? "",
1868
+ onChange: (e) => handleChange(field.key, e.target.value),
1869
+ placeholder: field.placeholder,
1870
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring resize-none",
1871
+ rows: 4
1872
+ }
1873
+ ) : field.type === "select" ? /* @__PURE__ */ jsxs9(
1874
+ "select",
1875
+ {
1876
+ value: formData[field.key] ?? "",
1877
+ onChange: (e) => handleChange(field.key, e.target.value),
1878
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring",
1879
+ children: [
1880
+ /* @__PURE__ */ jsx10("option", { value: "", children: "Select..." }),
1881
+ field.options?.map((opt) => /* @__PURE__ */ jsx10("option", { value: opt, children: opt }, opt))
1882
+ ]
1883
+ }
1884
+ ) : field.type === "checkbox" ? /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
1885
+ /* @__PURE__ */ jsx10(
1886
+ "input",
1887
+ {
1888
+ type: "checkbox",
1889
+ checked: formData[field.key] ?? false,
1890
+ onChange: (e) => handleChange(field.key, e.target.checked),
1891
+ className: "h-4 w-4 rounded border-border"
1892
+ }
1893
+ ),
1894
+ /* @__PURE__ */ jsx10("span", { className: "text-sm text-muted-foreground", children: field.placeholder })
1895
+ ] }) : field.type === "date" ? /* @__PURE__ */ jsx10(
1896
+ "input",
1897
+ {
1898
+ type: "date",
1899
+ value: formData[field.key] ?? "",
1900
+ onChange: (e) => handleChange(field.key, e.target.value),
1901
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring"
1902
+ }
1903
+ ) : /* @__PURE__ */ jsx10(
1904
+ "input",
1905
+ {
1906
+ type: "text",
1907
+ value: formData[field.key] ?? "",
1908
+ onChange: (e) => handleChange(field.key, e.target.value),
1909
+ placeholder: field.placeholder,
1910
+ className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background outline-none focus:ring-1 focus:ring-ring"
1911
+ }
1912
+ )
1913
+ ] }, field.key)),
1914
+ error && /* @__PURE__ */ jsx10("p", { className: "text-xs text-danger", children: error }),
1915
+ /* @__PURE__ */ jsxs9("div", { className: "flex justify-end gap-2 pt-2", children: [
1916
+ /* @__PURE__ */ jsx10(
1917
+ "button",
1918
+ {
1919
+ type: "button",
1920
+ onClick: onClose,
1921
+ disabled: loading,
1922
+ className: "px-4 py-1.5 text-sm rounded-xl border border-border hover:bg-accent transition-colors",
1923
+ children: "Cancel"
1924
+ }
1925
+ ),
1926
+ /* @__PURE__ */ jsxs9(
1927
+ "button",
1928
+ {
1929
+ type: "submit",
1930
+ disabled: loading,
1931
+ className: "px-4 py-1.5 text-sm rounded-xl bg-primary text-primary-foreground hover:bg-primary/90 transition-colors flex items-center gap-1.5",
1932
+ children: [
1933
+ loading && /* @__PURE__ */ jsx10(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
1934
+ loading ? "Saving\u2026" : "Save"
1935
+ ]
1936
+ }
1937
+ )
1938
+ ] })
1939
+ ] })
1940
+ ] })
1941
+ }
1942
+ ),
1943
+ document.body
1944
+ );
1945
+ }
1799
1946
  function ActionMenu({ item, actions }) {
1800
1947
  const [open, setOpen] = React8.useState(false);
1801
1948
  const ref = React8.useRef(null);
@@ -1945,12 +2092,17 @@ function BulletinBoard({
1945
2092
  onEdit,
1946
2093
  onDelete,
1947
2094
  preview = false,
2095
+ editBaseUrl,
2096
+ editMethod,
2097
+ editIdKey,
2098
+ editFields,
1948
2099
  deleteBaseUrl,
1949
2100
  deleteIdKey = "id",
1950
2101
  serverPagination,
1951
2102
  className
1952
2103
  }) {
1953
2104
  const [previewItem, setPreviewItem] = React8.useState(null);
2105
+ const [editItem, setEditItem] = React8.useState(null);
1954
2106
  const [deleteItem, setDeleteItem] = React8.useState(null);
1955
2107
  const [search, setSearch] = React8.useState("");
1956
2108
  const [category, setCategory] = React8.useState(null);
@@ -2096,7 +2248,10 @@ function BulletinBoard({
2096
2248
  onView: onView ? (item) => {
2097
2249
  onView(item);
2098
2250
  } : void 0,
2099
- onEdit: onEdit ? (item) => {
2251
+ onEdit: editBaseUrl && editFields ? (item) => {
2252
+ setPreviewItem(null);
2253
+ setEditItem(item);
2254
+ } : onEdit ? (item) => {
2100
2255
  onEdit(item);
2101
2256
  } : void 0,
2102
2257
  onDelete: deleteBaseUrl ? (item) => {
@@ -2107,6 +2262,20 @@ function BulletinBoard({
2107
2262
  } : void 0
2108
2263
  }
2109
2264
  ),
2265
+ editItem && editBaseUrl && editFields && /* @__PURE__ */ jsx10(
2266
+ BulletinEditModal,
2267
+ {
2268
+ item: editItem,
2269
+ baseUrl: editBaseUrl,
2270
+ idKey: editIdKey,
2271
+ method: editMethod,
2272
+ fields: editFields,
2273
+ onClose: () => setEditItem(null),
2274
+ onSuccess: (updated) => {
2275
+ onEdit?.(updated);
2276
+ }
2277
+ }
2278
+ ),
2110
2279
  deleteItem && deleteBaseUrl && /* @__PURE__ */ jsx10(
2111
2280
  BulletinDeleteConfirm,
2112
2281
  {
@@ -6296,7 +6465,6 @@ function DGEditModal({
6296
6465
  onSuccess,
6297
6466
  notif
6298
6467
  }) {
6299
- const { toast } = useToast();
6300
6468
  const [form, setForm] = React28.useState(() => {
6301
6469
  const init = {};
6302
6470
  fields.forEach((f) => {
@@ -6313,15 +6481,14 @@ function DGEditModal({
6313
6481
  setError(null);
6314
6482
  try {
6315
6483
  await axios3.put(`${baseUrl}/${itemId}/update`, form);
6316
- if (notif) {
6317
- if ((notif.type ?? "toast") === "toast") {
6318
- toast({ variant: notif.editVariant ?? "success", title: notif.editTitle ?? "Record updated", description: notif.editBody, position: notif.toastPosition });
6319
- } else {
6320
- setBanner(true);
6321
- }
6484
+ const updated = { ...item, ...form };
6485
+ if (notif && (notif.type ?? "toast") === "notification") {
6486
+ setBanner(true);
6487
+ onSuccess?.(updated);
6488
+ } else {
6489
+ onSuccess?.(updated);
6490
+ onClose();
6322
6491
  }
6323
- onSuccess?.({ ...item, ...form });
6324
- onClose();
6325
6492
  } catch (err) {
6326
6493
  setError(err?.response?.data?.message ?? err.message ?? "Update failed");
6327
6494
  } finally {
@@ -6373,7 +6540,6 @@ function DGDeleteModal({
6373
6540
  onSuccess,
6374
6541
  notif
6375
6542
  }) {
6376
- const { toast } = useToast();
6377
6543
  const [loading, setLoading] = React28.useState(false);
6378
6544
  const [error, setError] = React28.useState(null);
6379
6545
  const handleDelete = async () => {
@@ -6381,11 +6547,6 @@ function DGDeleteModal({
6381
6547
  setError(null);
6382
6548
  try {
6383
6549
  await axios3.delete(`${baseUrl}/${itemId}/delete`);
6384
- if (notif) {
6385
- if ((notif.type ?? "toast") === "toast") {
6386
- toast({ variant: notif.deleteVariant ?? "success", title: notif.deleteTitle ?? "Record deleted", description: notif.deleteBody, position: notif.toastPosition });
6387
- }
6388
- }
6389
6550
  onSuccess?.(item);
6390
6551
  onClose();
6391
6552
  } catch (err) {
@@ -6430,6 +6591,7 @@ function DataGrid({
6430
6591
  defaultActions,
6431
6592
  serverPagination
6432
6593
  }) {
6594
+ const { toast } = useToast();
6433
6595
  const [sortKey, setSortKey] = React28.useState(null);
6434
6596
  const [sortDir, setSortDir] = React28.useState(null);
6435
6597
  const [filters, setFilters] = React28.useState({});
@@ -6753,6 +6915,10 @@ function DataGrid({
6753
6915
  onSuccess: (updated) => {
6754
6916
  setTableData((prev) => prev.map((r) => String(r[actionIdKey]) === String(updated[actionIdKey]) ? updated : r));
6755
6917
  defaultActions.onSuccess?.("edit", updated);
6918
+ const notif = defaultActions.onSuccessNotif;
6919
+ if (notif && (notif.type ?? "toast") === "toast") {
6920
+ toast({ variant: notif.editVariant ?? "success", title: notif.editTitle ?? "Record updated", description: notif.editBody, position: notif.toastPosition });
6921
+ }
6756
6922
  }
6757
6923
  }
6758
6924
  ),
@@ -6767,6 +6933,10 @@ function DataGrid({
6767
6933
  onSuccess: (deleted) => {
6768
6934
  setTableData((prev) => prev.filter((r) => String(r[actionIdKey]) !== String(deleted[actionIdKey])));
6769
6935
  defaultActions.onSuccess?.("delete", deleted);
6936
+ const notif = defaultActions.onSuccessNotif;
6937
+ if (notif && (notif.type ?? "toast") === "toast") {
6938
+ toast({ variant: notif.deleteVariant ?? "success", title: notif.deleteTitle ?? "Record deleted", description: notif.deleteBody, position: notif.toastPosition });
6939
+ }
6770
6940
  }
6771
6941
  }
6772
6942
  )
@@ -10700,7 +10870,6 @@ function EditModal({
10700
10870
  onSuccess,
10701
10871
  notif
10702
10872
  }) {
10703
- const { toast } = useToast();
10704
10873
  const [form, setForm] = React46.useState(() => {
10705
10874
  const init = {};
10706
10875
  fields.forEach((f) => {
@@ -10717,20 +10886,14 @@ function EditModal({
10717
10886
  setError(null);
10718
10887
  try {
10719
10888
  await axios4.put(`${baseUrl}/${itemId}/update`, form);
10720
- if (notif) {
10721
- if ((notif.type ?? "toast") === "toast") {
10722
- toast({
10723
- variant: notif.editVariant ?? "success",
10724
- title: notif.editTitle ?? "Record updated",
10725
- description: notif.editBody,
10726
- position: notif.toastPosition
10727
- });
10728
- } else {
10729
- setBanner(true);
10730
- }
10889
+ const updated = { ...item, ...form };
10890
+ if (notif && (notif.type ?? "toast") === "notification") {
10891
+ setBanner(true);
10892
+ onSuccess?.(updated);
10893
+ } else {
10894
+ onSuccess?.(updated);
10895
+ onClose();
10731
10896
  }
10732
- onSuccess?.({ ...item, ...form });
10733
- onClose();
10734
10897
  } catch (err) {
10735
10898
  setError(err?.response?.data?.message ?? err.message ?? "Update failed");
10736
10899
  } finally {
@@ -10789,7 +10952,6 @@ function DeleteModal({
10789
10952
  onSuccess,
10790
10953
  notif
10791
10954
  }) {
10792
- const { toast } = useToast();
10793
10955
  const [loading, setLoading] = React46.useState(false);
10794
10956
  const [error, setError] = React46.useState(null);
10795
10957
  const handleDelete = async () => {
@@ -10797,16 +10959,6 @@ function DeleteModal({
10797
10959
  setError(null);
10798
10960
  try {
10799
10961
  await axios4.delete(`${baseUrl}/${itemId}/delete`);
10800
- if (notif) {
10801
- if ((notif.type ?? "toast") === "toast") {
10802
- toast({
10803
- variant: notif.deleteVariant ?? "success",
10804
- title: notif.deleteTitle ?? "Record deleted",
10805
- description: notif.deleteBody,
10806
- position: notif.toastPosition
10807
- });
10808
- }
10809
- }
10810
10962
  onSuccess?.(item);
10811
10963
  onClose();
10812
10964
  } catch (err) {
@@ -10892,6 +11044,7 @@ function Table({
10892
11044
  serverPagination,
10893
11045
  className
10894
11046
  }) {
11047
+ const { toast } = useToast();
10895
11048
  const [search, setSearch] = React46.useState("");
10896
11049
  const [currentPage, setCurrentPage] = React46.useState(1);
10897
11050
  const [selectedIds, setSelectedIds] = React46.useState([]);
@@ -11313,6 +11466,15 @@ function Table({
11313
11466
  (prev) => prev.map((r) => String(r[actionIdKey]) === String(updated[actionIdKey]) ? updated : r)
11314
11467
  );
11315
11468
  defaultActions.onSuccess?.("edit", updated);
11469
+ const notif = defaultActions.onSuccessNotif;
11470
+ if (notif && (notif.type ?? "toast") === "toast") {
11471
+ toast({
11472
+ variant: notif.editVariant ?? "success",
11473
+ title: notif.editTitle ?? "Record updated",
11474
+ description: notif.editBody,
11475
+ position: notif.toastPosition
11476
+ });
11477
+ }
11316
11478
  }
11317
11479
  }
11318
11480
  ),
@@ -11329,6 +11491,15 @@ function Table({
11329
11491
  (prev) => prev.filter((r) => String(r[actionIdKey]) !== String(deleted[actionIdKey]))
11330
11492
  );
11331
11493
  defaultActions.onSuccess?.("delete", deleted);
11494
+ const notif = defaultActions.onSuccessNotif;
11495
+ if (notif && (notif.type ?? "toast") === "toast") {
11496
+ toast({
11497
+ variant: notif.deleteVariant ?? "success",
11498
+ title: notif.deleteTitle ?? "Record deleted",
11499
+ description: notif.deleteBody,
11500
+ position: notif.toastPosition
11501
+ });
11502
+ }
11332
11503
  }
11333
11504
  }
11334
11505
  )
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "registry": "https://registry.npmjs.org/",
5
5
  "access": "public"
6
6
  },
7
- "version": "3.1.8",
7
+ "version": "3.2.2",
8
8
  "description": "Reusable React UI components",
9
9
  "license": "MIT",
10
10
  "main": "dist/index.js",
@@ -27,6 +27,7 @@
27
27
  "react-dom": ">=18"
28
28
  },
29
29
  "dependencies": {
30
+ "@juv/codego-react-ui": "^3.2.0",
30
31
  "@tailwindcss/vite": "^4.1.14",
31
32
  "@types/leaflet": "^1.9.21",
32
33
  "@types/leaflet.markercluster": "^1.5.6",