indas-ui 0.1.3 → 0.1.5

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.
Files changed (133) hide show
  1. package/dist/auth.d.ts +1 -0
  2. package/dist/auth.js +5 -0
  3. package/dist/cjs/auth.js +10 -0
  4. package/dist/cjs/components/chat/ai-messages.js +3 -2
  5. package/dist/cjs/components/chat/chat.js +4 -3
  6. package/dist/cjs/components/dashboard/FilterBar.js +31 -8
  7. package/dist/cjs/components/dashboard/charts/Area.js +6 -4
  8. package/dist/cjs/components/dashboard/charts/Bar.js +6 -4
  9. package/dist/cjs/components/dashboard/charts/FlowMap.js +2 -1
  10. package/dist/cjs/components/dashboard/charts/Funnel.js +2 -1
  11. package/dist/cjs/components/dashboard/charts/GeoMap.js +2 -1
  12. package/dist/cjs/components/dashboard/charts/Line.js +5 -3
  13. package/dist/cjs/components/dashboard/charts/Pareto.js +2 -1
  14. package/dist/cjs/components/dashboard/charts/Sankey.js +4 -3
  15. package/dist/cjs/components/dashboard/charts/Sparkline.js +2 -1
  16. package/dist/cjs/components/dashboard/charts/Treemap.js +2 -1
  17. package/dist/cjs/components/dashboard/charts/index.js +7 -1
  18. package/dist/cjs/components/dashboard/kpi/KpiBreakdown.js +3 -2
  19. package/dist/cjs/components/dashboard/kpi/KpiComparison.js +2 -1
  20. package/dist/cjs/components/dashboard/kpi/KpiQuickStats.js +2 -1
  21. package/dist/cjs/components/dashboard/kpi/KpiRating.js +3 -2
  22. package/dist/cjs/components/dashboard/time-range.js +71 -27
  23. package/dist/cjs/components/datagrid/columns/ColumnContextMenu.js +2 -1
  24. package/dist/cjs/components/datagrid/controls/menu/view/CardView.js +2 -1
  25. package/dist/cjs/components/datagrid/controls/menu/view/ChartView.js +2 -1
  26. package/dist/cjs/components/datagrid/utils/grid-helpers.js +6 -5
  27. package/dist/cjs/components/layout/footer.js +2 -1
  28. package/dist/cjs/components/layout/side-panel.js +4 -1
  29. package/dist/cjs/components/layout/sidebar.js +17 -2
  30. package/dist/cjs/components/messaging/messaging-panel-content.js +2 -2
  31. package/dist/cjs/components/ui/display/stats-card.js +2 -1
  32. package/dist/cjs/components/ui/navigation/tabs.js +4 -1
  33. package/dist/cjs/contexts/EmailContext.js +3 -2
  34. package/dist/cjs/contexts/MessagingContext.js +2 -2
  35. package/dist/cjs/contexts/NavigationContext.js +17 -2
  36. package/dist/cjs/contexts/NotificationContext.js +2 -2
  37. package/dist/cjs/contexts/SessionAdapterContext.js +8 -0
  38. package/dist/cjs/hooks/useApproval.js +2 -2
  39. package/dist/cjs/hooks/useBusinessAudit.js +2 -2
  40. package/dist/cjs/hooks/useDraft.js +2 -2
  41. package/dist/cjs/lib/api/utility/menu.js +8 -7
  42. package/dist/cjs/lib/number-format.js +60 -0
  43. package/dist/cjs/lib/utils.js +4 -3
  44. package/dist/cjs/providers.js +3 -3
  45. package/dist/components/auth/index.js +0 -1
  46. package/dist/components/chat/ai-messages.js +3 -2
  47. package/dist/components/chat/chat.js +4 -3
  48. package/dist/components/chat/index.js +0 -1
  49. package/dist/components/dashboard/FilterBar.d.ts +5 -1
  50. package/dist/components/dashboard/FilterBar.js +31 -8
  51. package/dist/components/dashboard/charts/Area.d.ts +5 -1
  52. package/dist/components/dashboard/charts/Area.js +6 -4
  53. package/dist/components/dashboard/charts/Bar.d.ts +5 -1
  54. package/dist/components/dashboard/charts/Bar.js +6 -4
  55. package/dist/components/dashboard/charts/FlowMap.js +2 -1
  56. package/dist/components/dashboard/charts/Funnel.js +2 -1
  57. package/dist/components/dashboard/charts/GeoMap.js +2 -1
  58. package/dist/components/dashboard/charts/Line.d.ts +5 -1
  59. package/dist/components/dashboard/charts/Line.js +5 -3
  60. package/dist/components/dashboard/charts/Pareto.js +2 -1
  61. package/dist/components/dashboard/charts/Sankey.js +4 -3
  62. package/dist/components/dashboard/charts/Sparkline.js +2 -1
  63. package/dist/components/dashboard/charts/Treemap.js +2 -1
  64. package/dist/components/dashboard/charts/index.d.ts +1 -0
  65. package/dist/components/dashboard/charts/index.js +2 -1
  66. package/dist/components/dashboard/index.js +0 -1
  67. package/dist/components/dashboard/kpi/KpiBreakdown.js +3 -2
  68. package/dist/components/dashboard/kpi/KpiComparison.js +2 -1
  69. package/dist/components/dashboard/kpi/KpiQuickStats.js +2 -1
  70. package/dist/components/dashboard/kpi/KpiRating.js +3 -2
  71. package/dist/components/dashboard/kpi/index.js +0 -1
  72. package/dist/components/dashboard/time-range.d.ts +12 -1
  73. package/dist/components/dashboard/time-range.js +71 -27
  74. package/dist/components/datagrid/cells/index.js +0 -1
  75. package/dist/components/datagrid/columns/ColumnContextMenu.js +2 -1
  76. package/dist/components/datagrid/controls/menu/grouping/index.js +0 -1
  77. package/dist/components/datagrid/controls/menu/index.js +0 -1
  78. package/dist/components/datagrid/controls/menu/view/CardView.js +2 -1
  79. package/dist/components/datagrid/controls/menu/view/ChartView.js +2 -1
  80. package/dist/components/datagrid/index.js +0 -1
  81. package/dist/components/datagrid/utils/grid-helpers.d.ts +1 -1
  82. package/dist/components/datagrid/utils/grid-helpers.js +3 -2
  83. package/dist/components/forms/date-picker/index.js +0 -1
  84. package/dist/components/forms/dropdown/index.js +0 -1
  85. package/dist/components/forms/file-attachment/index.js +0 -1
  86. package/dist/components/forms/index.js +0 -1
  87. package/dist/components/forms/remark-input/index.js +0 -1
  88. package/dist/components/index.js +0 -1
  89. package/dist/components/layout/footer.js +2 -1
  90. package/dist/components/layout/index.js +0 -1
  91. package/dist/components/layout/side-panel.js +4 -1
  92. package/dist/components/layout/sidebar.js +17 -2
  93. package/dist/components/master/editable-cells/index.js +0 -1
  94. package/dist/components/master/index.js +0 -1
  95. package/dist/components/messaging/messaging-panel-content.js +2 -2
  96. package/dist/components/modals/index.js +0 -1
  97. package/dist/components/ui/display/stats-card.js +2 -1
  98. package/dist/components/ui/feedback/badge.d.ts +1 -1
  99. package/dist/components/ui/index.js +0 -1
  100. package/dist/components/ui/navigation/tabs.js +4 -1
  101. package/dist/contexts/EmailContext.js +3 -2
  102. package/dist/contexts/MessagingContext.js +2 -2
  103. package/dist/contexts/NavigationContext.js +17 -2
  104. package/dist/contexts/NotificationContext.js +2 -2
  105. package/dist/contexts/SessionAdapterContext.d.ts +5 -0
  106. package/dist/contexts/SessionAdapterContext.js +7 -0
  107. package/dist/hooks/index.js +0 -1
  108. package/dist/hooks/useApproval.js +2 -2
  109. package/dist/hooks/useBusinessAudit.js +2 -2
  110. package/dist/hooks/useDraft.js +2 -2
  111. package/dist/index.js +0 -1
  112. package/dist/lib/api/activity/audit/index.js +0 -1
  113. package/dist/lib/api/activity/email/index.js +0 -1
  114. package/dist/lib/api/activity/index.js +0 -1
  115. package/dist/lib/api/activity/notifications/index.js +0 -1
  116. package/dist/lib/api/activity/remarks/index.js +0 -1
  117. package/dist/lib/api/approval/index.js +0 -1
  118. package/dist/lib/api/auth/index.js +0 -1
  119. package/dist/lib/api/core/index.js +0 -1
  120. package/dist/lib/api/draft/index.js +0 -1
  121. package/dist/lib/api/enquiry/index.js +0 -1
  122. package/dist/lib/api/estimation/index.js +0 -1
  123. package/dist/lib/api/index.js +0 -1
  124. package/dist/lib/api/master/index.js +0 -1
  125. package/dist/lib/api/utility/index.js +0 -1
  126. package/dist/lib/api/utility/menu.d.ts +7 -3
  127. package/dist/lib/api/utility/menu.js +8 -7
  128. package/dist/lib/number-format.d.ts +18 -0
  129. package/dist/lib/number-format.js +54 -0
  130. package/dist/lib/utils.js +4 -3
  131. package/dist/providers.d.ts +0 -1
  132. package/dist/providers.js +2 -2
  133. package/package.json +23 -8
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Main component index - exports all design system components
3
2
  export * from './ui/index.js';
