@prediktif/ui 0.1.27 → 0.1.28

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.d.mts CHANGED
@@ -16,8 +16,9 @@ interface NavItem {
16
16
  locked?: boolean;
17
17
  }
18
18
  interface OrganizationItem {
19
+ id?: string;
19
20
  name: string;
20
- logo?: React.ComponentType<{
21
+ logo?: string | React.ComponentType<{
21
22
  className?: string;
22
23
  }> | ReactNode;
23
24
  plan?: string;
@@ -27,6 +28,9 @@ interface AppSidebarProps {
27
28
  email?: string | null;
28
29
  orgRole?: string | null;
29
30
  brand?: string;
31
+ brandLogo?: string | React.ComponentType<{
32
+ className?: string;
33
+ }> | ReactNode;
30
34
  href?: string;
31
35
  confirmDialog?: boolean;
32
36
  headerDropdown?: boolean;
@@ -36,7 +40,7 @@ interface AppSidebarProps {
36
40
  navSecondaryItems?: NavItem[];
37
41
  organizations?: OrganizationItem[];
38
42
  activeOrg?: OrganizationItem;
39
- setActiveOrg?: (org: OrganizationItem) => void;
43
+ setActiveOrg?: (org: OrganizationItem) => unknown;
40
44
  children?: ReactNode;
41
45
  }
42
46
  declare const AppSidebar: react.NamedExoticComponent<AppSidebarProps>;
package/dist/index.d.ts CHANGED
@@ -16,8 +16,9 @@ interface NavItem {
16
16
  locked?: boolean;
17
17
  }
18
18
  interface OrganizationItem {
19
+ id?: string;
19
20
  name: string;
20
- logo?: React.ComponentType<{
21
+ logo?: string | React.ComponentType<{
21
22
  className?: string;
22
23
  }> | ReactNode;
23
24
  plan?: string;
@@ -27,6 +28,9 @@ interface AppSidebarProps {
27
28
  email?: string | null;
28
29
  orgRole?: string | null;
29
30
  brand?: string;
31
+ brandLogo?: string | React.ComponentType<{
32
+ className?: string;
33
+ }> | ReactNode;
30
34
  href?: string;
31
35
  confirmDialog?: boolean;
32
36
  headerDropdown?: boolean;
@@ -36,7 +40,7 @@ interface AppSidebarProps {
36
40
  navSecondaryItems?: NavItem[];
37
41
  organizations?: OrganizationItem[];
38
42
  activeOrg?: OrganizationItem;
39
- setActiveOrg?: (org: OrganizationItem) => void;
43
+ setActiveOrg?: (org: OrganizationItem) => unknown;
40
44
  children?: ReactNode;
41
45
  }
42
46
  declare const AppSidebar: react.NamedExoticComponent<AppSidebarProps>;
package/dist/index.js CHANGED
@@ -1379,14 +1379,40 @@ function SidebarMenuBadge(_a) {
1379
1379
 
1380
1380
  // src/components/composed/app-sidebar.tsx
1381
1381
  var import_jsx_runtime12 = require("react/jsx-runtime");
1382
- function renderNavIcon(icon) {
1382
+ function renderNavIcon(icon, className) {
1383
1383
  if (!icon) return null;
1384
1384
  if (typeof icon === "function" || typeof icon === "object" && icon !== null && "render" in icon) {
1385
1385
  const IconComp = icon;
1386
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconComp, {});
1386
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconComp, { className });
1387
1387
  }
1388
1388
  return icon;
1389
1389
  }
1390
+ function renderOrgLogo(logo, name, className) {
1391
+ if (typeof logo === "string" && logo.trim().length > 0) {
1392
+ return (
1393
+ // biome-ignore lint/performance/noImgElement: library component supports arbitrary external URLs without next.config restrictions
1394
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1395
+ "img",
1396
+ {
1397
+ src: logo,
1398
+ alt: name || "Organization logo",
1399
+ className: "size-full object-cover"
1400
+ }
1401
+ )
1402
+ );
1403
+ }
1404
+ if (typeof logo === "function" || typeof logo === "object" && logo !== null && "render" in logo) {
1405
+ const IconComp = logo;
1406
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconComp, { className });
1407
+ }
1408
+ if (logo) {
1409
+ return logo;
1410
+ }
1411
+ if (name && name.trim().length > 0) {
1412
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "font-semibold text-xs leading-none select-none", children: name.trim().charAt(0).toUpperCase() });
1413
+ }
1414
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.Command, { className });
1415
+ }
1390
1416
  function formatSegment(segment, subHref, navMainItems = [], navSecondaryItems = []) {
1391
1417
  if (subHref) {
1392
1418
  const matched = [...navMainItems, ...navSecondaryItems].find(
@@ -1423,6 +1449,7 @@ function AppSidebarInner({
1423
1449
  email = "",
1424
1450
  orgRole = "",
1425
1451
  brand = "Prediktif",
1452
+ brandLogo,
1426
1453
  href = "#",
1427
1454
  confirmDialog = false,
1428
1455
  headerDropdown = false,
@@ -1435,7 +1462,9 @@ function AppSidebarInner({
1435
1462
  children
1436
1463
  }) {
1437
1464
  const pathname = (0, import_navigation.usePathname)();
1465
+ const router = (0, import_navigation.useRouter)();
1438
1466
  const { setOpenMobile } = useSidebar();
1467
+ const [isPending, startTransition] = (0, import_react.useTransition)();
1439
1468
  const segments = pathname ? pathname.split("/").filter(Boolean) : [];
1440
1469
  const allNavItems = [...navMainItems, ...navSecondaryItems];
1441
1470
  (0, import_react.useEffect)(() => {
@@ -1446,6 +1475,19 @@ function AppSidebarInner({
1446
1475
  const handleLinkClick = () => {
1447
1476
  setOpenMobile(false);
1448
1477
  };
1478
+ const handleSelectOrg = (org) => {
1479
+ if (org.id && (activeOrg == null ? void 0 : activeOrg.id) && org.id === activeOrg.id) return;
1480
+ setOpenMobile(false);
1481
+ if (setActiveOrg) {
1482
+ startTransition(async () => {
1483
+ try {
1484
+ await setActiveOrg(org);
1485
+ } finally {
1486
+ router.refresh();
1487
+ }
1488
+ });
1489
+ }
1490
+ };
1449
1491
  const initials = name ? name.trim().split(" ").map((n) => n[0]).join("").substring(0, 2).toUpperCase() : "U";
1450
1492
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1451
1493
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Sidebar, { collapsible: "icon", children: [
@@ -1459,7 +1501,11 @@ function AppSidebarInner({
1459
1501
  size: "lg",
1460
1502
  className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
1461
1503
  children: [
1462
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.Command, { className: "size-4" }) }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden", children: renderOrgLogo(
1505
+ activeOrg == null ? void 0 : activeOrg.logo,
1506
+ (activeOrg == null ? void 0 : activeOrg.name) || brand,
1507
+ "size-4"
1508
+ ) }),
1463
1509
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
1464
1510
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate font-medium", children: (activeOrg == null ? void 0 : activeOrg.name) || brand }),
1465
1511
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate text-xs capitalize", children: (activeOrg == null ? void 0 : activeOrg.plan) || orgRole })
@@ -1480,17 +1526,24 @@ function AppSidebarInner({
1480
1526
  children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DropdownMenuGroup, { children: [
1481
1527
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "Organizations" }),
1482
1528
  (organizations || []).map((org) => {
1529
+ const isActive = org.id && org.id === (activeOrg == null ? void 0 : activeOrg.id) || org.name === (activeOrg == null ? void 0 : activeOrg.name);
1483
1530
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1484
1531
  DropdownMenuItem,
1485
1532
  {
1486
- onClick: () => setActiveOrg == null ? void 0 : setActiveOrg(org),
1487
- className: "gap-2 p-2",
1533
+ disabled: isPending,
1534
+ onClick: () => handleSelectOrg(org),
1535
+ className: "gap-2 p-2 cursor-pointer",
1488
1536
  children: [
1489
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex size-6 items-center justify-center rounded-md border", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.Command, { className: "size-3.5 shrink-0" }) }),
1490
- org.name
1537
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex size-6 items-center justify-center rounded-md border overflow-hidden bg-background", children: renderOrgLogo(
1538
+ org.logo,
1539
+ org.name,
1540
+ "size-3.5 shrink-0"
1541
+ ) }),
1542
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "flex-1 truncate", children: org.name }),
1543
+ isActive && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.Check, { className: "ml-auto size-4" })
1491
1544
  ]
1492
1545
  },
1493
- org.name
1546
+ org.id || org.name
1494
1547
  );
1495
1548
  })
1496
1549
  ] })
@@ -1502,7 +1555,7 @@ function AppSidebarInner({
1502
1555
  size: "lg",
1503
1556
  render: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_link.default, { href: "/", onClick: handleLinkClick }),
1504
1557
  children: [
1505
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.Command, { className: "size-4" }) }),
1558
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden", children: renderOrgLogo(brandLogo, brand, "size-4") }),
1506
1559
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
1507
1560
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate font-medium", children: brand }),
1508
1561
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate text-xs capitalize", children: orgRole })
package/dist/index.mjs CHANGED
@@ -32,10 +32,10 @@ var __objRest = (source, exclude) => {
32
32
  };
33
33
 
34
34
  // src/components/composed/app-sidebar.tsx
35
- import { ChevronsUpDown, Command, LockIcon } from "lucide-react";
35
+ import { Check, ChevronsUpDown, Command, LockIcon } from "lucide-react";
36
36
  import Link from "next/link";
37
- import { usePathname } from "next/navigation";
38
- import { memo, useEffect as useEffect3 } from "react";
37
+ import { usePathname, useRouter } from "next/navigation";
38
+ import { memo, useEffect as useEffect3, useTransition } from "react";
39
39
 
40
40
  // src/components/ui/alert-dialog.tsx
41
41
  import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
@@ -1288,14 +1288,40 @@ function SidebarMenuBadge(_a) {
1288
1288
 
1289
1289
  // src/components/composed/app-sidebar.tsx
1290
1290
  import { Fragment, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1291
- function renderNavIcon(icon) {
1291
+ function renderNavIcon(icon, className) {
1292
1292
  if (!icon) return null;
1293
1293
  if (typeof icon === "function" || typeof icon === "object" && icon !== null && "render" in icon) {
1294
1294
  const IconComp = icon;
1295
- return /* @__PURE__ */ jsx12(IconComp, {});
1295
+ return /* @__PURE__ */ jsx12(IconComp, { className });
1296
1296
  }
1297
1297
  return icon;
1298
1298
  }
1299
+ function renderOrgLogo(logo, name, className) {
1300
+ if (typeof logo === "string" && logo.trim().length > 0) {
1301
+ return (
1302
+ // biome-ignore lint/performance/noImgElement: library component supports arbitrary external URLs without next.config restrictions
1303
+ /* @__PURE__ */ jsx12(
1304
+ "img",
1305
+ {
1306
+ src: logo,
1307
+ alt: name || "Organization logo",
1308
+ className: "size-full object-cover"
1309
+ }
1310
+ )
1311
+ );
1312
+ }
1313
+ if (typeof logo === "function" || typeof logo === "object" && logo !== null && "render" in logo) {
1314
+ const IconComp = logo;
1315
+ return /* @__PURE__ */ jsx12(IconComp, { className });
1316
+ }
1317
+ if (logo) {
1318
+ return logo;
1319
+ }
1320
+ if (name && name.trim().length > 0) {
1321
+ return /* @__PURE__ */ jsx12("span", { className: "font-semibold text-xs leading-none select-none", children: name.trim().charAt(0).toUpperCase() });
1322
+ }
1323
+ return /* @__PURE__ */ jsx12(Command, { className });
1324
+ }
1299
1325
  function formatSegment(segment, subHref, navMainItems = [], navSecondaryItems = []) {
1300
1326
  if (subHref) {
1301
1327
  const matched = [...navMainItems, ...navSecondaryItems].find(
@@ -1332,6 +1358,7 @@ function AppSidebarInner({
1332
1358
  email = "",
1333
1359
  orgRole = "",
1334
1360
  brand = "Prediktif",
1361
+ brandLogo,
1335
1362
  href = "#",
1336
1363
  confirmDialog = false,
1337
1364
  headerDropdown = false,
@@ -1344,7 +1371,9 @@ function AppSidebarInner({
1344
1371
  children
1345
1372
  }) {
1346
1373
  const pathname = usePathname();
1374
+ const router = useRouter();
1347
1375
  const { setOpenMobile } = useSidebar();
1376
+ const [isPending, startTransition] = useTransition();
1348
1377
  const segments = pathname ? pathname.split("/").filter(Boolean) : [];
1349
1378
  const allNavItems = [...navMainItems, ...navSecondaryItems];
1350
1379
  useEffect3(() => {
@@ -1355,6 +1384,19 @@ function AppSidebarInner({
1355
1384
  const handleLinkClick = () => {
1356
1385
  setOpenMobile(false);
1357
1386
  };
1387
+ const handleSelectOrg = (org) => {
1388
+ if (org.id && (activeOrg == null ? void 0 : activeOrg.id) && org.id === activeOrg.id) return;
1389
+ setOpenMobile(false);
1390
+ if (setActiveOrg) {
1391
+ startTransition(async () => {
1392
+ try {
1393
+ await setActiveOrg(org);
1394
+ } finally {
1395
+ router.refresh();
1396
+ }
1397
+ });
1398
+ }
1399
+ };
1358
1400
  const initials = name ? name.trim().split(" ").map((n) => n[0]).join("").substring(0, 2).toUpperCase() : "U";
1359
1401
  return /* @__PURE__ */ jsxs7(Fragment, { children: [
1360
1402
  /* @__PURE__ */ jsxs7(Sidebar, { collapsible: "icon", children: [
@@ -1368,7 +1410,11 @@ function AppSidebarInner({
1368
1410
  size: "lg",
1369
1411
  className: "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
1370
1412
  children: [
1371
- /* @__PURE__ */ jsx12("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground", children: /* @__PURE__ */ jsx12(Command, { className: "size-4" }) }),
1413
+ /* @__PURE__ */ jsx12("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden", children: renderOrgLogo(
1414
+ activeOrg == null ? void 0 : activeOrg.logo,
1415
+ (activeOrg == null ? void 0 : activeOrg.name) || brand,
1416
+ "size-4"
1417
+ ) }),
1372
1418
  /* @__PURE__ */ jsxs7("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
1373
1419
  /* @__PURE__ */ jsx12("span", { className: "truncate font-medium", children: (activeOrg == null ? void 0 : activeOrg.name) || brand }),
1374
1420
  /* @__PURE__ */ jsx12("span", { className: "truncate text-xs capitalize", children: (activeOrg == null ? void 0 : activeOrg.plan) || orgRole })
@@ -1389,17 +1435,24 @@ function AppSidebarInner({
1389
1435
  children: /* @__PURE__ */ jsxs7(DropdownMenuGroup, { children: [
1390
1436
  /* @__PURE__ */ jsx12(DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "Organizations" }),
1391
1437
  (organizations || []).map((org) => {
1438
+ const isActive = org.id && org.id === (activeOrg == null ? void 0 : activeOrg.id) || org.name === (activeOrg == null ? void 0 : activeOrg.name);
1392
1439
  return /* @__PURE__ */ jsxs7(
1393
1440
  DropdownMenuItem,
1394
1441
  {
1395
- onClick: () => setActiveOrg == null ? void 0 : setActiveOrg(org),
1396
- className: "gap-2 p-2",
1442
+ disabled: isPending,
1443
+ onClick: () => handleSelectOrg(org),
1444
+ className: "gap-2 p-2 cursor-pointer",
1397
1445
  children: [
1398
- /* @__PURE__ */ jsx12("div", { className: "flex size-6 items-center justify-center rounded-md border", children: /* @__PURE__ */ jsx12(Command, { className: "size-3.5 shrink-0" }) }),
1399
- org.name
1446
+ /* @__PURE__ */ jsx12("div", { className: "flex size-6 items-center justify-center rounded-md border overflow-hidden bg-background", children: renderOrgLogo(
1447
+ org.logo,
1448
+ org.name,
1449
+ "size-3.5 shrink-0"
1450
+ ) }),
1451
+ /* @__PURE__ */ jsx12("span", { className: "flex-1 truncate", children: org.name }),
1452
+ isActive && /* @__PURE__ */ jsx12(Check, { className: "ml-auto size-4" })
1400
1453
  ]
1401
1454
  },
1402
- org.name
1455
+ org.id || org.name
1403
1456
  );
1404
1457
  })
1405
1458
  ] })
@@ -1411,7 +1464,7 @@ function AppSidebarInner({
1411
1464
  size: "lg",
1412
1465
  render: /* @__PURE__ */ jsx12(Link, { href: "/", onClick: handleLinkClick }),
1413
1466
  children: [
1414
- /* @__PURE__ */ jsx12("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground", children: /* @__PURE__ */ jsx12(Command, { className: "size-4" }) }),
1467
+ /* @__PURE__ */ jsx12("div", { className: "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden", children: renderOrgLogo(brandLogo, brand, "size-4") }),
1415
1468
  /* @__PURE__ */ jsxs7("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
1416
1469
  /* @__PURE__ */ jsx12("span", { className: "truncate font-medium", children: brand }),
1417
1470
  /* @__PURE__ */ jsx12("span", { className: "truncate text-xs capitalize", children: orgRole })
@@ -2685,7 +2738,7 @@ import {
2685
2738
  useTable
2686
2739
  } from "@tanstack/react-table";
2687
2740
  import { FolderOpenIcon } from "lucide-react";
2688
- import { useRouter as useRouter2 } from "next/navigation";
2741
+ import { useRouter as useRouter3 } from "next/navigation";
2689
2742
  import { useMemo as useMemo5, useState as useState6 } from "react";
2690
2743
 
2691
2744
  // src/components/composed/state-empty.tsx
@@ -4200,11 +4253,11 @@ function DataTableHeaderFilter({
4200
4253
 
4201
4254
  // src/components/composed/data-table/table-header-refresh.tsx
4202
4255
  import { RotateCwIcon } from "lucide-react";
4203
- import { useRouter } from "next/navigation";
4256
+ import { useRouter as useRouter2 } from "next/navigation";
4204
4257
  import { useState as useState5 } from "react";
4205
4258
  import { jsx as jsx42, jsxs as jsxs29 } from "react/jsx-runtime";
4206
4259
  function DataTableHeaderRefresh() {
4207
- const router = useRouter();
4260
+ const router = useRouter2();
4208
4261
  const [isRefreshing, setIsRefreshing] = useState5(false);
4209
4262
  const handleRefresh = () => {
4210
4263
  setIsRefreshing(true);
@@ -4476,7 +4529,7 @@ function DataTable({
4476
4529
  showColumnToggleButton = true,
4477
4530
  showAddButton = true
4478
4531
  }) {
4479
- const router = useRouter2();
4532
+ const router = useRouter3();
4480
4533
  const [createOpen, setCreateOpen] = useState6(false);
4481
4534
  const [sorting, setSorting] = useState6([]);
4482
4535
  const [columnFilters, setColumnFilters] = useState6([]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prediktif/ui",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",