organify-ui 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  export { I18nProvider, createTranslator, useI18n } from './chunk-FQA33MF4.js';
2
2
  export { ThemeProvider, useTheme } from './chunk-RFOKENE3.js';
3
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, Separator, ResponsiveDialog, Label, Input, Textarea, Button, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-GQZMW7XN.js';
4
- export { Alert, Button, ChatMessages, ChatSidebar, CreateRoomDialog, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, Input, Label, MOCK_PROJECTS, MOCK_USERS, OrgLoader, OrgLoaderInline, OrganifyChat, ResponsiveDialog, RoomManagementPanel, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TypingIndicatorMock, alertVariants, buttonVariants, generateAutoReplies, getMockMentionOptions, getRoomPermissions, inputVariants, orgLoaderVariants, typingIndicator, useChat } from './chunk-GQZMW7XN.js';
3
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, Separator, ResponsiveDialog, Label, Input, Textarea, Button, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-NUA6OPJV.js';
4
+ export { AiChatSidebar, Alert, Button, ChatMessages, ChatSidebar, CreateRoomDialog, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, Input, Label, MOCK_PROJECTS, MOCK_USERS, OrgLoader, OrgLoaderInline, OrganifyChat, ResponsiveDialog, RoomManagementPanel, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TypingIndicatorMock, alertVariants, buttonVariants, generateAutoReplies, getMockMentionOptions, getRoomPermissions, inputVariants, orgLoaderVariants, typingIndicator, useChat } from './chunk-NUA6OPJV.js';
5
5
  import { Popover, PopoverTrigger, PopoverContent } from './chunk-A2H2TBSV.js';
6
6
  export { NotificationBell, NotificationItem, NotificationList, OrganifyNotifications, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PresenceAvatarStack, PresenceIndicator, useNotifications, usePresence } from './chunk-A2H2TBSV.js';
7
7
  import { cn, Avatar, AvatarImage, AvatarFallback, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, Skeleton, useOrganify, useOrganifyGql, ScrollArea, useOrganifyUser, useOrganifyApi, Badge, useOrganifyWorkspace, useOrganifyNavigation, useOrganifyProject } from './chunk-VHQZS77G.js';
8
8
  export { Avatar, AvatarFallback, AvatarImage, Badge, OrganifyContext, OrganifyProvider, ScrollArea, ScrollBar, Skeleton, SkeletonCard, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, avatarVariants, badgeVariants, cn, useOrganify, useOrganifyApi, useOrganifyGql, useOrganifyNavigation, useOrganifyProject, useOrganifyRest, useOrganifyUser, useOrganifyWorkspace } from './chunk-VHQZS77G.js';
9
9
  import { OrgDiamond, OrgPlus, OrgComment, OrgEdit, OrgTrash, OrgCheckCircle, OrgAttachment, OrgCalendar, OrgMail, OrgBoard, OrgSprint, OrgRocket, OrgWarning, OrgFlag, OrgShield, OrgZap, OrgStar } from './chunk-MZKEDV5W.js';
10
10
  export { OrgAI, OrgActivity, OrgArrowLeft, OrgArrowRight, OrgAttachment, OrgBell, OrgBoard, OrgCalendar, OrgCelebrate, OrgChart, OrgChat, OrgCheck, OrgCheckCircle, OrgChevronDown, OrgChevronLeft, OrgChevronRight, OrgChevronUp, OrgClock, OrgClose, OrgComment, OrgCopy, OrgCreditCard, OrgDeveloper, OrgDiamond, OrgDoor, OrgDownload, OrgEdit, OrgError, OrgExecutive, OrgEye, OrgEyeOff, OrgFile, OrgFilter, OrgFlag, OrgFolder, OrgGauge, OrgGlobe, OrgGrid, OrgHeart, OrgHome, OrgInfo, OrgIntegration, OrgLink, OrgList, OrgLock, OrgLogo, OrgLogout, OrgMail, OrgMention, OrgMenu, OrgMoon, OrgPMO, OrgPause, OrgPlay, OrgPlus, OrgProjectManager, OrgReport, OrgRocket, OrgSearch, OrgSettings, OrgShield, OrgSort, OrgSprint, OrgStakeholder, OrgStar, OrgSun, OrgTag, OrgTarget, OrgTeam, OrgTrash, OrgTutorial, OrgUnlock, OrgUpload, OrgUser, OrgWarning, OrgWave, OrgWordmark, OrgWorkflow, OrgWorkspace, OrgZap } from './chunk-MZKEDV5W.js';
11
- import * as React4 from 'react';
11
+ import * as React5 from 'react';
12
12
  import * as SwitchPrimitive from '@radix-ui/react-switch';
13
13
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
14
14
  import * as ProgressPrimitive from '@radix-ui/react-progress';
@@ -21,9 +21,12 @@ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
21
21
  import { Command as Command$1 } from 'cmdk';
22
22
  import { Slot } from '@radix-ui/react-slot';
23
23
  import { Toaster as Toaster$1 } from 'sonner';
24
+ export { toast } from 'sonner';
25
+ import { useQueryClient, useQuery } from '@tanstack/react-query';
26
+ export { QueryClient, QueryClientProvider } from '@tanstack/react-query';
24
27
 