4
3
  export * from './layout/index.js';
@@ -2,6 +2,7 @@
2
2
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
3
  import { cn } from "../../lib/utils.js";
4
4
  import { useTranslation } from "../../hooks/useTranslation.js";
5
+ import { formatNumber } from '../../lib/number-format.js';
5
6
  export function Footer({ kpis, actions, iconButtons, leftContent, className, sticky = false, compact = false, padding, variant = 'page', gradient = false }) {
6
7
  if (!kpis && !actions && !leftContent && !iconButtons) {
7
8
  return null;
@@ -41,7 +42,7 @@ export function Footer({ kpis, actions, iconButtons, leftContent, className, sti
41
42
  padding === 'normal' ? "py-3 sm:py-4" :
42
43
  compact ? "py-1" :
43
44
  isModal ? "py-1.5 sm:py-2" : "py-3 sm:py-4"), children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:gap-4 w-full sm:w-auto", children: [leftContent, kpis && kpis.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-3 sm:gap-6 md:gap-8", children: kpis.map((kpi) => (_jsxs("div", { className: cn("flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm", kpi.onClick && "cursor-pointer hover:text-[rgb(var(--color-primary-hover))] transition-colors"), onClick: kpi.onClick, children: [_jsxs("span", { className: "text-[rgb(var(--fg-muted))] whitespace-nowrap", children: [kpi.label, ":"] }), _jsx("span", { className: "font-medium text-[rgb(var(--fg-default))]", children: typeof kpi.value === 'number'
44
- ? kpi.value.toLocaleString()
45
+ ? formatNumber(kpi.value)
45
46
  : kpi.value }), kpi.trend && (_jsxs("span", { className: cn("text-xs font-medium", kpi.trend.direction === 'up'
46
47
  ? "text-[rgb(var(--color-success))]"
47
48
  : "text-[rgb(var(--color-error))]"), children: [kpi.trend.direction === 'up' ? '↑' : '↓', Math.abs(kpi.trend.value), "%"] }))] }, kpi.id))) }))] }), iconButtons && iconButtons.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-2", children: iconButtons.map((button) => {
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Layout Components - Centralized exports
3
2
  export { DynamicSidebar } from './sidebar.js';