25
- var Toggle = React4.forwardRef(({ className, label, description, ...props }, ref) => {
26
- const id = React4.useId();
28
+ var Toggle = React5.forwardRef(({ className, label, description, ...props }, ref) => {
29
+ const id = React5.useId();
27
30
  if (label) {
28
31
  return /* @__PURE__ */ jsxs(
29
32
  "div",
@@ -164,7 +167,7 @@ function UserAvatar({
164
167
  ] }) });
165
168
  }
166
169
  UserAvatar.displayName = "UserAvatar";
167
- var Progress = React4.forwardRef(({ className, value, showLabel, label, variant = "default", ...props }, ref) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
170
+ var Progress = React5.forwardRef(({ className, value, showLabel, label, variant = "default", ...props }, ref) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
168
171
  (label || showLabel) && /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between", children: [
169
172
  label && /* @__PURE__ */ jsx("span", { className: "text-label uppercase tracking-widest text-org-text-muted", children: label }),
170
173
  showLabel && /* @__PURE__ */ jsxs("span", { className: "font-mono text-lg text-primary-light", children: [
@@ -342,8 +345,8 @@ function EmptyState({
342
345
  );
343
346
  }
344
347
  function useMediaQuery(query) {
345
- const [matches, setMatches] = React4.useState(false);
346
- React4.useEffect(() => {
348
+ const [matches, setMatches] = React5.useState(false);
349
+ React5.useEffect(() => {
347
350
  const mql = window.matchMedia(query);
348
351
  setMatches(mql.matches);
349
352
  const handler = (e) => setMatches(e.matches);
@@ -760,77 +763,127 @@ function DockSidebar({
760
763
  onExpandedChange,
761
764
  hoverExpand = false,
762
765
  renderLink,
766
+ mobileOpen = false,
767
+ onMobileClose,
763
768
  className,
764
769
  // Destructure hoverExpand out so it doesn't leak to DOM via ...props
765
770
  ...props
766
771
  }) {
767
772
  const handleToggle = () => onExpandedChange?.(!expanded);
768
- return /* @__PURE__ */ jsxs(
769
- "aside",
770
- {
771
- className: cn(
772
- sidebarVariants({ state: expanded ? "expanded" : "collapsed" }),
773
- className
774
- ),
775
- onMouseEnter: () => hoverExpand && onExpandedChange?.(true),
776
- onMouseLeave: () => hoverExpand && onExpandedChange?.(false),
777
- ...props,
778
- children: [
779
- /* @__PURE__ */ jsxs("div", { className: cn("flex items-center px-3 h-16 border-b border-white/10", expanded ? "justify-between" : "justify-center"), children: [
780
- header,
781
- /* @__PURE__ */ jsx(
782
- "button",
783
- {
784
- type: "button",
785
- onClick: handleToggle,
786
- "aria-label": expanded ? "Collapse sidebar" : "Expand sidebar",
787
- className: cn(
788
- "flex h-7 w-7 items-center justify-center rounded-xl",
789
- "text-org-text-muted transition-all hover:bg-white/[0.05] hover:text-org-text"
773
+ React5.useEffect(() => {
774
+ if (!mobileOpen) return;
775
+ const onKey = (e) => {
776
+ if (e.key === "Escape") onMobileClose?.();
777
+ };
778
+ document.addEventListener("keydown", onKey);
779
+ return () => document.removeEventListener("keydown", onKey);
780
+ }, [mobileOpen, onMobileClose]);
781
+ React5.useEffect(() => {
782
+ if (!mobileOpen) return;
783
+ document.body.style.overflow = "hidden";
784
+ return () => {
785
+ document.body.style.overflow = "";
786
+ };
787
+ }, [mobileOpen]);
788
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
789
+ mobileOpen && /* @__PURE__ */ jsx(
790
+ "div",
791
+ {
792
+ className: "fixed inset-0 z-[49] bg-black/60 backdrop-blur-sm sm:hidden animate-in fade-in duration-200",
793
+ onClick: onMobileClose,
794
+ "aria-hidden": "true"
795
+ }
796
+ ),
797
+ /* @__PURE__ */ jsxs(
798
+ "aside",
799
+ {
800
+ className: cn(
801
+ // Desktop: normal fixed sidebar
802
+ "hidden sm:flex",
803
+ sidebarVariants({ state: expanded ? "expanded" : "collapsed" }),
804
+ // Mobile: overlay drawer mode
805
+ mobileOpen && "flex sm:flex fixed inset-y-0 left-0 z-50 w-64 shadow-2xl shadow-black/50 animate-in slide-in-from-left duration-300",
806
+ !mobileOpen && "max-sm:hidden",
807
+ className
808
+ ),
809
+ onMouseEnter: () => hoverExpand && onExpandedChange?.(true),
810
+ onMouseLeave: () => hoverExpand && onExpandedChange?.(false),
811
+ ...props,
812
+ children: [
813
+ /* @__PURE__ */ jsxs("div", { className: cn(
814
+ "flex items-center px-3 h-16 border-b border-white/10",
815
+ expanded || mobileOpen ? "justify-between" : "justify-center"
816
+ ), children: [
817
+ header,
818
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
819
+ mobileOpen && /* @__PURE__ */ jsx(
820
+ "button",
821
+ {
822
+ type: "button",
823
+ onClick: onMobileClose,
824
+ "aria-label": "Fechar menu",
825
+ className: "flex h-7 w-7 items-center justify-center rounded-xl text-org-text-muted hover:bg-white/[0.05] hover:text-org-text sm:hidden",
826
+ children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M4 4L12 12M12 4L4 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
827
+ }
790
828
  ),
791
- children: /* @__PURE__ */ jsx(
792
- "svg",
829
+ /* @__PURE__ */ jsx(
830
+ "button",
793
831
  {
794
- width: "16",
795
- height: "16",
796
- viewBox: "0 0 16 16",
797
- fill: "none",
832
+ type: "button",
833
+ onClick: handleToggle,
834
+ "aria-label": expanded ? "Collapse sidebar" : "Expand sidebar",
798
835
  className: cn(
799
- "transition-transform duration-[400ms]",
800
- expanded ? "rotate-0" : "rotate-180"
836
+ "hidden sm:flex h-7 w-7 items-center justify-center rounded-xl",
837
+ "text-org-text-muted transition-all hover:bg-white/[0.05] hover:text-org-text"
801
838
  ),
802
- children: /* @__PURE__ */ jsx("path", { d: "M10 12L6 8L10 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
839
+ children: /* @__PURE__ */ jsx(
840
+ "svg",
841
+ {
842
+ width: "16",
843
+ height: "16",
844
+ viewBox: "0 0 16 16",
845
+ fill: "none",
846
+ className: cn(
847
+ "transition-transform duration-[400ms]",
848
+ expanded ? "rotate-0" : "rotate-180"
849
+ ),
850
+ children: /* @__PURE__ */ jsx("path", { d: "M10 12L6 8L10 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
851
+ }
852
+ )
803
853
  }
804
854
  )
805
- }
806
- )
807
- ] }),
808
- /* @__PURE__ */ jsx("nav", { className: "flex-1 overflow-y-auto overflow-x-hidden py-2 px-2 space-y-0.5", children: items.map((entry, i) => /* @__PURE__ */ jsx(
809
- SidebarEntry,
810
- {
811
- entry,
812
- expanded,
813
- renderLink
814
- },
815
- isSeparator(entry) ? `sep-${i}` : entry.id
816
- )) }),
817
- bottomItems && bottomItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "border-t border-white/10 py-2 px-2 space-y-0.5", children: bottomItems.map((entry, i) => /* @__PURE__ */ jsx(
818
- SidebarEntry,
819
- {
820
- entry,
821
- expanded,
822
- renderLink
823
- },
824
- isSeparator(entry) ? `bsep-${i}` : entry.id
825
- )) })
826
- ]
827
- }
828
- );
855
+ ] })
856
+ ] }),
857
+ /* @__PURE__ */ jsx("nav", { className: "flex-1 overflow-y-auto overflow-x-hidden py-2 px-2 space-y-0.5", children: items.map((entry, i) => /* @__PURE__ */ jsx(
858
+ SidebarEntry,
859
+ {
860
+ entry,
861
+ expanded: expanded || mobileOpen,
862
+ renderLink,
863
+ onNavigate: mobileOpen ? onMobileClose : void 0
864
+ },
865
+ isSeparator(entry) ? `sep-${i}` : entry.id
866
+ )) }),
867
+ bottomItems && bottomItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "border-t border-white/10 py-2 px-2 space-y-0.5", children: bottomItems.map((entry, i) => /* @__PURE__ */ jsx(
868
+ SidebarEntry,
869
+ {
870
+ entry,
871
+ expanded: expanded || mobileOpen,
872
+ renderLink,
873
+ onNavigate: mobileOpen ? onMobileClose : void 0
874
+ },
875
+ isSeparator(entry) ? `bsep-${i}` : entry.id
876
+ )) })
877
+ ]
878
+ }
879
+ )
880
+ ] });
829
881
  }
830
882
  function SidebarEntry({
831
883
  entry,
832
884
  expanded,
833
- renderLink
885
+ renderLink,
886
+ onNavigate
834
887
  }) {
835
888
  if (isSeparator(entry)) {
836
889
  return /* @__PURE__ */ jsxs("div", { className: "py-2", children: [
@@ -843,16 +896,22 @@ function SidebarEntry({
843
896
  {
844
897
  item: entry,
845
898
  expanded,
846
- renderLink
899
+ renderLink,
900
+ onNavigate
847
901
  }
848
902
  );
849
903
  }
850
904
  function SidebarButton({
851
905
  item,
852
906
  expanded,
853
- renderLink
907
+ renderLink,
908
+ onNavigate
854
909
  }) {
855
910
  const { icon, label, href, onClick, active, badge, disabled } = item;
911
+ const handleClick = () => {
912
+ onClick?.();
913
+ onNavigate?.();
914
+ };
856
915
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
857
916
  /* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-xl transition-all", children: icon }),
858
917
  /* @__PURE__ */ jsx(
@@ -888,13 +947,13 @@ function SidebarButton({
888
947
  !expanded && "justify-center"
889
948
  );
890
949
  if (href && renderLink) {
891
- return renderLink({ href, children: content, className: sharedClass, onClick });
950
+ return renderLink({ href, children: content, className: sharedClass, onClick: handleClick });
892
951
  }
893
952
  return /* @__PURE__ */ jsxs(
894
953
  "button",
895
954
  {
896
955
  type: "button",
897
- onClick,
956
+ onClick: handleClick,
898
957
  disabled,
899
958
  "aria-current": active ? "page" : void 0,
900
959
  className: sharedClass,
@@ -961,12 +1020,12 @@ function WorkspaceSwitcher({ compact = false, onCreateWorkspace, className }) {
961
1020
  onWorkspaceChange
962
1021
  } = useOrganify();
963
1022
  const gql = useOrganifyGql();
964
- const [isOpen, setIsOpen] = React4.useState(false);
965
- const [createDialogOpen, setCreateDialogOpen] = React4.useState(false);
966
- const [editingWorkspace, setEditingWorkspace] = React4.useState(null);
967
- const [deletingWorkspace, setDeletingWorkspace] = React4.useState(null);
968
- const dropdownRef = React4.useRef(null);
969
- React4.useEffect(() => {
1023
+ const [isOpen, setIsOpen] = React5.useState(false);
1024
+ const [createDialogOpen, setCreateDialogOpen] = React5.useState(false);
1025
+ const [editingWorkspace, setEditingWorkspace] = React5.useState(null);
1026
+ const [deletingWorkspace, setDeletingWorkspace] = React5.useState(null);
1027
+ const dropdownRef = React5.useRef(null);
1028
+ React5.useEffect(() => {
970
1029
  function handleClickOutside(event) {
971
1030
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
972
1031
  setIsOpen(false);
@@ -975,7 +1034,7 @@ function WorkspaceSwitcher({ compact = false, onCreateWorkspace, className }) {
975
1034
  document.addEventListener("mousedown", handleClickOutside);
976
1035
  return () => document.removeEventListener("mousedown", handleClickOutside);
977
1036
  }, []);
978
- const handleCreateWorkspace = React4.useCallback(
1037
+ const handleCreateWorkspace = React5.useCallback(
979
1038
  async (data) => {
980
1039
  const optimisticId = `temp-${Date.now()}`;
981
1040
  const optimisticSlug = data.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -1015,7 +1074,7 @@ function WorkspaceSwitcher({ compact = false, onCreateWorkspace, className }) {
1015
1074
  },
1016
1075
  [workspaces, gql, onWorkspacesChange, onWorkspaceChange, onCreateWorkspace]
1017
1076
  );
1018
- const handleUpdateWorkspace = React4.useCallback(
1077
+ const handleUpdateWorkspace = React5.useCallback(
1019
1078
  async (slug, data) => {
1020
1079
  const prevWorkspaces = [...workspaces];
1021
1080
  const prevActive = workspace;
@@ -1050,7 +1109,7 @@ function WorkspaceSwitcher({ compact = false, onCreateWorkspace, className }) {
1050
1109
  },
1051
1110
  [workspaces, workspace, gql, onWorkspacesChange, onWorkspaceChange]
1052
1111
  );
1053
- const handleDeleteWorkspace = React4.useCallback(
1112
+ const handleDeleteWorkspace = React5.useCallback(
1054
1113
  async (slug) => {
1055
1114
  const prevWorkspaces = [...workspaces];
1056
1115
  const prevActive = workspace;
@@ -1070,7 +1129,7 @@ function WorkspaceSwitcher({ compact = false, onCreateWorkspace, className }) {
1070
1129
  [workspaces, workspace, gql, onWorkspacesChange, onWorkspaceChange]
1071
1130
  );
1072
1131
  const displayName = workspace?.name ?? workspace?.slug ?? "\u2026";
1073
- return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), ref: dropdownRef, children: [
1132
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative min-w-0", className), ref: dropdownRef, children: [
1074
1133
  /* @__PURE__ */ jsxs(
1075
1134
  "button",
1076
1135
  {
@@ -1184,7 +1243,7 @@ function SwitcherDropdown({
1184
1243
  onEditClick,
1185
1244
  onDeleteClick
1186
1245
  }) {
1187
- return /* @__PURE__ */ jsxs("div", { className: "absolute top-full left-0 right-0 mt-1 rounded-xl border border-white/20 bg-white/[0.03] backdrop-blur-[40px] shadow-[0_24px_80px_-15px_rgba(0,0,0,0.5)] z-50 overflow-hidden min-w-52", children: [
1246
+ return /* @__PURE__ */ jsxs("div", { className: "absolute top-full left-0 mt-1 w-64 rounded-xl border border-white/20 bg-white/[0.03] backdrop-blur-[40px] shadow-[0_24px_80px_-15px_rgba(0,0,0,0.5)] z-50 overflow-hidden", children: [
1188
1247
  /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs font-medium uppercase tracking-wider text-theme-muted border-b border-white/10", children: "Workspaces" }),
1189
1248
  /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-64", children: /* @__PURE__ */ jsx("div", { className: "py-1", children: loading ? /* @__PURE__ */ jsx("div", { className: "space-y-1 px-3 py-2", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 py-2", children: [
1190
1249
  /* @__PURE__ */ jsx(Skeleton, { className: "w-7 h-7", variant: "rounded" }),
@@ -1239,7 +1298,7 @@ function SwitcherItem({
1239
1298
  onEdit,
1240
1299
  onDelete
1241
1300
  }) {
1242
- const [showActions, setShowActions] = React4.useState(false);
1301
+ const [showActions, setShowActions] = React5.useState(false);
1243
1302
  const isOptimistic = ws.id.startsWith("temp-");
1244
1303
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
1245
1304
  /* @__PURE__ */ jsx(
@@ -1294,7 +1353,7 @@ function SwitcherItem({
1294
1353
  ] })
1295
1354
  ] });
1296
1355
  const cls = cn(
1297
- "flex items-center gap-2.5 px-3 py-2 transition-colors hover:bg-theme-subtle",
1356
+ "flex items-center gap-2.5 px-3 py-2 w-full min-w-0 overflow-hidden transition-colors hover:bg-theme-subtle",
1298
1357
  isActive && "bg-theme-subtle",
1299
1358
  isOptimistic && "pointer-events-none"
1300
1359
  );
@@ -1303,9 +1362,9 @@ function SwitcherItem({
1303
1362
  onMouseLeave: () => setShowActions(false)
1304
1363
  };
1305
1364
  if (renderLink) {
1306
- return /* @__PURE__ */ jsx("div", { ...wrapperProps, children: renderLink({ href: `/${ws.slug}`, children: content, className: cls, onClick: onClose }) });
1365
+ return /* @__PURE__ */ jsx("div", { className: "min-w-0 overflow-hidden", ...wrapperProps, children: renderLink({ href: `/${ws.slug}`, children: content, className: cls, onClick: onClose }) });
1307
1366
  }
1308
- return /* @__PURE__ */ jsx("div", { ...wrapperProps, children: /* @__PURE__ */ jsx("a", { href: `/${ws.slug}`, onClick: onClose, className: cls, children: content }) });
1367
+ return /* @__PURE__ */ jsx("div", { className: "min-w-0 overflow-hidden", ...wrapperProps, children: /* @__PURE__ */ jsx("a", { href: `/${ws.slug}`, onClick: onClose, className: cls, children: content }) });
1309
1368
  }
1310
1369
  WorkspaceSwitcher.displayName = "WorkspaceSwitcher";
1311
1370
  function CreateWorkspaceInlineDialog({
@@ -1313,11 +1372,11 @@ function CreateWorkspaceInlineDialog({
1313
1372
  onOpenChange,
1314
1373
  onSubmit
1315
1374
  }) {
1316
- const [name, setName] = React4.useState("");
1317
- const [description, setDescription] = React4.useState("");
1318
- const [loading, setLoading] = React4.useState(false);
1319
- const [error, setError] = React4.useState("");
1320
- React4.useEffect(() => {
1375
+ const [name, setName] = React5.useState("");
1376
+ const [description, setDescription] = React5.useState("");
1377
+ const [loading, setLoading] = React5.useState(false);
1378
+ const [error, setError] = React5.useState("");
1379
+ React5.useEffect(() => {
1321
1380
  if (open) {
1322
1381
  setName("");
1323
1382
  setDescription("");
@@ -1390,11 +1449,11 @@ function EditWorkspaceDialog({
1390
1449
  workspace,
1391
1450
  onSubmit
1392
1451
  }) {
1393
- const [name, setName] = React4.useState("");
1394
- const [description, setDescription] = React4.useState("");
1395
- const [loading, setLoading] = React4.useState(false);
1396
- const [error, setError] = React4.useState("");
1397
- React4.useEffect(() => {
1452
+ const [name, setName] = React5.useState("");
1453
+ const [description, setDescription] = React5.useState("");
1454
+ const [loading, setLoading] = React5.useState(false);
1455
+ const [error, setError] = React5.useState("");
1456
+ React5.useEffect(() => {
1398
1457
  if (open && workspace) {
1399
1458
  setName(workspace.name);
1400
1459
  setDescription(workspace.description ?? "");
@@ -1468,10 +1527,10 @@ function DeleteWorkspaceDialog({
1468
1527
  workspace,
1469
1528
  onConfirm
1470
1529
  }) {
1471
- const [loading, setLoading] = React4.useState(false);
1472
- const [error, setError] = React4.useState("");
1473
- const [confirmText, setConfirmText] = React4.useState("");
1474
- React4.useEffect(() => {
1530
+ const [loading, setLoading] = React5.useState(false);
1531
+ const [error, setError] = React5.useState("");
1532
+ const [confirmText, setConfirmText] = React5.useState("");
1533
+ React5.useEffect(() => {
1475
1534
  if (open) {
1476
1535
  setError("");
1477
1536
  setConfirmText("");
@@ -4101,7 +4160,7 @@ function SheetContent({
4101
4160
  "data-slot": "sheet-content",
4102
4161
  className: cn(
4103
4162
  "data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-xl dark:shadow-[0_24px_80px_-15px_rgba(0,0,0,0.8)] transition ease-[cubic-bezier(0.25,1,0.5,1)] data-[state=closed]:duration-300 data-[state=open]:duration-500",
4104
- "border-gray-300 bg-white text-gray-900 dark:border-gray-700 dark:bg-gray-900 dark:text-white",
4163
+ "[background:var(--org-bg-elevated,#110E22)] [color:var(--org-text,#F0ECF9)] [border-color:var(--org-glass-border,rgba(167,139,250,0.15))]",
4105
4164
  side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l-2 sm:max-w-sm",
4106
4165
  side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r-2 sm:max-w-sm",
4107
4166
  side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b-2",
@@ -4199,9 +4258,9 @@ function CommentItem({
4199
4258
  onDelete,
4200
4259
  onReact
4201
4260
  }) {
4202
- const [editing, setEditing] = React4.useState(false);
4203
- const [editContent, setEditContent] = React4.useState(comment.content);
4204
- const [showReactions, setShowReactions] = React4.useState(false);
4261
+ const [editing, setEditing] = React5.useState(false);
4262
+ const [editContent, setEditContent] = React5.useState(comment.content);
4263
+ const [showReactions, setShowReactions] = React5.useState(false);
4205
4264
  const isAuthor = comment.author.id === currentUser.id;
4206
4265
  const isPending = comment._status === "pending";
4207
4266
  const isError = comment._status === "error";
@@ -4365,9 +4424,9 @@ function CommentInput({
4365
4424
  compact = false,
4366
4425
  alwaysExpanded = true
4367
4426
  }) {
4368
- const [content, setContent] = React4.useState("");
4369
- const textareaRef = React4.useRef(null);
4370
- React4.useEffect(() => {
4427
+ const [content, setContent] = React5.useState("");
4428
+ const textareaRef = React5.useRef(null);
4429
+ React5.useEffect(() => {
4371
4430
  if (autoFocus && textareaRef.current) {
4372
4431
  textareaRef.current.focus();
4373
4432
  }
@@ -4487,7 +4546,7 @@ function CommentThread({
4487
4546
  displayName: providerUser?.name ?? "",
4488
4547
  avatarUrl: providerUser?.avatarUrl ?? null
4489
4548
  };
4490
- const effectiveOnFetch = React4.useCallback(
4549
+ const effectiveOnFetch = React5.useCallback(
4491
4550
  async (_entityType, _entityId) => {
4492
4551
  if (onFetch) return onFetch(_entityType, _entityId);
4493
4552
  const data = await gql(
@@ -4506,7 +4565,7 @@ function CommentThread({
4506
4565
  // eslint-disable-next-line react-hooks/exhaustive-deps
4507
4566
  [gql, onFetch]
4508
4567
  );
4509
- const effectiveOnAdd = React4.useCallback(
4568
+ const effectiveOnAdd = React5.useCallback(
4510
4569
  async (input) => {
4511
4570
  if (onAdd) return onAdd(input);
4512
4571
  const data = await gql(
@@ -4523,7 +4582,7 @@ function CommentThread({
4523
4582
  // eslint-disable-next-line react-hooks/exhaustive-deps
4524
4583
  [gql, onAdd, currentUser.id]
4525
4584
  );
4526
- const effectiveOnEdit = React4.useCallback(
4585
+ const effectiveOnEdit = React5.useCallback(
4527
4586
  async (commentId, content) => {
4528
4587
  if (onEdit) return onEdit(commentId, content);
4529
4588
  const data = await gql(
@@ -4545,7 +4604,7 @@ function CommentThread({
4545
4604
  },
4546
4605
  [gql, onEdit]
4547
4606
  );
4548
- const effectiveOnDelete = React4.useCallback(
4607
+ const effectiveOnDelete = React5.useCallback(
4549
4608
  async (commentId) => {
4550
4609
  if (onDelete) return onDelete(commentId);
4551
4610
  await gql(
@@ -4556,15 +4615,15 @@ function CommentThread({
4556
4615
  },
4557
4616
  [gql, onDelete]
4558
4617
  );
4559
- const [comments, setComments] = React4.useState(externalComments || []);
4560
- const [loading, setLoading] = React4.useState(!externalComments);
4561
- const [showAll, setShowAll] = React4.useState(false);
4562
- const [replyingTo, setReplyingTo] = React4.useState(null);
4563
- const hasFetchedRef = React4.useRef(false);
4564
- React4.useEffect(() => {
4618
+ const [comments, setComments] = React5.useState(externalComments || []);
4619
+ const [loading, setLoading] = React5.useState(!externalComments);
4620
+ const [showAll, setShowAll] = React5.useState(false);
4621
+ const [replyingTo, setReplyingTo] = React5.useState(null);
4622
+ const hasFetchedRef = React5.useRef(false);
4623
+ React5.useEffect(() => {
4565
4624
  hasFetchedRef.current = false;
4566
4625
  }, [entityId, entityType]);
4567
- React4.useEffect(() => {
4626
+ React5.useEffect(() => {
4568
4627
  if (externalComments) return;
4569
4628
  if (hasFetchedRef.current) return;
4570
4629
  hasFetchedRef.current = true;
@@ -4582,12 +4641,12 @@ function CommentThread({
4582
4641
  cancelled = true;
4583
4642
  };
4584
4643
  }, [entityType, entityId, externalComments, effectiveOnFetch]);
4585
- React4.useEffect(() => {
4644
+ React5.useEffect(() => {
4586
4645
  if (externalComments) {
4587
4646
  setComments(externalComments);
4588
4647
  }
4589
4648
  }, [externalComments]);
4590
- React4.useEffect(() => {
4649
+ React5.useEffect(() => {
4591
4650
  if (externalComments || onFetch) return;
4592
4651
  if (!entityId || entityType !== "task") return;
4593
4652
  const projectsServiceUrl = api?.services?.projects || api?.gatewayUrl;
@@ -4847,7 +4906,7 @@ function CommentThread({
4847
4906
  /* @__PURE__ */ jsx("p", { className: "text-[11px] text-theme-muted mt-0.5", children: "Seja o primeiro a comentar." })
4848
4907
  ] }),
4849
4908
  !loading && visibleComments.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
4850
- visibleComments.map((comment) => /* @__PURE__ */ jsxs(React4.Fragment, { children: [
4909
+ visibleComments.map((comment) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
4851
4910
  /* @__PURE__ */ jsx(
4852
4911
  CommentItem,
4853
4912
  {
@@ -4936,11 +4995,11 @@ function LabelPicker({
4936
4995
  trigger,
4937
4996
  disabled = false
4938
4997
  }) {
4939
- const [open, setOpen] = React4.useState(false);
4940
- const [search, setSearch] = React4.useState("");
4941
- const [isCreating, setIsCreating] = React4.useState(false);
4942
- const [newLabelName, setNewLabelName] = React4.useState("");
4943
- const [newLabelColor, setNewLabelColor] = React4.useState(presetColors[8]);
4998
+ const [open, setOpen] = React5.useState(false);
4999
+ const [search, setSearch] = React5.useState("");
5000
+ const [isCreating, setIsCreating] = React5.useState(false);
5001
+ const [newLabelName, setNewLabelName] = React5.useState("");
5002
+ const [newLabelColor, setNewLabelColor] = React5.useState(presetColors[8]);
4944
5003
  const selectedIds = new Set(selectedLabels.map((l) => l.id));
4945
5004
  const filteredLabels = availableLabels.filter(
4946
5005
  (label) => label.name.toLowerCase().includes(search.toLowerCase())
@@ -5095,8 +5154,8 @@ function AssigneePicker({
5095
5154
  disabled = false,
5096
5155
  maxAssignees
5097
5156
  }) {
5098
- const [open, setOpen] = React4.useState(false);
5099
- const [search, setSearch] = React4.useState("");
5157
+ const [open, setOpen] = React5.useState(false);
5158
+ const [search, setSearch] = React5.useState("");
5100
5159
  const selectedIds = new Set(selectedAssignees.map((a) => a.id));
5101
5160
  const canAddMore = maxAssignees ? selectedAssignees.length < maxAssignees : true;
5102
5161
  const filteredUsers = availableUsers.filter(
@@ -5181,8 +5240,8 @@ var statusColors = {
5181
5240
  DONE: "bg-emerald-500/15 text-emerald-400 border-emerald-500/30"
5182
5241
  };
5183
5242
  function useMediaQuery2(query) {
5184
- const [matches, setMatches] = React4.useState(false);
5185
- React4.useEffect(() => {
5243
+ const [matches, setMatches] = React5.useState(false);
5244
+ React5.useEffect(() => {
5186
5245
  const mql = window.matchMedia(query);
5187
5246
  setMatches(mql.matches);
5188
5247
  const handler = (e) => setMatches(e.matches);
@@ -5199,13 +5258,13 @@ function EditableField({
5199
5258
  inputClassName,
5200
5259
  as: Tag = "span"
5201
5260
  }) {
5202
- const [editing, setEditing] = React4.useState(false);
5203
- const [localValue, setLocalValue] = React4.useState(value);
5204
- const inputRef = React4.useRef(null);
5205
- React4.useEffect(() => {
5261
+ const [editing, setEditing] = React5.useState(false);
5262
+ const [localValue, setLocalValue] = React5.useState(value);
5263
+ const inputRef = React5.useRef(null);
5264
+ React5.useEffect(() => {
5206
5265
  setLocalValue(value);
5207
5266
  }, [value]);
5208
- React4.useEffect(() => {
5267
+ React5.useEffect(() => {
5209
5268
  if (editing && inputRef.current) {
5210
5269
  inputRef.current.focus();
5211
5270
  inputRef.current.select();
@@ -5259,9 +5318,9 @@ function EditableField({
5259
5318
  );
5260
5319
  }
5261
5320
  function EditableDate({ value, onSave, placeholder = "Adicionar data" }) {
5262
- const [editing, setEditing] = React4.useState(false);
5263
- const inputRef = React4.useRef(null);
5264
- React4.useEffect(() => {
5321
+ const [editing, setEditing] = React5.useState(false);
5322
+ const inputRef = React5.useRef(null);
5323
+ React5.useEffect(() => {
5265
5324
  if (editing && inputRef.current) {
5266
5325
  inputRef.current.focus();
5267
5326
  }
@@ -5345,13 +5404,13 @@ function AssigneeItem({ assignee, onRemove, showDelete = true }) {
5345
5404
  ] });
5346
5405
  }
5347
5406
  function SubtaskItem({ subtask, onToggle, onDelete, onUpdate }) {
5348
- const [editing, setEditing] = React4.useState(false);
5349
- const [localTitle, setLocalTitle] = React4.useState(subtask.title);
5350
- const inputRef = React4.useRef(null);
5351
- React4.useEffect(() => {
5407
+ const [editing, setEditing] = React5.useState(false);
5408
+ const [localTitle, setLocalTitle] = React5.useState(subtask.title);
5409
+ const inputRef = React5.useRef(null);
5410
+ React5.useEffect(() => {
5352
5411
  setLocalTitle(subtask.title);
5353
5412
  }, [subtask.title]);
5354
- React4.useEffect(() => {
5413
+ React5.useEffect(() => {
5355
5414
  if (editing && inputRef.current) {
5356
5415
  inputRef.current.focus();
5357
5416
  inputRef.current.select();
@@ -5425,10 +5484,10 @@ function SubtaskItem({ subtask, onToggle, onDelete, onUpdate }) {
5425
5484
  ] });
5426
5485
  }
5427
5486
  function SubtaskAddForm({ onAdd }) {
5428
- const [adding, setAdding] = React4.useState(false);
5429
- const [title, setTitle] = React4.useState("");
5430
- const inputRef = React4.useRef(null);
5431
- React4.useEffect(() => {
5487
+ const [adding, setAdding] = React5.useState(false);
5488
+ const [title, setTitle] = React5.useState("");
5489
+ const inputRef = React5.useRef(null);
5490
+ React5.useEffect(() => {
5432
5491
  if (adding && inputRef.current) {
5433
5492
  inputRef.current.focus();
5434
5493
  }
@@ -5516,7 +5575,7 @@ function SubtaskSection({
5516
5575
  onDelete,
5517
5576
  onUpdate
5518
5577
  }) {
5519
- const [loading, setLoading] = React4.useState(null);
5578
+ const [loading, setLoading] = React5.useState(null);
5520
5579
  const handleAdd = async (title) => {
5521
5580
  if (onAdd) {
5522
5581
  setLoading("add");
@@ -5974,7 +6033,7 @@ function TaskCard({
5974
6033
  className,
5975
6034
  ...props
5976
6035
  }) {
5977
- const [sheetOpen, setSheetOpen] = React4.useState(false);
6036
+ const [sheetOpen, setSheetOpen] = React5.useState(false);
5978
6037
  if (!task) {
5979
6038
  return /* @__PURE__ */ jsx(TaskCardSkeleton, { compact, className });
5980
6039
  }
@@ -6413,8 +6472,33 @@ function TaskCard2({
6413
6472
  isDragging,
6414
6473
  externalDnD,
6415
6474
  onDragStart,
6416
- onDragEnd
6475
+ onDragEnd,
6476
+ onTouchDragStart
6417
6477
  }) {
6478
+ const touchTimerRef = React5.useRef(null);
6479
+ const hasMoved = React5.useRef(false);
6480
+ const handleTouchStart = (e) => {
6481
+ hasMoved.current = false;
6482
+ touchTimerRef.current = setTimeout(() => {
6483
+ if (!hasMoved.current) {
6484
+ onTouchDragStart?.(task, columnId);
6485
+ if (navigator.vibrate) navigator.vibrate(50);
6486
+ }
6487
+ }, 300);
6488
+ };
6489
+ const handleTouchMove = () => {
6490
+ hasMoved.current = true;
6491
+ if (touchTimerRef.current) {
6492
+ clearTimeout(touchTimerRef.current);
6493
+ touchTimerRef.current = null;
6494
+ }
6495
+ };
6496
+ const handleTouchEnd = () => {
6497
+ if (touchTimerRef.current) {
6498
+ clearTimeout(touchTimerRef.current);
6499
+ touchTimerRef.current = null;
6500
+ }
6501
+ };
6418
6502
  const displayLabels = (task.labels || []).map(
6419
6503
  (label) => typeof label === "string" ? label : label.name
6420
6504
  );
@@ -6425,14 +6509,18 @@ function TaskCard2({
6425
6509
  onDragStart: !externalDnD ? (e) => onDragStart?.(e, task, columnId) : void 0,
6426
6510
  onDragEnd: !externalDnD ? onDragEnd : void 0,
6427
6511
  onClick,
6512
+ onTouchStart: handleTouchStart,
6513
+ onTouchMove: handleTouchMove,
6514
+ onTouchEnd: handleTouchEnd,
6428
6515
  "data-task-id": task.id,
6429
6516
  "data-column-id": columnId,
6430
6517
  "data-index": index,
6431
6518
  className: cn(
6432
- "group relative flex flex-col gap-2 p-3 cursor-pointer",
6519
+ "group relative flex flex-col gap-2 p-3 cursor-pointer select-none",
6433
6520
  "bg-theme-subtle backdrop-blur-md border border-theme-subtle",
6434
6521
  "transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] rounded-xl",
6435
6522
  "hover:border-primary-light/30",
6523
+ "active:scale-[0.98]",
6436
6524
  isDragging && "opacity-50 scale-95"
6437
6525
  ),
6438
6526
  children: [
@@ -6444,7 +6532,7 @@ function TaskCard2({
6444
6532
  children: /* @__PURE__ */ jsx(GripVertical, { className: "w-4 h-4" })
6445
6533
  }
6446
6534
  ),
6447
- /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium text-theme pr-6", children: task.title }),
6535
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium text-theme pr-6 line-clamp-2", children: task.title }),
6448
6536
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
6449
6537
  task.priority && /* @__PURE__ */ jsx(
6450
6538
  Badge,
@@ -6485,18 +6573,19 @@ function Column({
6485
6573
  onDragEnd,
6486
6574
  onDragOver,
6487
6575
  onDrop,
6488
- isDropTarget
6576
+ isDropTarget,
6577
+ onTouchDragStart
6489
6578
  }) {
6490
- const [isRenaming, setIsRenaming] = React4.useState(false);
6491
- const [renameValue, setRenameValue] = React4.useState(column.title);
6492
- const renameInputRef = React4.useRef(null);
6493
- const [isAddingTask, setIsAddingTask] = React4.useState(false);
6494
- const [newTaskTitle, setNewTaskTitle] = React4.useState("");
6495
- const taskInputRef = React4.useRef(null);
6496
- React4.useEffect(() => {
6579
+ const [isRenaming, setIsRenaming] = React5.useState(false);
6580
+ const [renameValue, setRenameValue] = React5.useState(column.title);
6581
+ const renameInputRef = React5.useRef(null);
6582
+ const [isAddingTask, setIsAddingTask] = React5.useState(false);
6583
+ const [newTaskTitle, setNewTaskTitle] = React5.useState("");
6584
+ const taskInputRef = React5.useRef(null);
6585
+ React5.useEffect(() => {
6497
6586
  if (isRenaming) renameInputRef.current?.focus();
6498
6587
  }, [isRenaming]);
6499
- React4.useEffect(() => {
6588
+ React5.useEffect(() => {
6500
6589
  if (isAddingTask) taskInputRef.current?.focus();
6501
6590
  }, [isAddingTask]);
6502
6591
  const handleRenameSubmit = () => {
@@ -6540,9 +6629,10 @@ function Column({
6540
6629
  } : void 0,
6541
6630
  onDrop: !externalDnD ? (e) => onDrop?.(e, column.id) : void 0,
6542
6631
  className: cn(
6543
- "flex flex-col w-72 shrink-0 h-full",
6632
+ "flex flex-col w-[280px] sm:w-72 shrink-0 max-h-[calc(100vh-10rem)]",
6544
6633
  "bg-theme-glass backdrop-blur-md border border-theme-subtle rounded-xl",
6545
6634
  "transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)]",
6635
+ "snap-start",
6546
6636
  isDropTarget && "border-primary-light/50 bg-primary/5"
6547
6637
  ),
6548
6638
  children: [
@@ -6578,16 +6668,30 @@ function Column({
6578
6668
  children: column.title
6579
6669
  }
6580
6670
  ),
6581
- /* @__PURE__ */ jsx(
6671
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0", children: /* @__PURE__ */ jsxs(
6582
6672
  Badge,
6583
6673
  {
6584
- variant: "default",
6585
- className: "bg-theme-highlight text-theme-muted border-theme-subtle text-xs shrink-0",
6586
- children: column.tasks.length
6674
+ variant: column.wipLimit && column.tasks.length > column.wipLimit ? "error" : "default",
6675
+ className: cn(
6676
+ "text-xs",
6677
+ column.wipLimit && column.tasks.length > column.wipLimit ? "bg-red-500/20 text-red-400 border-red-500/40 animate-pulse" : "bg-theme-highlight text-theme-muted border-theme-subtle"
6678
+ ),
6679
+ children: [
6680
+ column.tasks.length,
6681
+ column.wipLimit != null && /* @__PURE__ */ jsxs("span", { className: "text-theme-muted", children: [
6682
+ "/",
6683
+ column.wipLimit
6684
+ ] })
6685
+ ]
6587
6686
  }
6588
- )
6687
+ ) })
6688
+ ] }),
6689
+ column.wipLimit != null && column.tasks.length > column.wipLimit && /* @__PURE__ */ jsxs("div", { className: "px-3 py-1.5 bg-red-500/10 border-b border-red-500/20 text-[10px] font-medium text-red-400 text-center", children: [
6690
+ "\u26A0 WIP limit exceeded (",
6691
+ column.tasks.length - column.wipLimit,
6692
+ " over)"
6589
6693
  ] }),
6590
- /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxs("div", { className: "p-2 space-y-2", children: [
6694
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1 min-h-0 [&_[data-radix-scroll-area-viewport]]:!overflow-y-auto [&_[data-radix-scroll-area-viewport]]:scrollbar-none", children: /* @__PURE__ */ jsxs("div", { className: "p-2 space-y-2", children: [
6591
6695
  column.tasks.map((task, index) => {
6592
6696
  const isDragging = draggedTask?.task.id === task.id;
6593
6697
  return /* @__PURE__ */ jsx(
@@ -6600,7 +6704,8 @@ function Column({
6600
6704
  isDragging,
6601
6705
  externalDnD,
6602
6706
  onDragStart,
6603
- onDragEnd
6707
+ onDragEnd,
6708
+ onTouchDragStart
6604
6709
  },
6605
6710
  task.id
6606
6711
  );
@@ -6684,16 +6789,40 @@ function KanbanBoard({
6684
6789
  onAddSubtask,
6685
6790
  onToggleSubtask,
6686
6791
  onDeleteSubtask,
6687
- onUpdateSubtask
6792
+ onUpdateSubtask,
6793
+ onTaskClick
6688
6794
  }) {
6689
- const [draggedTask, setDraggedTask] = React4.useState(null);
6690
- const [dropTargetColumnId, setDropTargetColumnId] = React4.useState(null);
6691
- const [sheetOpen, setSheetOpen] = React4.useState(false);
6692
- const [selectedTask, setSelectedTask] = React4.useState(null);
6693
- const [isAddingColumn, setIsAddingColumn] = React4.useState(false);
6694
- const [newColumnTitle, setNewColumnTitle] = React4.useState("");
6695
- const addColumnInputRef = React4.useRef(null);
6696
- React4.useEffect(() => {
6795
+ const [draggedTask, setDraggedTask] = React5.useState(null);
6796
+ const [dropTargetColumnId, setDropTargetColumnId] = React5.useState(null);
6797
+ const [touchDraggedTask, setTouchDraggedTask] = React5.useState(null);
6798
+ const handleTouchDragStart = React5.useCallback((task, fromColumnId) => {
6799
+ setTouchDraggedTask({ task, fromColumnId });
6800
+ }, []);
6801
+ const handleTouchDrop = React5.useCallback((toColumnId) => {
6802
+ if (!touchDraggedTask) return;
6803
+ const { task, fromColumnId } = touchDraggedTask;
6804
+ if (fromColumnId !== toColumnId) {
6805
+ onTaskMove?.(task.id, fromColumnId, toColumnId, 0);
6806
+ if (onColumnChange) {
6807
+ const newColumns = columns.map((col) => {
6808
+ if (col.id === fromColumnId) return { ...col, tasks: col.tasks.filter((t) => t.id !== task.id) };
6809
+ if (col.id === toColumnId) return { ...col, tasks: [...col.tasks, task] };
6810
+ return col;
6811
+ });
6812
+ onColumnChange(newColumns);
6813
+ }
6814
+ }
6815
+ setTouchDraggedTask(null);
6816
+ }, [touchDraggedTask, columns, onTaskMove, onColumnChange]);
6817
+ const cancelTouchDrag = React5.useCallback(() => setTouchDraggedTask(null), []);
6818
+ const isTouchDragging = touchDraggedTask !== null;
6819
+ const effectiveDraggedTask = draggedTask || touchDraggedTask;
6820
+ const [sheetOpen, setSheetOpen] = React5.useState(false);
6821
+ const [selectedTask, setSelectedTask] = React5.useState(null);
6822
+ const [isAddingColumn, setIsAddingColumn] = React5.useState(false);
6823
+ const [newColumnTitle, setNewColumnTitle] = React5.useState("");
6824
+ const addColumnInputRef = React5.useRef(null);
6825
+ React5.useEffect(() => {
6697
6826
  if (isAddingColumn) addColumnInputRef.current?.focus();
6698
6827
  }, [isAddingColumn]);
6699
6828
  const handleAddColumnSubmit = () => {
@@ -6714,6 +6843,10 @@ function KanbanBoard({
6714
6843
  }
6715
6844
  };
6716
6845
  const handleTaskClick = (task, columnId) => {
6846
+ if (onTaskClick) {
6847
+ onTaskClick(task.id);
6848
+ return;
6849
+ }
6717
6850
  setSelectedTask({ task, columnId });
6718
6851
  setSheetOpen(true);
6719
6852
  };
@@ -6755,8 +6888,9 @@ function KanbanBoard({
6755
6888
  "div",
6756
6889
  {
6757
6890
  className: cn(
6758
- "flex gap-4 p-4 overflow-x-auto h-full min-h-[400px]",
6759
- "bg-void",
6891
+ "flex gap-3 sm:gap-4 p-3 sm:p-4 overflow-x-auto h-full min-h-[400px]",
6892
+ "bg-void snap-x snap-mandatory sm:snap-none",
6893
+ "scrollbar-none",
6760
6894
  className
6761
6895
  ),
6762
6896
  children: [
@@ -6768,12 +6902,13 @@ function KanbanBoard({
6768
6902
  onAddTask,
6769
6903
  onRenameColumn,
6770
6904
  externalDnD,
6771
- draggedTask,
6905
+ draggedTask: effectiveDraggedTask,
6772
6906
  onDragStart: handleDragStart,
6773
6907
  onDragEnd: handleDragEnd,
6774
6908
  onDragOver: handleDragOver,
6775
6909
  onDrop: handleDrop,
6776
- isDropTarget: dropTargetColumnId === column.id
6910
+ isDropTarget: dropTargetColumnId === column.id,
6911
+ onTouchDragStart: handleTouchDragStart
6777
6912
  },
6778
6913
  column.id
6779
6914
  )),
@@ -6834,6 +6969,35 @@ function KanbanBoard({
6834
6969
  ]
6835
6970
  }
6836
6971
  ),
6972
+ isTouchDragging && /* @__PURE__ */ jsxs("div", { className: "fixed inset-x-0 bottom-0 z-50 flex gap-2 p-3 bg-void/90 backdrop-blur-xl border-t border-theme-subtle animate-in slide-in-from-bottom-4 duration-300 sm:hidden", children: [
6973
+ /* @__PURE__ */ jsx("div", { className: "w-full overflow-x-auto scrollbar-none flex gap-2", children: columns.filter((col) => col.id !== touchDraggedTask?.fromColumnId).map((col) => /* @__PURE__ */ jsxs(
6974
+ "button",
6975
+ {
6976
+ onClick: () => handleTouchDrop(col.id),
6977
+ className: cn(
6978
+ "flex-shrink-0 flex items-center gap-2 px-4 py-3 rounded-xl",
6979
+ "bg-theme-glass border border-theme-subtle",
6980
+ "text-sm font-medium text-theme",
6981
+ "active:bg-primary/20 active:border-primary-light/50",
6982
+ "transition-all duration-200"
6983
+ ),
6984
+ children: [
6985
+ col.color && /* @__PURE__ */ jsx("div", { className: "w-2.5 h-2.5 rounded-full shrink-0", style: { backgroundColor: col.color } }),
6986
+ col.title,
6987
+ /* @__PURE__ */ jsx(Badge, { className: "text-[10px] bg-theme-highlight text-theme-muted border-theme-subtle", children: col.tasks.length })
6988
+ ]
6989
+ },
6990
+ col.id
6991
+ )) }),
6992
+ /* @__PURE__ */ jsx(
6993
+ "button",
6994
+ {
6995
+ onClick: cancelTouchDrag,
6996
+ className: "shrink-0 p-3 rounded-xl bg-rose-500/20 text-rose-400 border border-rose-500/30",
6997
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
6998
+ }
6999
+ )
7000
+ ] }),
6837
7001
  /* @__PURE__ */ jsx(
6838
7002
  TaskDetailSheet,
6839
7003
  {
@@ -7022,7 +7186,7 @@ function PlanBadgeFull({
7022
7186
  config.bgColor,
7023
7187
  config.borderColor
7024
7188
  ),
7025
- children: /* @__PURE__ */ jsx("span", { className: config.color, children: React4.cloneElement(config.icon, {
7189
+ children: /* @__PURE__ */ jsx("span", { className: config.color, children: React5.cloneElement(config.icon, {
7026
7190
  className: "w-5 h-5"
7027
7191
  }) })
7028
7192
  }
@@ -7323,7 +7487,7 @@ function Checkbox({
7323
7487
  }
7324
7488
  );
7325
7489
  }
7326
- var Switch = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
7490
+ var Switch = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
7327
7491
  SwitchPrimitive.Root,
7328
7492
  {
7329
7493
  className: cn(
@@ -7667,6 +7831,63 @@ function Toaster({ ...props }) {
7667
7831
  }
7668
7832
  );
7669
7833
  }
7834
+ var CURRENT_USER_KEY = ["current-user"];
7835
+ function readCookieToken() {
7836
+ if (typeof document === "undefined") return null;
7837
+ const match3 = document.cookie.split(";").map((s) => s.trim()).find((s) => s.startsWith("auth_token="));
7838
+ if (!match3) return null;
7839
+ return decodeURIComponent(match3.split("=")[1]);
7840
+ }
7841
+ function readAuthToken() {
7842
+ return readCookieToken() ?? (typeof localStorage !== "undefined" ? localStorage.getItem("organify_access_token") : null);
7843
+ }
7844
+ async function fetchCurrentUser() {
7845
+ const res = await fetch("/api/auth/me", { credentials: "include" });
7846
+ if (!res.ok) {
7847
+ if (res.status === 401) {
7848
+ window.location.href = "/login";
7849
+ throw new Error("UNAUTHENTICATED");
7850
+ }
7851
+ throw new Error(`HTTP ${res.status}`);
7852
+ }
7853
+ const data = await res.json();
7854
+ return {
7855
+ id: String(data.id),
7856
+ name: data.name,
7857
+ email: data.email,
7858
+ avatarUrl: data.avatar ?? null,
7859
+ role: data.role
7860
+ };
7861
+ }
7862
+ function useCurrentUser() {
7863
+ const queryClient = useQueryClient();
7864
+ const { data: user, isLoading, error } = useQuery({
7865
+ queryKey: CURRENT_USER_KEY,
7866
+ queryFn: fetchCurrentUser,
7867
+ staleTime: 5 * 60 * 1e3,
7868
+ // 5 minutes — user data rarely changes
7869
+ gcTime: 10 * 60 * 1e3,
7870
+ // Keep in GC for 10 min
7871
+ retry: (failureCount, err) => {
7872
+ if (err instanceof Error && err.message === "UNAUTHENTICATED") return false;
7873
+ return failureCount < 2;
7874
+ },
7875
+ refetchOnWindowFocus: true,
7876
+ refetchOnMount: false
7877
+ // Use cache if not stale
7878
+ });
7879
+ const authToken = typeof window !== "undefined" ? readAuthToken() : null;
7880
+ const refetch = async () => {
7881
+ await queryClient.invalidateQueries({ queryKey: CURRENT_USER_KEY });
7882
+ };
7883
+ return {
7884
+ user: user ?? null,
7885
+ authToken,
7886
+ loading: isLoading,
7887
+ error: error instanceof Error ? error.message : null,
7888
+ refetch
7889
+ };
7890
+ }
7670
7891
  var CREATE_PROJECT_MUTATION = `
7671
7892
  mutation CreateProject($input: CreateProjectInput!) {
7672
7893
  createProject(input: $input) {
@@ -7684,11 +7905,11 @@ function CreateProjectDialog({
7684
7905
  const gql = useOrganifyGql();
7685
7906
  const { workspace } = useOrganifyWorkspace();
7686
7907
  const { navigate } = useOrganifyNavigation();
7687
- const [name, setName] = React4.useState("");
7688
- const [type, setType] = React4.useState("KANBAN");
7689
- const [error, setError] = React4.useState("");
7690
- const [loading, setLoading] = React4.useState(false);
7691
- React4.useEffect(() => {
7908
+ const [name, setName] = React5.useState("");
7909
+ const [type, setType] = React5.useState("KANBAN");
7910
+ const [error, setError] = React5.useState("");
7911
+ const [loading, setLoading] = React5.useState(false);
7912
+ React5.useEffect(() => {
7692
7913
  if (open) {
7693
7914
  setName("");
7694
7915
  setType("KANBAN");
@@ -7721,7 +7942,7 @@ function CreateProjectDialog({
7721
7942
  onOpenChange(false);
7722
7943
  onCreated?.(data.createProject);
7723
7944
  if (!onCreated && navigate) {
7724
- navigate(`/board/${workspace.slug}/projects/${data.createProject.slug}`);
7945
+ navigate(`/${workspace.slug}/projects/${data.createProject.slug}`);
7725
7946
  }
7726
7947
  } catch (err) {
7727
7948
  setError(err.message || "Failed to create project");
@@ -7804,10 +8025,10 @@ function CreateWorkspaceDialog({
7804
8025
  }) {
7805
8026
  const gql = useOrganifyGql();
7806
8027
  const { navigate } = useOrganifyNavigation();
7807
- const [name, setName] = React4.useState("");
7808
- const [error, setError] = React4.useState("");
7809
- const [loading, setLoading] = React4.useState(false);
7810
- React4.useEffect(() => {
8028
+ const [name, setName] = React5.useState("");
8029
+ const [error, setError] = React5.useState("");
8030
+ const [loading, setLoading] = React5.useState(false);
8031
+ React5.useEffect(() => {
7811
8032
  if (open) {
7812
8033
  setName("");
7813
8034
  setError("");
@@ -7897,13 +8118,13 @@ function CreateSprintDialog({
7897
8118
  const gql = useOrganifyGql();
7898
8119
  const { project } = useOrganifyProject();
7899
8120
  const effectiveProjectId = propProjectId ?? project?.id;
7900
- const [name, setName] = React4.useState("");
7901
- const [goal, setGoal] = React4.useState("");
7902
- const [startDate, setStartDate] = React4.useState("");
7903
- const [endDate, setEndDate] = React4.useState("");
7904
- const [error, setError] = React4.useState("");
7905
- const [loading, setLoading] = React4.useState(false);
7906
- React4.useEffect(() => {
8121
+ const [name, setName] = React5.useState("");
8122
+ const [goal, setGoal] = React5.useState("");
8123
+ const [startDate, setStartDate] = React5.useState("");
8124
+ const [endDate, setEndDate] = React5.useState("");
8125
+ const [error, setError] = React5.useState("");
8126
+ const [loading, setLoading] = React5.useState(false);
8127
+ React5.useEffect(() => {
7907
8128
  if (open) {
7908
8129
  setName("");
7909
8130
  setGoal("");
@@ -8044,12 +8265,12 @@ function CreateEpicDialog({
8044
8265
  const gql = useOrganifyGql();
8045
8266
  const { project } = useOrganifyProject();
8046
8267
  const effectiveProjectId = propProjectId ?? project?.id;
8047
- const [name, setName] = React4.useState("");
8048
- const [description, setDescription] = React4.useState("");
8049
- const [color, setColor] = React4.useState(EPIC_COLORS[0]);
8050
- const [error, setError] = React4.useState("");
8051
- const [loading, setLoading] = React4.useState(false);
8052
- React4.useEffect(() => {
8268
+ const [name, setName] = React5.useState("");
8269
+ const [description, setDescription] = React5.useState("");
8270
+ const [color, setColor] = React5.useState(EPIC_COLORS[0]);
8271
+ const [error, setError] = React5.useState("");
8272
+ const [loading, setLoading] = React5.useState(false);
8273
+ React5.useEffect(() => {
8053
8274
  if (open) {
8054
8275
  setName("");
8055
8276
  setDescription("");
@@ -8185,11 +8406,11 @@ function CreateTaskDialog({
8185
8406
  const gql = useOrganifyGql();
8186
8407
  const projectCtx = useOrganifyProject();
8187
8408
  const projectId = projectIdProp ?? projectCtx?.project?.id;
8188
- const [title, setTitle] = React4.useState("");
8189
- const [priority, setPriority] = React4.useState("NONE");
8190
- const [error, setError] = React4.useState("");
8191
- const [loading, setLoading] = React4.useState(false);
8192
- React4.useEffect(() => {
8409
+ const [title, setTitle] = React5.useState("");
8410
+ const [priority, setPriority] = React5.useState("NONE");
8411
+ const [error, setError] = React5.useState("");
8412
+ const [loading, setLoading] = React5.useState(false);
8413
+ React5.useEffect(() => {
8193
8414
  if (open) {
8194
8415
  setTitle("");
8195
8416
  setPriority("NONE");
@@ -8301,11 +8522,11 @@ function InviteMemberDialog({
8301
8522
  const gql = useOrganifyGql();
8302
8523
  const { workspace } = useOrganifyWorkspace();
8303
8524
  const effectiveWsId = propWorkspaceId ?? workspace?.id;
8304
- const [email, setEmail] = React4.useState("");
8305
- const [role, setRole] = React4.useState("MEMBER");
8306
- const [error, setError] = React4.useState("");
8307
- const [loading, setLoading] = React4.useState(false);
8308
- React4.useEffect(() => {
8525
+ const [email, setEmail] = React5.useState("");
8526
+ const [role, setRole] = React5.useState("MEMBER");
8527
+ const [error, setError] = React5.useState("");
8528
+ const [loading, setLoading] = React5.useState(false);
8529
+ React5.useEffect(() => {
8309
8530
  if (open) {
8310
8531
  setEmail("");
8311
8532
  setRole("MEMBER");
@@ -8417,6 +8638,6 @@ function InviteMemberDialog({
8417
8638
  );
8418
8639
  }
8419
8640
 
8420
- export { BoardSkeleton, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatSkeleton, Checkbox, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, CommentItem, CommentThread, CreateEpicDialog, CreateProjectDialog, CreateSprintDialog, CreateTaskDialog, CreateWorkspaceDialog, DashboardSkeleton, Dock, DockButton, DockSidebar, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DueDateBadge, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, GeometricIcon, GlassPanel, InviteMemberDialog, KanbanBoard, Column as KanbanColumn, TaskCard2 as KanbanTaskCard, Logo, MemberListSkeleton, MetricCard, MetricCardSkeleton, PlanBadge, Progress, ProjectListSkeleton, SettingsSkeleton, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SidebarButton, SidebarEntry, SprintSkeleton, StatusPill, StepCircle, StepConnector, Stepper, Switch, TaskCard, TaskCardSkeleton, TaskDetailDialog, TaskDetailPanel, TaskDetailSheet, TaskListSkeleton, Toast, ToastProgress, Toaster, Toggle, UserAvatar, WorkspaceSwitcher, emptyMediaVariants, geometricIconVariants, glassPanelVariants, metricCardVariants, priorityConfig, statusPillVariants, useMediaQuery };
8641
+ export { BoardSkeleton, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, CURRENT_USER_KEY, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatSkeleton, Checkbox, Collapsible, CollapsibleContent2 as CollapsibleContent, CollapsibleTrigger2 as CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, CommentItem, CommentThread, CreateEpicDialog, CreateProjectDialog, CreateSprintDialog, CreateTaskDialog, CreateWorkspaceDialog, DashboardSkeleton, Dock, DockButton, DockSidebar, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DueDateBadge, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, GeometricIcon, GlassPanel, InviteMemberDialog, KanbanBoard, Column as KanbanColumn, TaskCard2 as KanbanTaskCard, Logo, MemberListSkeleton, MetricCard, MetricCardSkeleton, PlanBadge, Progress, ProjectListSkeleton, SettingsSkeleton, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SidebarButton, SidebarEntry, SprintSkeleton, StatusPill, StepCircle, StepConnector, Stepper, Switch, TaskCard, TaskCardSkeleton, TaskDetailDialog, TaskDetailPanel, TaskDetailSheet, TaskListSkeleton, Toast, ToastProgress, Toaster, Toggle, UserAvatar, WorkspaceSwitcher, emptyMediaVariants, geometricIconVariants, glassPanelVariants, metricCardVariants, priorityConfig, statusPillVariants, useCurrentUser, useMediaQuery };
8421
8642
  //# sourceMappingURL=index.js.map
8422
8643
  //# sourceMappingURL=index.js.map