4
3
  export { Footer, CompactFooter } from './footer.js';
@@ -107,7 +107,10 @@ export function SidePanel({ title, icon: Icon, categories, activeItemId, onItemS
107
107
  return _jsx(ItemIcon, { className: "w-4 h-4 shrink-0 opacity-50" });
108
108
  })(), _jsx("span", { className: "truncate flex-1", children: item.label }), item.badge !== undefined && item.badge !== 0 && (_jsx("span", { className: cn("text-xs px-1.5 py-0.5 rounded-full shrink-0 tabular-nums", activeItemId === item.id
109
109
  ? "bg-[rgb(var(--color-primary))] text-white"
110
- : "bg-[rgb(var(--bg-subtle))] text-[rgb(var(--fg-muted))]"), children: item.badge }))] }), itemContextMenu && (_jsx("button", { onClick: (e) => handleMenuToggle(e, item.id), className: "absolute right-1.5 top-1/2 -translate-y-1/2 w-6 h-6 rounded-md text-[rgb(var(--fg-muted))]/50 flex items-center justify-center hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-all opacity-0 group-hover:opacity-100", children: _jsx(MoreVertical, { className: "w-3.5 h-3.5" }) }))] }, item.id))) }))] }, category.id))))] })) }), footer && !isCollapsed && (_jsx("div", { className: cn("shrink-0 border-t border-[rgb(var(--bd-default))] transition-opacity duration-200", isAnimating ? "opacity-0" : "opacity-100"), children: footer }))] }), menuOpen !== null && itemContextMenu && (_jsx("div", { ref: menuRef, className: "fixed bg-[rgb(var(--bg-surface))] border border-[rgb(var(--bd-default))] rounded-lg min-w-[170px] shadow-lg overflow-hidden z-[9999]", style: {
110
+ : "bg-[rgb(var(--bg-subtle))] text-[rgb(var(--fg-muted))]"), children: item.badge }))] }), itemContextMenu && (
111
+ // Named for screen readers, and shown on keyboard focus as well as hover:
112
+ // it was an unlabelled button nobody tabbing could see.
113
+ _jsx("button", { type: "button", onClick: (e) => handleMenuToggle(e, item.id), "aria-label": `More actions for ${item.label}`, "aria-haspopup": "menu", "aria-expanded": menuOpen === item.id, className: "absolute right-1.5 top-1/2 -translate-y-1/2 w-6 h-6 rounded-md text-[rgb(var(--fg-muted))]/50 flex items-center justify-center hover:text-[rgb(var(--fg-default))] hover:bg-[rgb(var(--bg-subtle))] transition-all opacity-0 group-hover:opacity-100 focus-visible:opacity-100", children: _jsx(MoreVertical, { className: "w-3.5 h-3.5" }) }))] }, item.id))) }))] }, category.id))))] })) }), footer && !isCollapsed && (_jsx("div", { className: cn("shrink-0 border-t border-[rgb(var(--bd-default))] transition-opacity duration-200", isAnimating ? "opacity-0" : "opacity-100"), children: footer }))] }), menuOpen !== null && itemContextMenu && (_jsx("div", { ref: menuRef, className: "fixed bg-[rgb(var(--bg-surface))] border border-[rgb(var(--bd-default))] rounded-lg min-w-[170px] shadow-lg overflow-hidden z-[9999]", style: {
111
114
  left: `${menuPosition.left}px`,
112
115
  top: `${menuPosition.top}px`,
113
116
  transform: 'translateY(-50%)'
@@ -13,6 +13,7 @@ import '../../styles/sidebar-scrollbar.css';
13
13
  import { createPortal } from 'react-dom';
14
14
  import { SidebarContextMenu, SidebarGroupContextMenu } from './SidebarContextMenu.js';
15
15
  import { useSidebarPreferences } from '../../contexts/SidebarPreferencesContext.js';
16
+ import { useOptionalSessionAdapter } from '../../contexts/SessionAdapterContext.js';
16
17
  // Route mapping imported from shared utility: getModuleRoutePath
17
18
  // Routes that support quick-create via sidebar "+" button.
18
19
  // When clicked, navigates to {route}?create=true — the page opens its create modal.
@@ -123,6 +124,14 @@ export function DynamicSidebar({ isCollapsed = false, onMenuItemClick, onToggleS
123
124
  const [expandedSubModules, setExpandedSubModules] = React.useState([]);
124
125
  const [loading, setLoading] = React.useState(true);
125
126
  const [error, setError] = React.useState(null);
127
+ // The menu API needs the signed-in session (company credentials). Read from the session adapter,
128
+ // not next-auth, so the library does not require next-auth; held in a ref so a fresh session
129
+ // object does not refetch the menu, only the session arriving or leaving does.
130
+ const sessionAdapter = useOptionalSessionAdapter();
131
+ const sessionRef = React.useRef(sessionAdapter?.data ?? null);
132
+ sessionRef.current = sessionAdapter?.data ?? null;
133
+ const hasSession = Boolean(sessionAdapter?.data);
134
+ const sessionStatus = sessionAdapter?.status ?? 'unauthenticated';
126
135
  const [needsScroll, setNeedsScroll] = React.useState(false);
127
136
  const navRef = React.useRef(null);
128
137
  const [hoveredGroup, setHoveredGroup] = React.useState(null);
@@ -164,6 +173,12 @@ export function DynamicSidebar({ isCollapsed = false, onMenuItemClick, onToggleS
164
173
  if (!companyId || !userId) {
165
174
  return;
166
175
  }
176
+ // No session yet: wait for it (this runs again when it arrives); none at all: no menu.
177
+ if (!hasSession) {
178
+ if (sessionStatus !== 'loading')
179
+ setLoading(false);
180
+ return;
181
+ }
167
182
  let isCancelled = false;
168
183
  const fetchNavigation = async () => {
169
184
  // Check cache first - include session start time to invalidate cache across logins
@@ -189,7 +204,7 @@ export function DynamicSidebar({ isCollapsed = false, onMenuItemClick, onToggleS
189
204
  try {
190
205
  setLoading(true);
191
206
  setError(null);
192
- const navigation = await getDynamicNavigation(companyId, userId);
207
+ const navigation = await getDynamicNavigation(companyId, userId, sessionRef.current);
193
208
  if (!isCancelled) {
194
209
  setGroupedModules(navigation);
195
210
  // Cache the result
@@ -217,7 +232,7 @@ export function DynamicSidebar({ isCollapsed = false, onMenuItemClick, onToggleS
217
232
  clearTimeout(hoverTimeoutRef.current);
218
233
  }
219
234
  };
220
- }, [companyId, userId]);
235
+ }, [companyId, userId, hasSession, sessionStatus]);
221
236
  // Auto-open parent items based on current path (optimized)
222
237
  React.useEffect(() => {
223
238
  if (groupedModules.length === 0)
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  /**
3
2
  * Unified Editable Cell Components
4
3
  *
@@ -1,3 +1,2 @@
1
- 'use client';
2
1
  // Editable Cells
3
2
  export { EditableCell, EditableTextCell, EditableDecimalCell } from './editable-cells/index.js';
@@ -5,14 +5,14 @@ import { useRouter } from 'next/navigation';
5
5
  import { MessageSquare, Send, RefreshCw } from 'lucide-react';
6
6
  import { cn } from '../../lib/utils.js';
7
7
  import { useLanguage } from '../../contexts/LanguageContext.js';
8
- import { useSession } from 'next-auth/react';
8
+ import { useOptionalSessionAdapter } from '../../contexts/SessionAdapterContext.js';
9
9
  import { useMessaging } from '../../contexts/MessagingContext.js';
10
10
  import { parseParticipants } from '../../lib/api/activity/messaging/messaging.js';
11
11
  import { getAvatarColor, getInitials, formatRelativeTime } from './conversation-list.js';
12
12
  import { Button } from '../ui/index.js';
13
13
  export function MessagingPanelContent({ onClose }) {
14
14
  const { t } = useLanguage();
15
- const { data: session } = useSession();
15
+ const session = useOptionalSessionAdapter()?.data ?? null;
16
16
  const { state, actions } = useMessaging();
17
17
  const router = useRouter();
18
18
  const currentUserId = String(session?.user?.UserID || session?.user?.userID || '');
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Modal components exports
3
2
  export { APIConfigModal } from './api-config-modal.js';
4
3
  export { GlobalAPIConfigModal } from './global-api-config-modal.js';
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { TrendingUp, TrendingDown } from "lucide-react";
4
4
  import { cn } from "../../../lib/utils.js";
5
5
  import { Card } from "./card.js";
6
+ import { formatNumber } from '../../../lib/number-format.js';
6
7
  const iconVariants = {
7
8
  default: "bg-primary text-white",
8
9
  accent: "bg-accent text-white",
@@ -12,7 +13,7 @@ const iconVariants = {
12
13
  export function StatsCard({ title, value, icon: Icon, trend, description, className, variant = 'default' }) {
13
14
  const TrendIcon = trend?.direction === 'up' ? TrendingUp : TrendingDown;
14
15
  const trendColor = trend?.direction === 'up' ? 'text-success' : 'text-error';
15
- return (_jsx(Card, { className: cn("p-6 min-h-[140px] w-full", className), children: _jsxs("div", { className: "flex items-start gap-4", children: [Icon && (_jsx("div", { className: cn("flex-shrink-0 p-3 rounded-lg", iconVariants[variant]), children: _jsx(Icon, { className: "h-6 w-6" }) })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-baseline flex-wrap gap-2", children: [_jsx("p", { className: "text-2xl font-semibold text-fg-default truncate", children: typeof value === 'number' ? value.toLocaleString() : value }), trend && (_jsxs("div", { className: cn("flex items-center text-sm font-semibold", trendColor), children: [_jsx(TrendIcon, { className: "h-4 w-4 flex-shrink-0" }), _jsxs("span", { className: "ml-1", children: [Math.abs(trend.value), "%", trend.label && ` ${trend.label}`] })] }))] }), _jsx("p", { className: "text-sm font-medium text-fg-muted mt-2 line-clamp-2", children: title }), description && (_jsx("p", { className: "text-sm text-fg-subtle mt-1 line-clamp-1", children: description }))] })] }) }));
16
+ return (_jsx(Card, { className: cn("p-6 min-h-[140px] w-full", className), children: _jsxs("div", { className: "flex items-start gap-4", children: [Icon && (_jsx("div", { className: cn("flex-shrink-0 p-3 rounded-lg", iconVariants[variant]), children: _jsx(Icon, { className: "h-6 w-6" }) })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-baseline flex-wrap gap-2", children: [_jsx("p", { className: "text-2xl font-semibold text-fg-default truncate", children: typeof value === 'number' ? formatNumber(value) : value }), trend && (_jsxs("div", { className: cn("flex items-center text-sm font-semibold", trendColor), children: [_jsx(TrendIcon, { className: "h-4 w-4 flex-shrink-0" }), _jsxs("span", { className: "ml-1", children: [Math.abs(trend.value), "%", trend.label && ` ${trend.label}`] })] }))] }), _jsx("p", { className: "text-sm font-medium text-fg-muted mt-2 line-clamp-2", children: title }), description && (_jsx("p", { className: "text-sm text-fg-subtle mt-1 line-clamp-1", children: description }))] })] }) }));
16
17
  }
17
18
  export function StatsGrid({ children, columns = 4, className }) {
18
19
  const gridCols = {
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  declare const badgeVariants: (props?: {
4
- variant?: "secondary" | "outline" | "destructive" | "warning" | "success" | "orange" | "cyan" | "indigo" | "lime" | "pink" | "purple" | "teal" | "violet" | "default" | "department" | "module" | "danger" | "info" | "amber" | "emerald" | "sky" | "rose" | "primary-selected" | "warning-selected" | "success-selected" | "orange-selected" | "error-selected" | "primary-unselected" | "warning-unselected" | "success-unselected" | "orange-unselected" | "error-unselected";
4
+ variant?: "secondary" | "outline" | "destructive" | "warning" | "success" | "orange" | "cyan" | "indigo" | "lime" | "pink" | "purple" | "teal" | "violet" | "default" | "module" | "info" | "department" | "danger" | "amber" | "emerald" | "sky" | "rose" | "primary-selected" | "warning-selected" | "success-selected" | "orange-selected" | "error-selected" | "primary-unselected" | "warning-unselected" | "success-unselected" | "orange-unselected" | "error-unselected";
5
5
  size?: "sm" | "lg" | "default";
6
6
  } & import("class-variance-authority/types").ClassProp) => string;
7
7
  export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // UI Components - Centralized exports with organized folder structure
3
2
  // ============================================================================
4
3
  // FORMS
@@ -38,7 +38,10 @@ export function Tabs({ tabs, activeTab, onTabChange, fullWidth = false, classNam
38
38
  ? 'bg-[rgb(var(--color-primary))] text-white border-[rgb(var(--color-primary))]'
39
39
  : 'bg-[rgb(var(--bg-surface))] text-[rgb(var(--fg-muted))] border-[rgb(var(--bd-default))] hover:border-[rgb(var(--color-primary))] hover:text-[rgb(var(--color-primary))]'), children: q.label }, q.value));
40
40
  }) })) : undefined;
41
- return (_jsx(Dropdown, { options: tab.dropdownOptions, value: selected, onValueChange: (sel) => {
41
+ return (_jsx(Dropdown, { options: tab.dropdownOptions,
42
+ // Periods are listed in time order (Apr, May, Jun… / Week 1, 2, 3…); sorted by label,
43
+ // the months came out Apr, Aug, Dec, Feb.
44
+ preserveOrder: true, value: selected, onValueChange: (sel) => {
42
45
  const arr = Array.isArray(sel) ? sel.map(String) : [String(sel)];
43
46
  // Ticking a named option clears any quick preset (This/Last) — they're exclusive.
44
47
  const quickVals = (tab.quickOptions || []).map(q => q.value);
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useReducer, useEffect, useCallback, useMemo } from 'react';
4
- import { useSession } from 'next-auth/react';
4
+ import { useOptionalSessionAdapter } from './SessionAdapterContext.js';
5
5
  import { createEmailAPI, DEFAULT_FOLDERS, DEFAULT_CATEGORIES } from '../lib/api/activity/email/index.js';
6
6
  const FOLDER_CACHE_MAX_AGE = 5 * 60 * 1000; // 5 minutes
7
7
  // Initial State
@@ -145,11 +145,12 @@ function emailReducer(state, action) {
145
145
  const EmailContext = createContext(undefined);
146
146
  // Email Provider Component
147
147
  export function EmailProvider({ children }) {
148
- const { data: session } = useSession();
148
+ const session = useOptionalSessionAdapter()?.data ?? null;
149
149
  const [state, dispatch] = useReducer(emailReducer, initialState);
150
150
  // Memoize on stable identity — useSession() returns a new object each render, so an
151
151
  // unmemoized api would churn every useCallback([api]) and trigger repeated fetches.
152
152
  const sessionIdentity = `${session?.user?.CompanyID ?? ''}:${session?.user?.UserID ?? ''}`;
153
+ // The adapter carries the next-auth session as AuthSessionProvider received it, so it has the shape the email API reads.
153
154
  const api = useMemo(() => createEmailAPI(session), [sessionIdentity]); // eslint-disable-line react-hooks/exhaustive-deps
154
155
  // Initialize email system when session is available
155
156
  // NOTE: Initialization is now manual - call actions.fetchEmails() when needed
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useReducer, useCallback, useEffect, useRef, useMemo } from 'react';
4
4
  import { HubConnectionBuilder, HubConnectionState, LogLevel } from '@microsoft/signalr';
5
- import { useSession } from 'next-auth/react';
5
+ import { useOptionalSessionAdapter } from './SessionAdapterContext.js';
6
6
  import { MessagingAPI } from '../lib/api/activity/messaging/messaging.js';
7
7
  import { parseReactions } from '../lib/api/activity/messaging/messaging.js';
8
8
  import { getBasicAuthHeader } from '../lib/api/core/config.js';
@@ -192,7 +192,7 @@ function messagingReducer(state, action) {
192
192
  const MessagingContext = createContext(undefined);
193
193
  // ─── Provider ───────────────────────────────────────────────────────────────
194
194
  export function MessagingProvider({ children }) {
195
- const { data: session } = useSession();
195
+ const session = useOptionalSessionAdapter()?.data ?? null;
196
196
  const [state, dispatch] = useReducer(messagingReducer, initialState);
197
197
  const stateRef = useRef(state);
198
198
  stateRef.current = state;
@@ -3,6 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from 'react';
4
4
  import { getDynamicNavigation } from '../lib/api/index.js';
5
5
  import { getModuleRoutePath } from '../lib/utils.js';
6
+ import { useOptionalSessionAdapter } from './SessionAdapterContext.js';
6
7
  /**
7
8
  * Filter grouped modules to only those with MobileSupported = 1/true.
8
9
  * Returns grouped structure with empty groups removed.
@@ -20,11 +21,25 @@ export function NavigationProvider({ children, companyId, userId }) {
20
21
  const [groupedModules, setGroupedModules] = React.useState([]);
21
22
  const [loading, setLoading] = React.useState(true);
22
23
  const [error, setError] = React.useState(null);
24
+ // The menu API needs the signed-in session (company credentials). Read from the session adapter,
25
+ // not next-auth, so the library does not require next-auth; held in a ref so a fresh session
26
+ // object does not refetch the menu, only the session arriving or leaving does.
27
+ const sessionAdapter = useOptionalSessionAdapter();
28
+ const sessionRef = React.useRef(sessionAdapter?.data ?? null);
29
+ sessionRef.current = sessionAdapter?.data ?? null;
30
+ const hasSession = Boolean(sessionAdapter?.data);
31
+ const sessionStatus = sessionAdapter?.status ?? 'unauthenticated';
23
32
  const fetchNavigation = React.useCallback(async () => {
24
33
  if (!companyId || !userId) {
25
34
  setLoading(false);
26
35
  return;
27
36
  }
37
+ // No session yet: wait for it (this runs again when it arrives); none at all: no menu.
38
+ if (!hasSession) {
39
+ if (sessionStatus !== 'loading')
40
+ setLoading(false);
41
+ return;
42
+ }
28
43
  const cacheKey = `navigation_${companyId}_${userId}`;
29
44
  const cacheTimeKey = `${cacheKey}_time`;
30
45
  const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
@@ -41,7 +56,7 @@ export function NavigationProvider({ children, companyId, userId }) {
41
56
  }
42
57
  }
43
58
  setLoading(true);
44
- const navigation = await getDynamicNavigation(companyId, userId);
59
+ const navigation = await getDynamicNavigation(companyId, userId, sessionRef.current);
45
60
  setGroupedModules(navigation);
46
61
  localStorage.setItem(cacheKey, JSON.stringify(navigation));
47
62
  localStorage.setItem(cacheTimeKey, Date.now().toString());
@@ -53,7 +68,7 @@ export function NavigationProvider({ children, companyId, userId }) {
53
68
  finally {
54
69
  setLoading(false);
55
70
  }
56
- }, [companyId, userId]);
71
+ }, [companyId, userId, hasSession, sessionStatus]);
57
72
  React.useEffect(() => {
58
73
  fetchNavigation();
59
74
  }, [fetchNavigation]);
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useState, useEffect, useCallback, useMemo, useRef } from 'react';
4
- import { useSession } from 'next-auth/react';
4
+ import { useOptionalSessionAdapter } from './SessionAdapterContext.js';
5
5
  import { NotificationsAPI } from '../lib/api/activity/index.js';
6
6
  import { fromBackendNotification } from '../lib/api/activity/notifications/types.js';
7
7
  import { useSignalR } from '../hooks/useSignalR.js';
@@ -11,7 +11,7 @@ const NotificationContext = createContext(undefined);
11
11
  * app. Consumers read shared state via `useNotifications()` instead of each mounting their own.
12
12
  */
13
13
  export function NotificationProvider({ children }) {
14
- const { data: session } = useSession();
14
+ const session = useOptionalSessionAdapter()?.data ?? null;
15
15
  const [notifications, setNotifications] = useState([]);
16
16
  const [notificationsLoading, setNotificationsLoading] = useState(false);
17
17
  const [showNotificationDropdown, setShowNotificationDropdown] = useState(false);
@@ -35,5 +35,10 @@ export interface SessionAdapterProviderProps {
35
35
  children: ReactNode;
36
36
  }
37
37
  export declare function SessionAdapterProvider({ user, status, signOut, children }: SessionAdapterProviderProps): import("react").JSX.Element;
38
+ /**
39
+ * The session when a provider is present, null when not: for components that also render in apps
40
+ * without one, such as the dynamic sidebar, which simply loads no menu then.
41
+ */
42
+ export declare function useOptionalSessionAdapter(): SessionAdapterValue | null;
38
43
  /** Drop-in replacement for next-auth's `useSession()` inside library components. */
39
44
  export declare function useSessionAdapter(): SessionAdapterValue;
@@ -10,6 +10,13 @@ export function SessionAdapterProvider({ user, status, signOut, children }) {
10
10
  }), [user, status, signOut]);
11
11
  return _jsx(SessionAdapterContext.Provider, { value: value, children: children });
12
12
  }
13
+ /**
14
+ * The session when a provider is present, null when not: for components that also render in apps
15
+ * without one, such as the dynamic sidebar, which simply loads no menu then.
16
+ */
17
+ export function useOptionalSessionAdapter() {
18
+ return useContext(SessionAdapterContext);
19
+ }
13
20
  /** Drop-in replacement for next-auth's `useSession()` inside library components. */
14
21
  export function useSessionAdapter() {
15
22
  const ctx = useContext(SessionAdapterContext);
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  export { useClientId, useUniqueId } from './useId.js';
3
2
  export { useTheme } from './useTheme.js';
4
3
  export { useDebounce, useDebouncedValue } from './useDebounce.js';
@@ -20,13 +20,13 @@
20
20
  * ```
21
21
  */
22
22
  import { useState, useEffect, useCallback } from 'react';
23
- import { useSession } from 'next-auth/react';
23
+ import { useOptionalSessionAdapter } from '../contexts/SessionAdapterContext.js';
24
24
  import { ApprovalAPI } from '../lib/api/approval/index.js';
25
25
  /**
26
26
  * useApproval Hook
27
27
  */
28
28
  export function useApproval() {
29
- const { data: session } = useSession();
29
+ const session = useOptionalSessionAdapter()?.data ?? null;
30
30
  // State
31
31
  const [pendingApprovals, setPendingApprovals] = useState([]);
32
32
  const [isLoading, setIsLoading] = useState(false);
@@ -14,10 +14,10 @@
14
14
  * ```
15
15
  */
16
16
  import { useState, useCallback } from 'react';
17
- import { useSession } from 'next-auth/react';
17
+ import { useOptionalSessionAdapter } from '../contexts/SessionAdapterContext.js';
18
18
  import { BusinessAuditAPI } from '../lib/api/activity/audit/index.js';
19
19
  export function useBusinessAudit() {
20
- const { data: session } = useSession();
20
+ const session = useOptionalSessionAdapter()?.data ?? null;
21
21
  const [isLogging, setIsLogging] = useState(false);
22
22
  const logAction = useCallback(async (action, module, details) => {
23
23
  if (!session)
@@ -19,13 +19,13 @@
19
19
  * ```
20
20
  */
21
21
  import { useState, useEffect, useCallback } from 'react';
22
- import { useSession } from 'next-auth/react';
22
+ import { useOptionalSessionAdapter } from '../contexts/SessionAdapterContext.js';
23
23
  import { DraftAPI } from '../lib/api/draft/index.js';
24
24
  /**
25
25
  * useDraft Hook - Milestone Based
26
26
  */
27
27
  export function useDraft(module, currentState) {
28
- const { data: session } = useSession();
28
+ const session = useOptionalSessionAdapter()?.data ?? null;
29
29
  // State
30
30
  const [drafts, setDrafts] = useState([]);
31
31
  const [isLoadingDrafts, setIsLoadingDrafts] = useState(false);
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Components
3
2
  export * from './components/index.js';
4
3
  // Contexts, providers, session adapter, utils (also available as 'indas-ui/providers')
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  /**
3
2
  * Audit API Module
4
3
  * Exports audit logging functionality
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Email API - Main Export File
3
2
  export * from './types.js';
4
3
  export * from './client.js';
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  /**
3
2
  * Activity API Module
4
3
  * Exports audit logs, email, and notifications functionality
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  /**
3
2
  * Notifications API Module
4
3
  * Exports notification functionality
@@ -1,2 +1 @@
1
- 'use client';
2
1
  export { RemarkIntelligenceAPI } from './remarks.js';
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  /**
3
2
  * Approval API Module
4
3
  * Exports approval workflow functionality
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Authentication API exports
3
2
  // Central export point for all authentication APIs
4
3
  export { authenticateUser, setAuthAPIConfig, getAuthAPIConfig, clearAuthAPIConfig } from './auth.js';
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Core API exports
3
2
  // Central export point for all core API utilities
4
3
  // Export APIClient as both named and default export
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  /**
3
2
  * Draft API Module
4
3
  * Exports draft management functionality
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Enquiry API exports
3
2
  export * from './enquiry.js';
4
3
  export * from './rate-enquiry.js';
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Estimation API exports
3
2
  // Central export point for all estimation-related APIs
4
3
  // Export category APIs
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Main API barrel export
3
2
  // Provides convenient access to all API modules
4
3
  // Re-export core utilities
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Master API exports
3
2
  // Central export point for all master data APIs
4
3
  // Export Process Master API
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  // Utility API exports
3
2
  // Central export point for all utility APIs
4
3
  // Export menu API
@@ -18,9 +18,13 @@ export interface GroupedModule {
18
18
  }
19
19
  /**
20
20
  * Fetch dynamic menu modules from external API
21
- * Now uses APIClient for proper authentication with company credentials
21
+ * Now uses APIClient for proper authentication with company credentials.
22
+ *
23
+ * The caller passes the session (from useSessionAdapter, which AuthSessionProvider fills from
24
+ * next-auth) rather than this module calling next-auth's getSession: importing next-auth here made
25
+ * every app installing indas-ui install next-auth too, since nearly every entry reaches this file.
22
26
  */
23
- export declare function fetchDynamicMenuModules(companyId: number, userId: number): Promise<DynamicModule[]>;
27
+ export declare function fetchDynamicMenuModules(companyId: number, userId: number, session?: unknown): Promise<DynamicModule[]>;
24
28
  /**
25
29
  * Group modules by ModuleHeadName and sort by ModuleDisplayOrder
26
30
  * Now supports hierarchical sub-modules
@@ -29,4 +33,4 @@ export declare function groupAndSortModules(modules: DynamicModule[]): GroupedMo
29
33
  /**
30
34
  * Get dynamic navigation structure for sidebar
31
35
  */
32
- export declare function getDynamicNavigation(companyId?: number, userId?: number): Promise<GroupedModule[]>;
36
+ export declare function getDynamicNavigation(companyId?: number, userId?: number, session?: unknown): Promise<GroupedModule[]>;
@@ -2,15 +2,16 @@
2
2
  // Dynamic Menu API
3
3
  // Handles dynamic menu and navigation
4
4
  import APIClient from '../core/client.js';
5
- import { getSession } from 'next-auth/react';
6
5
  /**
7
6
  * Fetch dynamic menu modules from external API
8
- * Now uses APIClient for proper authentication with company credentials
7
+ * Now uses APIClient for proper authentication with company credentials.
8
+ *
9
+ * The caller passes the session (from useSessionAdapter, which AuthSessionProvider fills from
10
+ * next-auth) rather than this module calling next-auth's getSession: importing next-auth here made
11
+ * every app installing indas-ui install next-auth too, since nearly every entry reaches this file.
9
12
  */
10
- export async function fetchDynamicMenuModules(companyId, userId) {
13
+ export async function fetchDynamicMenuModules(companyId, userId, session) {
11
14
  try {
12
- // Get session to pass to APIClient for company credentials
13
- const session = await getSession();
14
15
  if (!session) {
15
16
  return [];
16
17
  }
@@ -119,10 +120,10 @@ export function groupAndSortModules(modules) {
119
120
  /**
120
121
  * Get dynamic navigation structure for sidebar
121
122
  */
122
- export async function getDynamicNavigation(companyId, userId) {
123
+ export async function getDynamicNavigation(companyId, userId, session) {
123
124
  if (!companyId || !userId) {
124
125
  return [];
125
126
  }
126
- const modules = await fetchDynamicMenuModules(companyId, userId);
127
+ const modules = await fetchDynamicMenuModules(companyId, userId, session);
127
128
  return groupAndSortModules(modules);
128
129
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * How every number in the library is written. Indian grouping by default (20,00,000, not
3
+ * 2,000,000): the products built on this library keep Indian books, and a bare `toLocaleString()`
4
+ * follows whatever locale the browser happens to be set to, so the same figure read differently on
5
+ * two machines.
6
+ *
7
+ * A product that needs another convention calls `setNumberLocale()` once at start-up.
8
+ */
9
+ export declare function setNumberLocale(locale: string): void;
10
+ export declare function getNumberLocale(): string;
11
+ /** A full number with grouping, up to two decimals. Empty for null, undefined or NaN. */
12
+ export declare function formatNumber(value: number | string | null | undefined, maximumFractionDigits?: number): string;
13
+ /**
14
+ * A short number for chart axes, where a full figure runs into its neighbour: 12,500 · 20 L ·
15
+ * 1.4 Cr in Indian grouping; 12.5K · 2M · 1.4B otherwise. Below a lakh (or a thousand) it is the
16
+ * full number.
17
+ */
18
+ export declare function formatCompactNumber(value: number | string | null | undefined): string;
@@ -0,0 +1,54 @@
1
+ 'use client';
2
+ /**
3
+ * How every number in the library is written. Indian grouping by default (20,00,000, not
4
+ * 2,000,000): the products built on this library keep Indian books, and a bare `toLocaleString()`
5
+ * follows whatever locale the browser happens to be set to, so the same figure read differently on
6
+ * two machines.
7
+ *
8
+ * A product that needs another convention calls `setNumberLocale()` once at start-up.
9
+ */
10
+ let numberLocale = 'en-IN';
11
+ export function setNumberLocale(locale) {
12
+ numberLocale = locale;
13
+ }
14
+ export function getNumberLocale() {
15
+ return numberLocale;
16
+ }
17
+ /** A full number with grouping, up to two decimals. Empty for null, undefined or NaN. */
18
+ export function formatNumber(value, maximumFractionDigits = 2) {
19
+ if (value == null || value === '')
20
+ return '';
21
+ const n = typeof value === 'number' ? value : Number(value);
22
+ if (!Number.isFinite(n))
23
+ return typeof value === 'string' ? value : '';
24
+ return n.toLocaleString(numberLocale, { maximumFractionDigits });
25
+ }
26
+ const trim = (n) => n.toFixed(n >= 100 ? 0 : n >= 10 ? 1 : 2).replace(/\.?0+$/, '');
27
+ /**
28
+ * A short number for chart axes, where a full figure runs into its neighbour: 12,500 · 20 L ·
29
+ * 1.4 Cr in Indian grouping; 12.5K · 2M · 1.4B otherwise. Below a lakh (or a thousand) it is the
30
+ * full number.
31
+ */
32
+ export function formatCompactNumber(value) {
33
+ if (value == null || value === '')
34
+ return '';
35
+ const n = typeof value === 'number' ? value : Number(value);
36
+ if (!Number.isFinite(n))
37
+ return String(value);
38
+ const sign = n < 0 ? '-' : '';
39
+ const a = Math.abs(n);
40
+ if (numberLocale === 'en-IN') {
41
+ if (a >= 1e7)
42
+ return `${sign}${trim(a / 1e7)} Cr`;
43
+ if (a >= 1e5)
44
+ return `${sign}${trim(a / 1e5)} L`;
45
+ return formatNumber(n, 0);
46
+ }
47
+ if (a >= 1e9)
48
+ return `${sign}${trim(a / 1e9)}B`;
49
+ if (a >= 1e6)
50
+ return `${sign}${trim(a / 1e6)}M`;
51
+ if (a >= 1e3)
52
+ return `${sign}${trim(a / 1e3)}K`;
53
+ return formatNumber(n, 0);
54
+ }