@fayz-ai/saas 0.1.6 → 0.2.0

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/dist/crud/CrudDetailPage.d.ts.map +1 -1
  4. package/dist/crud/CrudFormPage.d.ts.map +1 -1
  5. package/dist/crud/archetypes/PersonDetailTabs.d.ts +12 -0
  6. package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -1
  7. package/dist/crud/fieldToColumn.d.ts.map +1 -1
  8. package/dist/crud/index.d.ts +1 -0
  9. package/dist/crud/index.d.ts.map +1 -1
  10. package/dist/crud/relation-field.d.ts +28 -0
  11. package/dist/crud/relation-field.d.ts.map +1 -0
  12. package/dist/hooks/useModuleNavigation.d.ts +2 -0
  13. package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
  14. package/dist/index.cjs +726 -379
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +550 -204
  19. package/dist/index.js.map +1 -1
  20. package/dist/shell/components/plugins/ConnectorsHub.d.ts +6 -0
  21. package/dist/shell/components/plugins/ConnectorsHub.d.ts.map +1 -0
  22. package/dist/shell/components/plugins/PluginSettingsPanel.d.ts +4 -2
  23. package/dist/shell/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
  24. package/dist/shell/createSaasApp.d.ts.map +1 -1
  25. package/dist/shell/hooks/useModuleNavigation.d.ts +2 -0
  26. package/dist/shell/hooks/useModuleNavigation.d.ts.map +1 -1
  27. package/dist/shell/types/crud.d.ts +12 -1
  28. package/dist/shell/types/crud.d.ts.map +1 -1
  29. package/package.json +4 -4
  30. package/src/crud/CrudDetailPage.tsx +5 -0
  31. package/src/crud/CrudFormPage.tsx +12 -0
  32. package/src/crud/archetypes/PersonDetailTabs.tsx +22 -0
  33. package/src/crud/fieldToColumn.tsx +3 -0
  34. package/src/crud/index.ts +1 -0
  35. package/src/crud/relation-field.tsx +115 -0
  36. package/src/hooks/useModuleNavigation.ts +27 -1
  37. package/src/index.ts +1 -0
  38. package/src/shell/components/plugins/ConnectorsHub.tsx +163 -0
  39. package/src/shell/components/plugins/PluginSettingsPanel.tsx +106 -27
  40. package/src/shell/createSaasApp.tsx +22 -1
  41. package/src/shell/hooks/useModuleNavigation.ts +27 -1
  42. package/src/shell/types/crud.ts +13 -0
package/dist/index.js CHANGED
@@ -4,14 +4,14 @@ import { PermissionsProvider, usePermissionsStore, usePermissionOptional } from
4
4
  export { PermissionsProvider, useHasPermission, usePermission, usePermissions, usePermissionsStore } from './chunk-XNWCGJYH.js';
5
5
  import { useBillingStore } from './chunk-5GYQK5IW.js';
6
6
  export { useBillingStore } from './chunk-5GYQK5IW.js';
7
- import * as React40 from 'react';
8
- import React40__default, { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext, useSyncExternalStore } from 'react';
9
- import { hashRouterAdapter, registerScaffold, registerEntity, deriveEntityKey, resolveDataProvider, setCurrentLocale, resolvePluginRuntime, PluginRuntimeProvider, I18nProvider, useTranslation as useTranslation$1, getSupabaseClientOptional as getSupabaseClientOptional$1, setDefaultCurrency, registerTranslations, getComponent, getPluginFactory, mergeTranslations, coreTranslations, usePluginRuntime, resolvePluginComponent, exportCSV, usePluginRuntimeOptional, getCurrentLocale, getWidgetsForZone, formatDateTime as formatDateTime$1, formatDate as formatDate$1, formatCurrency as formatCurrency$1, defineApp, createMockProvider } from '@fayz-ai/core';
7
+ import * as React41 from 'react';
8
+ import React41__default, { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext, useSyncExternalStore } from 'react';
9
+ import { hashRouterAdapter, registerScaffold, registerEntity, deriveEntityKey, resolveDataProvider, setCurrentLocale, resolvePluginRuntime, PluginRuntimeProvider, I18nProvider, useTranslation as useTranslation$1, getSupabaseClientOptional as getSupabaseClientOptional$1, setDefaultCurrency, registerTranslations, getComponent, getPluginFactory, mergeTranslations, coreTranslations, usePluginRuntime, resolvePluginComponent, exportCSV, recordNavigation, usePluginRuntimeOptional, getCurrentLocale, getWidgetsForZone, formatDateTime as formatDateTime$1, formatDate as formatDate$1, formatCurrency as formatCurrency$1, getActiveTenantId, defineApp, useConnectorsForPlugin, createMockProvider } from '@fayz-ai/core';
10
10
  import { AuthProvider, useAuth, createSupabaseAuthAdapter, createMockAuthAdapter } from '@fayz-ai/auth';
11
11
  import { ToastProvider, NavTransitionProvider, toast, Card, Button, cn, Input, ModuleLayoutProvider, AppShell, PageTransition, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, DataTable, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalFooter, ModalBody, createFayzTheme, Avatar, AvatarImage, AvatarFallback, DropdownLabel, DropdownSeparator, Badge, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, useLayoutStore, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Checkbox, useSaveBar, useBackHandler, Breadcrumb, Separator, Tabs, TabsList, TabsTrigger, TabsContent, ListView, useModuleLayout, ICON_MAP as ICON_MAP$1, Popover, PopoverTrigger, PopoverContent, CurrencyInput, Skeleton, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, DatePicker, TimePicker } from '@fayz-ai/ui';
12
12
  export { createFayzTheme, fayzThemePresets } from '@fayz-ai/ui';
13
13
  import * as LucideIcons4 from 'lucide-react';
14
- import { X, MessageCircle, Settings2, ArrowUp, MoreVertical, Upload, Download, Building2, User, Shield, Palette, ArrowLeft, Pencil, BookOpen, Sparkles, Users, ShieldCheck, MapPin, SlidersHorizontal, Puzzle, Trash2, Check, FileSpreadsheet, ArrowRight, AlertCircle, ChevronDown, Loader2, Calendar, ChevronRight, Plus, WifiOff, Eye, RefreshCw, UserPlus, MoreHorizontal, Mail, Clock, Save, Camera, KeyRound, Search, Box, List, Filter, Contact, Handshake, UtensilsCrossed, Tag, Percent, Globe, UserCog, Briefcase, ClipboardList, Wrench, Target, ShoppingCart, Megaphone, DollarSign, FileText, BarChart3, Package, Bell, CreditCard, Settings, Home, ChevronsUpDown, RotateCcw, Edit, ShoppingBag, CalendarDays, ExternalLink, Phone, Cake, ChevronLeft, CircleCheckBig, UserX, Ban, CircleAlert, CircleEllipsis, CircleDashed, ImagePlus, Truck, CalendarOff, CalendarPlus, Copy } from 'lucide-react';
14
+ import { X, MessageCircle, Settings2, ArrowUp, MoreVertical, Upload, Download, Building2, User, Shield, Palette, ArrowLeft, Pencil, BookOpen, Sparkles, Users, ShieldCheck, MapPin, SlidersHorizontal, Puzzle, Trash2, Check, FileSpreadsheet, ArrowRight, AlertCircle, ChevronDown, Loader2, Calendar, ChevronRight, Plus, WifiOff, Eye, RefreshCw, UserPlus, MoreHorizontal, Mail, Clock, Save, Camera, KeyRound, Search, Box, List, Filter, Contact, Handshake, UtensilsCrossed, Tag, Percent, Globe, UserCog, Briefcase, ClipboardList, Wrench, Target, ShoppingCart, Megaphone, DollarSign, FileText, BarChart3, Package, Bell, CreditCard, Settings, Home, ChevronsUpDown, RotateCcw, Edit, ShoppingBag, CalendarDays, ExternalLink, Phone, Cake, Plug, TableProperties, ChevronLeft, CircleCheckBig, UserX, Ban, CircleAlert, CircleEllipsis, CircleDashed, CheckCircle2, Power, ImagePlus, Truck, CalendarOff, CalendarPlus, Copy } from 'lucide-react';
15
15
  import { create, useStore } from 'zustand';
16
16
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
17
  import { createPortal } from 'react-dom';
@@ -131,7 +131,7 @@ function detectActivePluginId(runtime) {
131
131
  }
132
132
  function useAITools() {
133
133
  const runtime = usePluginRuntimeOptional();
134
- return React40.useMemo(() => {
134
+ return React41.useMemo(() => {
135
135
  const hasPermission = runtime?.context.hasPermission;
136
136
  const verticalId = runtime?.context.tenant?.verticalId;
137
137
  const activePluginId = detectActivePluginId(runtime);
@@ -264,9 +264,9 @@ function useTranslation() {
264
264
  return { t, locale: getCurrentLocale() };
265
265
  }
266
266
  function TypewriterText({ text, speed = 28, onDone }) {
267
- const [displayed, setDisplayed] = React40.useState("");
268
- const indexRef = React40.useRef(0);
269
- React40.useEffect(() => {
267
+ const [displayed, setDisplayed] = React41.useState("");
268
+ const indexRef = React41.useRef(0);
269
+ React41.useEffect(() => {
270
270
  setDisplayed("");
271
271
  indexRef.current = 0;
272
272
  const interval = setInterval(() => {
@@ -311,11 +311,11 @@ function ChatFab({ className, apiEndpoint, systemPrompt }) {
311
311
  const { contextualSuggestions } = useAITools();
312
312
  const { sendMessage } = useChat({ apiEndpoint, systemPrompt });
313
313
  const { t } = useTranslation();
314
- const [phase, setPhase] = React40.useState("idle");
315
- const [activeSuggestion, setActiveSuggestion] = React40.useState(null);
316
- const timersRef = React40.useRef([]);
317
- const prevSuggestionRef = React40.useRef(null);
318
- React40.useEffect(ensureStyles, []);
314
+ const [phase, setPhase] = React41.useState("idle");
315
+ const [activeSuggestion, setActiveSuggestion] = React41.useState(null);
316
+ const timersRef = React41.useRef([]);
317
+ const prevSuggestionRef = React41.useRef(null);
318
+ React41.useEffect(ensureStyles, []);
319
319
  const clearTimers = () => {
320
320
  timersRef.current.forEach(clearTimeout);
321
321
  timersRef.current = [];
@@ -325,7 +325,7 @@ function ChatFab({ className, apiEndpoint, systemPrompt }) {
325
325
  };
326
326
  const topSuggestion = contextualSuggestions[0] ?? null;
327
327
  const topLabel = topSuggestion?.label ?? null;
328
- React40.useEffect(() => {
328
+ React41.useEffect(() => {
329
329
  if (isOpen || !topSuggestion || !topLabel) {
330
330
  if (phase !== "idle") {
331
331
  clearTimers();
@@ -355,7 +355,7 @@ function ChatFab({ className, apiEndpoint, systemPrompt }) {
355
355
  }, 9e3);
356
356
  return clearTimers;
357
357
  }, [topLabel, isOpen]);
358
- React40.useEffect(() => {
358
+ React41.useEffect(() => {
359
359
  if (isOpen && phase !== "idle") {
360
360
  clearTimers();
361
361
  setPhase("idle");
@@ -498,16 +498,16 @@ function ChatPanel({
498
498
  const { sendMessage } = useChat({ apiEndpoint, systemPrompt });
499
499
  const { t } = useTranslation();
500
500
  const { suggestions, toolGroups } = useAITools();
501
- const [input, setInput] = React40.useState("");
502
- const [showTools, setShowTools] = React40.useState(false);
503
- const scrollRef = React40.useRef(null);
504
- const inputRef = React40.useRef(null);
501
+ const [input, setInput] = React41.useState("");
502
+ const [showTools, setShowTools] = React41.useState(false);
503
+ const scrollRef = React41.useRef(null);
504
+ const inputRef = React41.useRef(null);
505
505
  const hasMessages = messages.length > 0;
506
506
  const totalTools = toolGroups.reduce((sum, g) => sum + g.tools.length, 0);
507
- React40.useEffect(() => {
507
+ React41.useEffect(() => {
508
508
  scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
509
509
  }, [messages]);
510
- React40.useEffect(() => {
510
+ React41.useEffect(() => {
511
511
  if (isOpen) {
512
512
  const timer = setTimeout(() => inputRef.current?.focus(), 100);
513
513
  return () => clearTimeout(timer);
@@ -1064,8 +1064,8 @@ function navigateTo(to) {
1064
1064
  adapter.navigate(normalizeAdminPath(to));
1065
1065
  }
1066
1066
  function useAdminPath() {
1067
- const [path, setPath] = React40.useState(() => normalizeAdminPath(adapter.getCurrentPath() || "/"));
1068
- React40.useEffect(() => {
1067
+ const [path, setPath] = React41.useState(() => normalizeAdminPath(adapter.getCurrentPath() || "/"));
1068
+ React41.useEffect(() => {
1069
1069
  return adapter.onPathChange((p) => {
1070
1070
  setPath(normalizeAdminPath(p || "/"));
1071
1071
  if (typeof window !== "undefined") window.scrollTo(0, 0);
@@ -1120,12 +1120,12 @@ function LoginPage({
1120
1120
  return !v || v === key || v.includes("{{") ? fallback : v;
1121
1121
  };
1122
1122
  const { signIn, signUp, signInWithOAuth, resetPassword, isLoading, error } = useAuth();
1123
- const [view, setView] = React40.useState("login");
1124
- const [email, setEmail] = React40.useState("");
1125
- const [password, setPassword] = React40.useState("");
1126
- const [fullName, setFullName] = React40.useState("");
1127
- const [notice, setNotice] = React40.useState(null);
1128
- const [submitting, setSubmitting] = React40.useState(false);
1123
+ const [view, setView] = React41.useState("login");
1124
+ const [email, setEmail] = React41.useState("");
1125
+ const [password, setPassword] = React41.useState("");
1126
+ const [fullName, setFullName] = React41.useState("");
1127
+ const [notice, setNotice] = React41.useState(null);
1128
+ const [submitting, setSubmitting] = React41.useState(false);
1129
1129
  const busy = submitting || isLoading;
1130
1130
  const brandLogo = loginLogo ?? logo;
1131
1131
  const heading = view === "login" ? tr("auth.login.title", "Welcome back") : view === "signup" ? tr("auth.signup.title", "Create account") : tr("auth.recovery.title", "Reset password");
@@ -1366,11 +1366,11 @@ var CURRENCIES = [
1366
1366
  function CompanySettings({ tenant, onSave }) {
1367
1367
  const { t } = useTranslation();
1368
1368
  const { locale: uiLocale, setLocale: setUiLocale } = useLocaleStore();
1369
- const [name, setName] = React40.useState(tenant?.name ?? "");
1370
- const [timezone, setTimezone] = React40.useState(tenant?.settings?.timezone ?? "UTC");
1371
- const [currency, setCurrency] = React40.useState(tenant?.settings?.currency ?? "USD");
1372
- const [saving, setSaving] = React40.useState(false);
1373
- React40.useEffect(() => {
1369
+ const [name, setName] = React41.useState(tenant?.name ?? "");
1370
+ const [timezone, setTimezone] = React41.useState(tenant?.settings?.timezone ?? "UTC");
1371
+ const [currency, setCurrency] = React41.useState(tenant?.settings?.currency ?? "USD");
1372
+ const [saving, setSaving] = React41.useState(false);
1373
+ React41.useEffect(() => {
1374
1374
  if (tenant) {
1375
1375
  setName(tenant.name);
1376
1376
  setTimezone(tenant.settings?.timezone ?? "UTC");
@@ -1520,10 +1520,10 @@ function getInitials(name) {
1520
1520
  }
1521
1521
  function UserProfile({ user, onSave, onAvatarChange }) {
1522
1522
  const { t } = useTranslation();
1523
- const [fullName, setFullName] = React40.useState(user?.fullName ?? "");
1524
- const [saving, setSaving] = React40.useState(false);
1525
- const fileInputRef = React40.useRef(null);
1526
- React40.useEffect(() => {
1523
+ const [fullName, setFullName] = React41.useState(user?.fullName ?? "");
1524
+ const [saving, setSaving] = React41.useState(false);
1525
+ const fileInputRef = React41.useRef(null);
1526
+ React41.useEffect(() => {
1527
1527
  if (user) {
1528
1528
  setFullName(user.fullName);
1529
1529
  }
@@ -1732,9 +1732,9 @@ function SecuritySettings({
1732
1732
  onTenantSecurityChange
1733
1733
  }) {
1734
1734
  const { t } = useTranslation();
1735
- const [resetting, setResetting] = React40.useState(false);
1736
- const [resetSent, setResetSent] = React40.useState(false);
1737
- const [savingTenant, setSavingTenant] = React40.useState(false);
1735
+ const [resetting, setResetting] = React41.useState(false);
1736
+ const [resetSent, setResetSent] = React41.useState(false);
1737
+ const [savingTenant, setSavingTenant] = React41.useState(false);
1738
1738
  const handleResetPassword = async () => {
1739
1739
  if (!onResetPassword) return;
1740
1740
  setResetting(true);
@@ -1894,8 +1894,8 @@ function FileUploadField({
1894
1894
  hint
1895
1895
  }) {
1896
1896
  const { t } = useTranslation();
1897
- const inputRef = React40.useRef(null);
1898
- const [preview, setPreview] = React40.useState(null);
1897
+ const inputRef = React41.useRef(null);
1898
+ const [preview, setPreview] = React41.useState(null);
1899
1899
  const handleChange = (e) => {
1900
1900
  const file = e.target.files?.[0];
1901
1901
  if (file) {
@@ -1962,14 +1962,14 @@ function FileUploadField({
1962
1962
  }
1963
1963
  function BrandingSettings({ branding, onSave }) {
1964
1964
  const { t } = useTranslation();
1965
- const [primaryColor, setPrimaryColor] = React40.useState(branding?.primaryColor ?? "#000000");
1966
- const [accentColor, setAccentColor] = React40.useState(branding?.accentColor ?? "#6366f1");
1967
- const [logoFile, setLogoFile] = React40.useState(null);
1968
- const [faviconFile, setFaviconFile] = React40.useState(null);
1969
- const [logoUrl, setLogoUrl] = React40.useState(branding?.logoUrl ?? "");
1970
- const [faviconUrl, setFaviconUrl] = React40.useState(branding?.faviconUrl ?? "");
1971
- const [saving, setSaving] = React40.useState(false);
1972
- React40.useEffect(() => {
1965
+ const [primaryColor, setPrimaryColor] = React41.useState(branding?.primaryColor ?? "#000000");
1966
+ const [accentColor, setAccentColor] = React41.useState(branding?.accentColor ?? "#6366f1");
1967
+ const [logoFile, setLogoFile] = React41.useState(null);
1968
+ const [faviconFile, setFaviconFile] = React41.useState(null);
1969
+ const [logoUrl, setLogoUrl] = React41.useState(branding?.logoUrl ?? "");
1970
+ const [faviconUrl, setFaviconUrl] = React41.useState(branding?.faviconUrl ?? "");
1971
+ const [saving, setSaving] = React41.useState(false);
1972
+ React41.useEffect(() => {
1973
1973
  if (branding) {
1974
1974
  setPrimaryColor(branding.primaryColor ?? "#000000");
1975
1975
  setAccentColor(branding.accentColor ?? "#6366f1");
@@ -2172,7 +2172,7 @@ function SettingsPage({
2172
2172
  afterContent
2173
2173
  }) {
2174
2174
  const { t } = useTranslation();
2175
- const defaultTabs = React40.useMemo(() => [
2175
+ const defaultTabs = React41.useMemo(() => [
2176
2176
  { id: "general", label: t("settings.general"), icon: /* @__PURE__ */ jsx(Building2, { className: "h-4 w-4" }), component: /* @__PURE__ */ jsx(ConnectedCompanySettings, {}) },
2177
2177
  { id: "profile", label: t("settings.profile"), icon: /* @__PURE__ */ jsx(User, { className: "h-4 w-4" }), component: /* @__PURE__ */ jsx(ConnectedUserProfile, {}) },
2178
2178
  { id: "security", label: t("settings.security"), icon: /* @__PURE__ */ jsx(Shield, { className: "h-4 w-4" }), component: /* @__PURE__ */ jsx(ConnectedSecuritySettings, {}) },
@@ -2188,10 +2188,10 @@ function SettingsPage({
2188
2188
  }
2189
2189
  return null;
2190
2190
  }
2191
- const [activeTab, setActiveTab] = React40.useState(() => {
2191
+ const [activeTab, setActiveTab] = React41.useState(() => {
2192
2192
  return getTabFromHash() ?? defaultTab ?? null;
2193
2193
  });
2194
- React40.useEffect(() => {
2194
+ React41.useEffect(() => {
2195
2195
  const handler = () => {
2196
2196
  const tab = getTabFromHash();
2197
2197
  if (tab) setActiveTab(tab);
@@ -2211,7 +2211,7 @@ function SettingsPage({
2211
2211
  setActiveTab(null);
2212
2212
  window.location.hash = "/settings";
2213
2213
  };
2214
- const renderNav = (activeId) => /* @__PURE__ */ jsx("ul", { className: "space-y-0.5", children: resolvedTabs.map((tab, i) => /* @__PURE__ */ jsxs(React40.Fragment, { children: [
2214
+ const renderNav = (activeId) => /* @__PURE__ */ jsx("ul", { className: "space-y-0.5", children: resolvedTabs.map((tab, i) => /* @__PURE__ */ jsxs(React41.Fragment, { children: [
2215
2215
  tab.isPlugin && (i === 0 || !resolvedTabs[i - 1]?.isPlugin) && /* @__PURE__ */ jsxs("li", { className: "py-2", children: [
2216
2216
  /* @__PURE__ */ jsx("div", { className: "border-t" }),
2217
2217
  /* @__PURE__ */ jsx("p", { className: "px-3 pt-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: t("common.plugins") })
@@ -2271,10 +2271,10 @@ function InviteMemberDialog({ open, onOpenChange, defaultEmail }) {
2271
2271
  const profiles = usePermissionsStore((s) => s.profiles);
2272
2272
  const setInvites = useInviteStore((s) => s.setInvites);
2273
2273
  const user = useAuthStore((s) => s.user);
2274
- const [emails, setEmails] = React40.useState([defaultEmail ?? ""]);
2275
- const [profileId, setProfileId] = React40.useState(profiles[1]?.id ?? profiles[0]?.id ?? "");
2276
- const [sending, setSending] = React40.useState(false);
2277
- const [sent, setSent] = React40.useState(false);
2274
+ const [emails, setEmails] = React41.useState([defaultEmail ?? ""]);
2275
+ const [profileId, setProfileId] = React41.useState(profiles[1]?.id ?? profiles[0]?.id ?? "");
2276
+ const [sending, setSending] = React41.useState(false);
2277
+ const [sent, setSent] = React41.useState(false);
2278
2278
  const addEmail = () => setEmails((prev) => [...prev, ""]);
2279
2279
  const updateEmail = (idx, value) => {
2280
2280
  setEmails((prev) => {
@@ -2472,8 +2472,8 @@ function TeamTab() {
2472
2472
  const setInvites = useInviteStore((s) => s.setInvites);
2473
2473
  const user = useAuthStore((s) => s.user);
2474
2474
  const { hasSystemPermission } = usePermission2();
2475
- const [inviteOpen, setInviteOpen] = React40.useState(false);
2476
- React40.useEffect(() => {
2475
+ const [inviteOpen, setInviteOpen] = React41.useState(false);
2476
+ React41.useEffect(() => {
2477
2477
  if (adapter2 && currentOrg) {
2478
2478
  dedup("team:invites:" + currentOrg.id, () => adapter2.listInvites(currentOrg.id)).then(setInvites).catch(() => {
2479
2479
  });
@@ -2620,12 +2620,12 @@ var SYSTEM_PERMISSIONS = [
2620
2620
  ];
2621
2621
  function PermissionMatrixEditor({ features, profile, onSave, onCancel, saving }) {
2622
2622
  const { t } = useTranslation();
2623
- const [name, setName] = React40.useState(profile?.name ?? "");
2624
- const [description, setDescription] = React40.useState(profile?.description ?? "");
2625
- const [systemPerms, setSystemPerms] = React40.useState(profile?.systemPermissions ?? []);
2626
- const [grants, setGrants] = React40.useState(profile?.grants ?? {});
2627
- const [collapsed, setCollapsed] = React40.useState(/* @__PURE__ */ new Set());
2628
- const [search, setSearch] = React40.useState("");
2623
+ const [name, setName] = React41.useState(profile?.name ?? "");
2624
+ const [description, setDescription] = React41.useState(profile?.description ?? "");
2625
+ const [systemPerms, setSystemPerms] = React41.useState(profile?.systemPermissions ?? []);
2626
+ const [grants, setGrants] = React41.useState(profile?.grants ?? {});
2627
+ const [collapsed, setCollapsed] = React41.useState(/* @__PURE__ */ new Set());
2628
+ const [search, setSearch] = React41.useState("");
2629
2629
  const toggleSystemPerm = (perm) => {
2630
2630
  setSystemPerms(
2631
2631
  (prev) => prev.includes(perm) ? prev.filter((p) => p !== perm) : [...prev, perm]
@@ -2649,7 +2649,7 @@ function PermissionMatrixEditor({ features, profile, onSave, onCancel, saving })
2649
2649
  if (!name.trim()) return;
2650
2650
  onSave({ name: name.trim(), description: description.trim() || void 0, systemPermissions: systemPerms, grants });
2651
2651
  };
2652
- const groups = React40.useMemo(() => {
2652
+ const groups = React41.useMemo(() => {
2653
2653
  const filtered = search ? features.filter((f) => f.label.toLowerCase().includes(search.toLowerCase())) : features;
2654
2654
  const map = /* @__PURE__ */ new Map();
2655
2655
  for (const f of filtered) {
@@ -2710,7 +2710,7 @@ function PermissionMatrixEditor({ features, profile, onSave, onCancel, saving })
2710
2710
  }
2711
2711
  ) }) })
2712
2712
  ] }, sp.id)),
2713
- Array.from(groups.entries()).map(([groupName, groupFeatures]) => /* @__PURE__ */ jsxs(React40.Fragment, { children: [
2713
+ Array.from(groups.entries()).map(([groupName, groupFeatures]) => /* @__PURE__ */ jsxs(React41.Fragment, { children: [
2714
2714
  /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 5, className: "p-2 px-3", children: /* @__PURE__ */ jsxs(
2715
2715
  "button",
2716
2716
  {
@@ -2755,10 +2755,10 @@ function PermissionProfilesTab() {
2755
2755
  const startImpersonation = usePermissionsStore((s) => s.startImpersonation);
2756
2756
  const canPreview = (realProfile?.systemPermissions ?? []).includes("manage_permissions") ?? false;
2757
2757
  const { t } = useTranslation();
2758
- const [editingProfile, setEditingProfile] = React40.useState(null);
2759
- const [creating, setCreating] = React40.useState(false);
2760
- const [saving, setSaving] = React40.useState(false);
2761
- const memberCountByProfile = React40.useMemo(() => {
2758
+ const [editingProfile, setEditingProfile] = React41.useState(null);
2759
+ const [creating, setCreating] = React41.useState(false);
2760
+ const [saving, setSaving] = React41.useState(false);
2761
+ const memberCountByProfile = React41.useMemo(() => {
2762
2762
  const counts = {};
2763
2763
  for (const m of members) {
2764
2764
  counts[m.profileId] = (counts[m.profileId] ?? 0) + 1;
@@ -3498,7 +3498,7 @@ function SubjectFormLayout({ fields, allFields, fieldGroups, values, onChange, r
3498
3498
  const IconComponent = LucideIcons4[entityIcon ?? "Heart"] ?? LucideIcons4.Heart;
3499
3499
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
3500
3500
  /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "pt-5", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-5", children: [
3501
- /* @__PURE__ */ jsx("div", { className: "flex h-20 w-20 shrink-0 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: React40__default.createElement(IconComponent, { className: "h-8 w-8" }) }),
3501
+ /* @__PURE__ */ jsx("div", { className: "flex h-20 w-20 shrink-0 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: React41__default.createElement(IconComponent, { className: "h-8 w-8" }) }),
3502
3502
  /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4", children: [
3503
3503
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
3504
3504
  nameField && /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1.5", children: [
@@ -3542,6 +3542,99 @@ function SubjectFormLayout({ fields, allFields, fieldGroups, values, onChange, r
3542
3542
  notesField && /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "pt-5", children: /* @__PURE__ */ jsx(FieldItem5, { field: notesField, value: values[notesField.key], onChange: (val) => onChange(notesField.key, val), renderField: renderField2 }) }) })
3543
3543
  ] });
3544
3544
  }
3545
+ var cache = /* @__PURE__ */ new Map();
3546
+ function keyOf(r) {
3547
+ const tenant = r.tenantScoped !== false ? getActiveTenantId() ?? "" : "";
3548
+ return JSON.stringify([
3549
+ r.schema ?? "public",
3550
+ r.table,
3551
+ r.valueField ?? "id",
3552
+ r.labelField ?? "name",
3553
+ tenant,
3554
+ r.filter ?? null
3555
+ ]);
3556
+ }
3557
+ async function fetchOptions(r) {
3558
+ const supabase = getSupabaseClientOptional$1();
3559
+ if (!supabase) return [];
3560
+ const valueField = r.valueField ?? "id";
3561
+ const labelField = r.labelField ?? "name";
3562
+ let q = (r.schema ? supabase.schema(r.schema) : supabase).from(r.table).select(`${valueField}, ${labelField}`);
3563
+ if (r.tenantScoped !== false) {
3564
+ const tid = getActiveTenantId();
3565
+ if (tid) q = q.eq("tenant_id", tid);
3566
+ }
3567
+ if (r.filter) for (const [k, v] of Object.entries(r.filter)) q = q.eq(k, v);
3568
+ q = q.order(labelField, { ascending: true });
3569
+ const { data } = await q;
3570
+ return (data ?? []).map((row) => ({
3571
+ value: String(row[valueField]),
3572
+ label: String(row[labelField] ?? row[valueField])
3573
+ }));
3574
+ }
3575
+ function loadRelationOptions(r) {
3576
+ const k = keyOf(r);
3577
+ if (!cache.has(k)) {
3578
+ cache.set(k, fetchOptions(r).catch(() => {
3579
+ cache.delete(k);
3580
+ return [];
3581
+ }));
3582
+ }
3583
+ return cache.get(k);
3584
+ }
3585
+ function useRelationOptions(r) {
3586
+ const [options, setOptions] = useState([]);
3587
+ const [loading, setLoading] = useState(true);
3588
+ const dep = r ? keyOf(r) : "none";
3589
+ useEffect(() => {
3590
+ let alive = true;
3591
+ if (!r) {
3592
+ setLoading(false);
3593
+ return;
3594
+ }
3595
+ setLoading(true);
3596
+ loadRelationOptions(r).then((opts) => {
3597
+ if (alive) {
3598
+ setOptions(opts);
3599
+ setLoading(false);
3600
+ }
3601
+ });
3602
+ return () => {
3603
+ alive = false;
3604
+ };
3605
+ }, [dep]);
3606
+ const labelFor = (value) => options.find((o) => o.value === String(value))?.label ?? "";
3607
+ return { options, loading, labelFor };
3608
+ }
3609
+ function RelationSelect({
3610
+ relation,
3611
+ value,
3612
+ onChange,
3613
+ className,
3614
+ required,
3615
+ placeholder
3616
+ }) {
3617
+ const { options, loading } = useRelationOptions(relation);
3618
+ return /* @__PURE__ */ jsxs(
3619
+ "select",
3620
+ {
3621
+ value: value ?? "",
3622
+ onChange: (e) => onChange(e.target.value || null),
3623
+ required,
3624
+ className,
3625
+ children: [
3626
+ /* @__PURE__ */ jsx("option", { value: "", children: loading ? "Loading\u2026" : placeholder ?? "Select..." }),
3627
+ options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
3628
+ ]
3629
+ }
3630
+ );
3631
+ }
3632
+ function RelationCell({ relation, value }) {
3633
+ const { labelFor, loading } = useRelationOptions(relation);
3634
+ if (value == null || value === "") return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
3635
+ const label = labelFor(value);
3636
+ return /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: label || (loading ? "\u2026" : String(value)) });
3637
+ }
3545
3638
  function currencySymbolFor(code, locale) {
3546
3639
  if (!code) return "R$";
3547
3640
  try {
@@ -3628,6 +3721,18 @@ function renderField(field, value, onChange, allValues = {}) {
3628
3721
  className: `${baseClass} min-h-[60px] py-1.5`
3629
3722
  }
3630
3723
  );
3724
+ case "relation":
3725
+ return /* @__PURE__ */ jsx(
3726
+ RelationSelect,
3727
+ {
3728
+ relation: field.relation,
3729
+ value,
3730
+ onChange,
3731
+ className: baseClass,
3732
+ required: field.required,
3733
+ placeholder: field.placeholder ?? `Select ${field.label.toLowerCase()}...`
3734
+ }
3735
+ );
3631
3736
  case "select": {
3632
3737
  const options = (field.options ?? []).map(
3633
3738
  (o) => typeof o === "string" ? { label: o, value: o } : o
@@ -3754,7 +3859,7 @@ function CrudFormPage({ entityDef, mode, initialData, onSubmit, onCancel, namePl
3754
3859
  const computeInit = () => mode === "edit" && initialData ? { ...getDefaultValues(formFields), ...initialData } : getDefaultValues(formFields);
3755
3860
  const [values, setValues] = useState(computeInit);
3756
3861
  const [saving, setSaving] = useState(false);
3757
- const initialRef = React40__default.useRef(JSON.stringify(values));
3862
+ const initialRef = React41__default.useRef(JSON.stringify(values));
3758
3863
  useEffect(() => {
3759
3864
  const init = computeInit();
3760
3865
  setValues(init);
@@ -3948,6 +4053,8 @@ function fieldToColumns(fields, options) {
3948
4053
  /* @__PURE__ */ jsx("span", { className: "h-4 w-4 rounded-full border shrink-0", style: { backgroundColor: String(value) } }),
3949
4054
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: String(value) })
3950
4055
  ] });
4056
+ case "relation":
4057
+ return /* @__PURE__ */ jsx(RelationCell, { relation: field.relation, value });
3951
4058
  case "select":
3952
4059
  return /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: String(value) });
3953
4060
  case "number":
@@ -5275,6 +5382,25 @@ function DocumentsTab({ item }) {
5275
5382
  }
5276
5383
  );
5277
5384
  }
5385
+ function FinancialTab({ item }) {
5386
+ const runtime = usePluginRuntimeOptional();
5387
+ const widgets = runtime ? getWidgetsForZone(runtime, "person.detail.financial") : [];
5388
+ if (widgets.length > 0 && item) {
5389
+ const w = widgets[0];
5390
+ const Widget = w.component;
5391
+ return /* @__PURE__ */ jsx(
5392
+ Widget,
5393
+ {
5394
+ item,
5395
+ config: w.config,
5396
+ runtime: runtime?.context,
5397
+ plugin: w.plugin,
5398
+ widget: w
5399
+ }
5400
+ );
5401
+ }
5402
+ return null;
5403
+ }
5278
5404
  function ScheduleTab({ item }) {
5279
5405
  const t = useTranslation$1();
5280
5406
  if (!item?.id) {
@@ -5292,6 +5418,9 @@ function ScheduleTab({ item }) {
5292
5418
  var PERSON_DETAIL_TABS = [
5293
5419
  { id: "schedule", label: "Schedule", icon: "CalendarDays", component: ScheduleTab, visibleFor: ["staff"] },
5294
5420
  { id: "documents", label: "Documents", icon: "FileText", component: DocumentsTab },
5421
+ // Per-person financial statement — only shown when the financial plugin contributes
5422
+ // its widget (see requiresWidgetZone). Clients = paid invoices; staff = commissions.
5423
+ { id: "financial", label: "Statement", icon: "DollarSign", component: FinancialTab, requiresWidgetZone: "person.detail.financial" },
5295
5424
  { id: "activity", label: "Activity", icon: "Clock", component: ActivityTab }
5296
5425
  ];
5297
5426
  function ComingSoon2({ icon: Icon, title, description }) {
@@ -5744,8 +5873,9 @@ function CrudDetailPage({
5744
5873
  const subtitleValue = entityDef.subtitleField ? item[entityDef.subtitleField] : null;
5745
5874
  const imageValue = entityDef.imageField ? item[entityDef.imageField] : null;
5746
5875
  const initial = typeof displayValue === "string" ? displayValue.charAt(0).toUpperCase() : "?";
5876
+ const runtime = usePluginRuntimeOptional();
5747
5877
  const archetypeTabs = getArchetypeTabs(entityDef.layout, entityDef.data?.archetypeKind);
5748
- const customTabs = [...archetypeTabs, ...entityDef.detailTabs ?? []];
5878
+ const customTabs = [...archetypeTabs, ...entityDef.detailTabs ?? []].filter((tab) => !tab.requiresWidgetZone || (runtime ? getWidgetsForZone(runtime, tab.requiresWidgetZone).length > 0 : false));
5749
5879
  const entityId = entityDef.name.toLowerCase().replace(/\s+/g, "-");
5750
5880
  const widgetZone = `${entityId}.detail.tabs`;
5751
5881
  const validTabs = ["overview", ...customTabs.map((t2) => t2.id)];
@@ -5801,7 +5931,7 @@ function CrudDetailPage({
5801
5931
  })
5802
5932
  ] }),
5803
5933
  /* @__PURE__ */ jsx(TabsContent, { value: "overview", className: embedded ? "mt-2" : "mt-4", children: /* @__PURE__ */ jsx(OverviewTab, { entityDef, item, compact: embedded }) }),
5804
- customTabs.map((tab) => /* @__PURE__ */ jsx(TabsContent, { value: tab.id, className: "mt-4", children: tab.component ? React40__default.createElement(tab.component, { item, entityDef, ...tab.props ?? {} }) : /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: t("common.noContent") }) }) }) }, tab.id))
5934
+ customTabs.map((tab) => /* @__PURE__ */ jsx(TabsContent, { value: tab.id, className: "mt-4", children: tab.component ? React41__default.createElement(tab.component, { item, entityDef, ...tab.props ?? {} }) : /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: t("common.noContent") }) }) }) }, tab.id))
5805
5935
  ] }),
5806
5936
  /* @__PURE__ */ jsx(WidgetSlot, { zone: widgetZone })
5807
5937
  ] });
@@ -5926,7 +6056,7 @@ function StepIndicator({ current }) {
5926
6056
  return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: STEPS.map((step, i) => {
5927
6057
  const isDone = i < currentIdx || current === "done";
5928
6058
  const isActive = step === current;
5929
- return /* @__PURE__ */ jsxs(React40__default.Fragment, { children: [
6059
+ return /* @__PURE__ */ jsxs(React41__default.Fragment, { children: [
5930
6060
  i > 0 && /* @__PURE__ */ jsx("div", { className: cn("h-px w-6", isDone ? "bg-primary" : "bg-border") }),
5931
6061
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
5932
6062
  /* @__PURE__ */ jsx(
@@ -7051,7 +7181,7 @@ function FieldRulesSettings({ entities, rules: initialRules, onSave }) {
7051
7181
  const [saving, setSaving] = useState(false);
7052
7182
  const [search, setSearch] = useState("");
7053
7183
  const [showMore, setShowMore] = useState(false);
7054
- React40.useEffect(() => {
7184
+ React41.useEffect(() => {
7055
7185
  setDraft(initialRules);
7056
7186
  }, [initialRules]);
7057
7187
  const isDirty = useMemo(
@@ -7407,8 +7537,8 @@ function buildSettingsTabs(pluginTabs, can, t, showOrgSettings) {
7407
7537
  settingsTabs.push({
7408
7538
  id: tab.id,
7409
7539
  label: translated === labelKey ? tab.label : translated,
7410
- icon: React40.createElement(IconComp, { className: "h-4 w-4" }),
7411
- component: React40.createElement(tab.component),
7540
+ icon: React41.createElement(IconComp, { className: "h-4 w-4" }),
7541
+ component: React41.createElement(tab.component),
7412
7542
  // Flag plugin-contributed tabs so SettingsPage draws the core/plugins divider.
7413
7543
  isPlugin: true
7414
7544
  });
@@ -7425,7 +7555,7 @@ function AdminShellInner({ appName, layout = "sidebar", logo, pages = [], showSe
7425
7555
  };
7426
7556
  const path = useAdminPath();
7427
7557
  const { user, signOut } = useAuth();
7428
- const navigation = React40.useMemo(() => {
7558
+ const navigation = React41.useMemo(() => {
7429
7559
  const items = runtime.navigation.map((entry) => ({
7430
7560
  ...navEntryToItem(entry),
7431
7561
  position: entry.position
@@ -7436,7 +7566,7 @@ function AdminShellInner({ appName, layout = "sidebar", logo, pages = [], showSe
7436
7566
  }
7437
7567
  return items.sort(compareNavigation);
7438
7568
  }, [runtime.navigation, pages]);
7439
- const routes = React40.useMemo(() => {
7569
+ const routes = React41.useMemo(() => {
7440
7570
  const list = [];
7441
7571
  const entityRouteMap = /* @__PURE__ */ new Map();
7442
7572
  for (const r of runtime.routes) {
@@ -7452,27 +7582,27 @@ function AdminShellInner({ appName, layout = "sidebar", logo, pages = [], showSe
7452
7582
  setEntityRouteMap(entityRouteMap);
7453
7583
  return list.sort((a, b) => routeScore(b.path) - routeScore(a.path));
7454
7584
  }, [runtime.routes, pages, showSettings]);
7455
- const match = React40.useMemo(() => {
7585
+ const match = React41.useMemo(() => {
7456
7586
  for (const r of routes) {
7457
7587
  const params = matchRoute(r.path, path);
7458
7588
  if (params) return { route: r, params };
7459
7589
  }
7460
7590
  return null;
7461
7591
  }, [routes, path]);
7462
- React40.useEffect(() => {
7592
+ React41.useEffect(() => {
7463
7593
  if (!match && (path === "/" || path === "") && navigation.length > 0) {
7464
7594
  navigateTo(navigation[0].route);
7465
7595
  }
7466
7596
  }, [match, path, navigation]);
7467
7597
  const ActiveComponent = match?.route.Component;
7468
7598
  const activeParams = match?.params ?? {};
7469
- const settingsTabs = React40.useMemo(
7599
+ const settingsTabs = React41.useMemo(
7470
7600
  () => buildSettingsTabs(runtime.settingsTabs, can, t, showOrgSettings),
7471
7601
  [runtime.settingsTabs, can, t, showOrgSettings]
7472
7602
  );
7473
7603
  const shellUser = user ? { fullName: user.fullName ?? user.email, email: user.email, avatarUrl: user.avatarUrl } : void 0;
7474
7604
  const moduleVariant = moduleNav ?? (layout === "sidebar" ? "tabs" : "rail");
7475
- const activePageTitle = React40.useMemo(() => {
7605
+ const activePageTitle = React41.useMemo(() => {
7476
7606
  if (path === "/settings" || path.startsWith("/settings/")) return tr("common.settings", "Settings");
7477
7607
  const candidates = navigation.filter((n) => n.route).sort((a, b) => b.route.length - a.route.length);
7478
7608
  const hit = candidates.find(
@@ -7593,7 +7723,7 @@ function buildI18nConfig(config) {
7593
7723
  }
7594
7724
  function BillingInitializer({ config }) {
7595
7725
  const setPlans = useBillingStore((s) => s.setPlans);
7596
- React40.useEffect(() => {
7726
+ React41.useEffect(() => {
7597
7727
  if (config.billing?.plans) setPlans(config.billing.plans.map(normalizeBillingPlan));
7598
7728
  }, [config.billing, setPlans]);
7599
7729
  return null;
@@ -7615,7 +7745,7 @@ function ThemeInitializer({ config }) {
7615
7745
  const initialize = useThemeStore((s) => s.initialize);
7616
7746
  const setOverrides = useThemeStore((s) => s.setOverrides);
7617
7747
  const setMode = useThemeStore((s) => s.setMode);
7618
- React40.useEffect(() => {
7748
+ React41.useEffect(() => {
7619
7749
  if (config.theme) {
7620
7750
  const isSaasTheme = "brand" in config.theme && ("radius" in config.theme || "sidebar" in config.theme || "font" in config.theme);
7621
7751
  const resolved = isSaasTheme ? resolveTheme(config.theme) : config.theme;
@@ -7630,7 +7760,7 @@ function ThemeInitializer({ config }) {
7630
7760
  }
7631
7761
  function AdminProviders({ config, children }) {
7632
7762
  const currentOrg = useOrganizationStore((s) => s.currentOrg);
7633
- const resolved = React40.useMemo(() => {
7763
+ const resolved = React41.useMemo(() => {
7634
7764
  if (config.supabaseUrl && config.supabaseAnonKey) {
7635
7765
  createFayzSupabaseClient(config.supabaseUrl, config.supabaseAnonKey);
7636
7766
  }
@@ -7644,7 +7774,7 @@ function AdminProviders({ config, children }) {
7644
7774
  };
7645
7775
  }, [config]);
7646
7776
  const path = useAdminPath();
7647
- const runtime = React40.useMemo(
7777
+ const runtime = React41.useMemo(
7648
7778
  () => resolvePluginRuntime({
7649
7779
  plugins: resolved.plugins,
7650
7780
  context: {
@@ -7736,10 +7866,10 @@ function hashRouterAdapter2() {
7736
7866
  window.location.hash = path;
7737
7867
  },
7738
7868
  usePathname: () => {
7739
- const [pathname, setPathname] = React40.useState(
7869
+ const [pathname, setPathname] = React41.useState(
7740
7870
  typeof window !== "undefined" ? window.location.hash.slice(1) || "/" : "/"
7741
7871
  );
7742
- React40.useEffect(() => {
7872
+ React41.useEffect(() => {
7743
7873
  const handler = () => setPathname(window.location.hash.slice(1) || "/");
7744
7874
  window.addEventListener("hashchange", handler);
7745
7875
  return () => window.removeEventListener("hashchange", handler);
@@ -7748,7 +7878,7 @@ function hashRouterAdapter2() {
7748
7878
  }
7749
7879
  };
7750
7880
  }
7751
- var RouterContext = React40.createContext(null);
7881
+ var RouterContext = React41.createContext(null);
7752
7882
  var RouterProvider = RouterContext.Provider;
7753
7883
  function MockModeBanner() {
7754
7884
  const { t } = useTranslation();
@@ -8031,8 +8161,8 @@ function BillingPage({ className }) {
8031
8161
  const { plans, subscription, loading } = useBillingStore();
8032
8162
  const billing = useBilling();
8033
8163
  const { t } = useTranslation();
8034
- const [interval, setInterval2] = React40.useState("monthly");
8035
- React40.useEffect(() => {
8164
+ const [interval, setInterval2] = React41.useState("monthly");
8165
+ React41.useEffect(() => {
8036
8166
  billing.fetchSubscription();
8037
8167
  }, []);
8038
8168
  const currentPlan = plans.find((p) => p.id === subscription?.planId);
@@ -8835,7 +8965,7 @@ function LoadingSkeleton() {
8835
8965
  function ProtectedRoute({ children, requiredRole, fallback, onUnauthenticated }) {
8836
8966
  const { user, loading, initialized } = useAuth3();
8837
8967
  const { requireRole } = usePermission2();
8838
- React40__default.useEffect(() => {
8968
+ React41__default.useEffect(() => {
8839
8969
  if (initialized && !loading && !user && onUnauthenticated) {
8840
8970
  onUnauthenticated();
8841
8971
  }
@@ -9662,9 +9792,9 @@ function OrgSwitcher({ collapsed }) {
9662
9792
  const setCurrentProfile = usePermissionsStore((s) => s.setCurrentProfile);
9663
9793
  const setProfiles = usePermissionsStore((s) => s.setProfiles);
9664
9794
  const user = useAuthStore((s) => s.user);
9665
- const [createOpen, setCreateOpen] = React40.useState(false);
9666
- const [newOrgName, setNewOrgName] = React40.useState("");
9667
- const [creating, setCreating] = React40.useState(false);
9795
+ const [createOpen, setCreateOpen] = React41.useState(false);
9796
+ const [newOrgName, setNewOrgName] = React41.useState("");
9797
+ const [creating, setCreating] = React41.useState(false);
9668
9798
  const { t } = useTranslation();
9669
9799
  if (!adapter2 || !currentOrg) return null;
9670
9800
  const initial = currentOrg.name.charAt(0).toUpperCase();
@@ -9850,11 +9980,11 @@ function TenantOnboarding({ verticalId, appName, logo }) {
9850
9980
  const setMembers = useOrganizationStore((s) => s.setMembers);
9851
9981
  const setProfiles = usePermissionsStore((s) => s.setProfiles);
9852
9982
  const setCurrentProfile = usePermissionsStore((s) => s.setCurrentProfile);
9853
- const [step, setStep] = React40.useState(0);
9854
- const [error, setError] = React40.useState("");
9855
- const [companyName, setCompanyName] = React40.useState("");
9856
- const [timezone, setTimezone] = React40.useState("America/Sao_Paulo");
9857
- const [currency, setCurrency] = React40.useState("BRL");
9983
+ const [step, setStep] = React41.useState(0);
9984
+ const [error, setError] = React41.useState("");
9985
+ const [companyName, setCompanyName] = React41.useState("");
9986
+ const [timezone, setTimezone] = React41.useState("America/Sao_Paulo");
9987
+ const [currency, setCurrency] = React41.useState("BRL");
9858
9988
  const handleCreate = async () => {
9859
9989
  if (!user || !companyName.trim()) return;
9860
9990
  setStep(2);
@@ -12188,8 +12318,8 @@ function createGlobalEntitySearch() {
12188
12318
  }
12189
12319
  function CommandPaletteWrapper({ navigation, routerAdapter }) {
12190
12320
  const { commandPaletteOpen, setCommandPaletteOpen } = useLayoutStore();
12191
- const entitySearch = React40__default.useMemo(() => createGlobalEntitySearch(), []);
12192
- const commands = React40__default.useMemo(() => {
12321
+ const entitySearch = React41__default.useMemo(() => createGlobalEntitySearch(), []);
12322
+ const commands = React41__default.useMemo(() => {
12193
12323
  const items = [];
12194
12324
  for (const nav of navigation) {
12195
12325
  items.push({
@@ -12217,13 +12347,13 @@ function CommandPaletteWrapper({ navigation, routerAdapter }) {
12217
12347
  );
12218
12348
  return items;
12219
12349
  }, [navigation, routerAdapter]);
12220
- const handleEntitySelect = React40__default.useCallback((result) => {
12350
+ const handleEntitySelect = React41__default.useCallback((result) => {
12221
12351
  const archetype = result.data?.archetype ? String(result.data.archetype) : void 0;
12222
12352
  const kind = result.data?.kind ? String(result.data.kind) : void 0;
12223
12353
  const href = resolveEntityHref2(result.id, archetype, kind);
12224
12354
  routerAdapter.navigate(href);
12225
12355
  }, [routerAdapter]);
12226
- return React40__default.createElement(CommandPalette, {
12356
+ return React41__default.createElement(CommandPalette, {
12227
12357
  commands,
12228
12358
  open: commandPaletteOpen,
12229
12359
  onOpenChange: setCommandPaletteOpen,
@@ -12234,13 +12364,13 @@ function CommandPaletteWrapper({ navigation, routerAdapter }) {
12234
12364
  function renderLogo(name, logo) {
12235
12365
  if (logo && typeof logo !== "string") return logo;
12236
12366
  const initial = typeof logo === "string" ? logo : name.charAt(0).toUpperCase();
12237
- return React40__default.createElement(
12367
+ return React41__default.createElement(
12238
12368
  "div",
12239
12369
  { className: "flex items-center gap-3" },
12240
- React40__default.createElement("div", {
12370
+ React41__default.createElement("div", {
12241
12371
  className: "w-8 h-8 rounded-lg bg-primary flex items-center justify-center text-primary-foreground font-bold text-sm"
12242
12372
  }, initial),
12243
- React40__default.createElement("span", { className: "font-bold text-lg" }, name)
12373
+ React41__default.createElement("span", { className: "font-bold text-lg" }, name)
12244
12374
  );
12245
12375
  }
12246
12376
  function resolveAuthAdapter2(config) {
@@ -12269,10 +12399,10 @@ function buildSettingsTabs2(config, pluginTabs, can, t) {
12269
12399
  const settingsTabs = [];
12270
12400
  if (config.organization) {
12271
12401
  settingsTabs.push(
12272
- { id: "team", label: t("settings.team"), icon: React40__default.createElement(Users, { className: "h-4 w-4" }), component: React40__default.createElement(TeamTab) },
12273
- { id: "permissions", label: t("settings.permissions"), icon: React40__default.createElement(ShieldCheck, { className: "h-4 w-4" }), component: React40__default.createElement(PermissionProfilesTab) },
12274
- { id: "locations", label: t("settings.locations"), icon: React40__default.createElement(MapPin, { className: "h-4 w-4" }), component: React40__default.createElement(LocationsCrudPage) },
12275
- { id: "field-rules", label: t("settings.fieldRules"), icon: React40__default.createElement(SlidersHorizontal, { className: "h-4 w-4" }), component: React40__default.createElement(ConnectedFieldRulesSettings) }
12402
+ { id: "team", label: t("settings.team"), icon: React41__default.createElement(Users, { className: "h-4 w-4" }), component: React41__default.createElement(TeamTab) },
12403
+ { id: "permissions", label: t("settings.permissions"), icon: React41__default.createElement(ShieldCheck, { className: "h-4 w-4" }), component: React41__default.createElement(PermissionProfilesTab) },
12404
+ { id: "locations", label: t("settings.locations"), icon: React41__default.createElement(MapPin, { className: "h-4 w-4" }), component: React41__default.createElement(LocationsCrudPage) },
12405
+ { id: "field-rules", label: t("settings.fieldRules"), icon: React41__default.createElement(SlidersHorizontal, { className: "h-4 w-4" }), component: React41__default.createElement(ConnectedFieldRulesSettings) }
12276
12406
  );
12277
12407
  }
12278
12408
  if (config.settingsTabs) {
@@ -12288,8 +12418,8 @@ function buildSettingsTabs2(config, pluginTabs, can, t) {
12288
12418
  settingsTabs.push({
12289
12419
  id: tab.id,
12290
12420
  label: pluginLabel === pluginLabelKey ? tab.label : pluginLabel,
12291
- icon: React40__default.createElement(IconComp, { className: "h-4 w-4" }),
12292
- component: React40__default.createElement(tab.component),
12421
+ icon: React41__default.createElement(IconComp, { className: "h-4 w-4" }),
12422
+ component: React41__default.createElement(tab.component),
12293
12423
  isPlugin: true
12294
12424
  });
12295
12425
  }
@@ -12308,7 +12438,7 @@ function createSaasApp(config) {
12308
12438
  const orgAdapter = resolveOrgAdapter2(config);
12309
12439
  const requireAuth = config.auth?.requireAuth ?? !!authAdapter;
12310
12440
  const LoginPageWrapper = () => {
12311
- return React40__default.createElement(LoginPage2, {
12441
+ return React41__default.createElement(LoginPage2, {
12312
12442
  appName: config.name,
12313
12443
  logo: config.auth?.loginLogo ?? logoNode,
12314
12444
  layout: config.auth?.loginLayout ?? "split",
@@ -12331,7 +12461,7 @@ function createSaasApp(config) {
12331
12461
  const { tenantPlugins: hydratedTenantPlugins } = useTenantPlugins({ enabled: shouldHydrateTenantPlugins });
12332
12462
  const { t } = useTranslation();
12333
12463
  const user = authUser ? { fullName: authUser.fullName, email: authUser.email, avatarUrl: authUser.avatarUrl } : config.user;
12334
- React40__default.useEffect(() => {
12464
+ React41__default.useEffect(() => {
12335
12465
  const pf = config.plugins?.flatMap((p) => p.declaredFeatures ?? []) ?? [];
12336
12466
  if (pf.length === 0) return;
12337
12467
  const store = usePermissionsStore.getState();
@@ -12339,8 +12469,12 @@ function createSaasApp(config) {
12339
12469
  const toAdd = pf.filter((f) => !existing.has(f.id));
12340
12470
  if (toAdd.length > 0) store.setFeatures([...store.features, ...toAdd]);
12341
12471
  }, []);
12472
+ const navLabelRef = React41__default.useRef(() => "");
12473
+ React41__default.useEffect(() => {
12474
+ recordNavigation(route, navLabelRef.current(route));
12475
+ }, [route]);
12342
12476
  if (route === "/login") {
12343
- return React40__default.createElement(LoginPageWrapper);
12477
+ return React41__default.createElement(LoginPageWrapper);
12344
12478
  }
12345
12479
  const tenantPluginBindings = config.pluginRuntime?.resolveTenantPlugins?.({
12346
12480
  tenant: currentOrg ? {
@@ -12395,6 +12529,14 @@ function createSaasApp(config) {
12395
12529
  fullBleed: pluginRoute.fullBleed
12396
12530
  });
12397
12531
  }
12532
+ navLabelRef.current = (path) => {
12533
+ const exact = navigation.find((n) => n.route === path)?.label;
12534
+ if (exact) return exact;
12535
+ for (const n of navigation) {
12536
+ if (n.route !== "/" && path.startsWith(n.route + "/")) return n.label;
12537
+ }
12538
+ return "";
12539
+ };
12398
12540
  let matchedPath = route;
12399
12541
  let routeEntry = routes.get(route);
12400
12542
  if (!routeEntry) {
@@ -12413,13 +12555,13 @@ function createSaasApp(config) {
12413
12555
  matchedPath
12414
12556
  }
12415
12557
  };
12416
- const NotFoundPage = () => React40__default.createElement(
12558
+ const NotFoundPage = () => React41__default.createElement(
12417
12559
  "div",
12418
12560
  { className: "flex flex-col items-center justify-center py-24 text-center" },
12419
- React40__default.createElement("h1", { className: "text-6xl font-bold text-muted-foreground/20 mb-4" }, t("app.notFound.title")),
12420
- React40__default.createElement("h2", { className: "text-xl font-semibold mb-2" }, t("app.notFound.heading")),
12421
- React40__default.createElement("p", { className: "text-sm text-muted-foreground mb-6" }, t("app.notFound.message")),
12422
- React40__default.createElement("button", {
12561
+ React41__default.createElement("h1", { className: "text-6xl font-bold text-muted-foreground/20 mb-4" }, t("app.notFound.title")),
12562
+ React41__default.createElement("h2", { className: "text-xl font-semibold mb-2" }, t("app.notFound.heading")),
12563
+ React41__default.createElement("p", { className: "text-sm text-muted-foreground mb-6" }, t("app.notFound.message")),
12564
+ React41__default.createElement("button", {
12423
12565
  className: "rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
12424
12566
  onClick: () => routerAdapter.navigate("/")
12425
12567
  }, t("app.notFound.goToDashboard"))
@@ -12439,67 +12581,67 @@ function createSaasApp(config) {
12439
12581
  routerAdapter.navigate("/login");
12440
12582
  }
12441
12583
  };
12442
- const renderedPage = matchedPath === "/settings" && config.showSettings !== false ? React40__default.createElement(SettingsPage, {
12584
+ const renderedPage = matchedPath === "/settings" && config.showSettings !== false ? React41__default.createElement(SettingsPage, {
12443
12585
  extraTabs: settingsTabs,
12444
- beforeContent: React40__default.createElement(WidgetSlot, {
12586
+ beforeContent: React41__default.createElement(WidgetSlot, {
12445
12587
  zone: "settings.before",
12446
12588
  className: "space-y-4",
12447
12589
  contextOverrides: { matchedPath }
12448
12590
  }),
12449
- afterContent: React40__default.createElement(WidgetSlot, {
12591
+ afterContent: React41__default.createElement(WidgetSlot, {
12450
12592
  zone: "settings.after",
12451
12593
  className: "space-y-4",
12452
12594
  contextOverrides: { matchedPath }
12453
12595
  })
12454
- }) : React40__default.createElement(PageComponent, routeEntry?.plugin ? {
12596
+ }) : React41__default.createElement(PageComponent, routeEntry?.plugin ? {
12455
12597
  plugin: routeEntry.plugin,
12456
12598
  runtime: pluginRuntime.context,
12457
12599
  config: routeEntry.plugin.config
12458
12600
  } : void 0);
12459
12601
  const fullBleed = Boolean(routeEntry?.fullBleed);
12460
- const pageContent = fullBleed ? React40__default.createElement("div", { key: matchedPath, className: "h-full min-h-0 overflow-hidden" }, renderedPage) : React40__default.createElement(
12602
+ const pageContent = fullBleed ? React41__default.createElement("div", { key: matchedPath, className: "h-full min-h-0 overflow-hidden" }, renderedPage) : React41__default.createElement(
12461
12603
  "div",
12462
12604
  { key: matchedPath, className: "saas-page-enter space-y-6 p-6" },
12463
- React40__default.createElement(WidgetSlot, {
12605
+ React41__default.createElement(WidgetSlot, {
12464
12606
  zone: "page.before",
12465
12607
  className: "space-y-4",
12466
12608
  contextOverrides: { matchedPath }
12467
12609
  }),
12468
12610
  renderedPage,
12469
- React40__default.createElement(WidgetSlot, {
12611
+ React41__default.createElement(WidgetSlot, {
12470
12612
  zone: "page.after",
12471
12613
  className: "space-y-4",
12472
12614
  contextOverrides: { matchedPath }
12473
12615
  })
12474
12616
  );
12475
- const pageElement = pagePermission ? React40__default.createElement(
12617
+ const pageElement = pagePermission ? React41__default.createElement(
12476
12618
  PermissionGate,
12477
12619
  {
12478
12620
  feature: pagePermission.feature,
12479
12621
  action: pagePermission.action,
12480
- fallback: React40__default.createElement(
12622
+ fallback: React41__default.createElement(
12481
12623
  "div",
12482
12624
  { className: "flex items-center justify-center py-24" },
12483
- React40__default.createElement(
12625
+ React41__default.createElement(
12484
12626
  "div",
12485
12627
  { className: "text-center" },
12486
- React40__default.createElement("h2", { className: "text-xl font-semibold mb-2" }, t("app.accessDenied.heading")),
12487
- React40__default.createElement("p", { className: "text-muted-foreground" }, t("app.accessDenied.message"))
12628
+ React41__default.createElement("h2", { className: "text-xl font-semibold mb-2" }, t("app.accessDenied.heading")),
12629
+ React41__default.createElement("p", { className: "text-muted-foreground" }, t("app.accessDenied.message"))
12488
12630
  )
12489
12631
  ),
12490
12632
  children: pageContent
12491
12633
  }
12492
12634
  ) : pageContent;
12493
- const orgSwitcherElement = orgAdapter ? React40__default.createElement(OrgSwitcher) : void 0;
12494
- const content = React40__default.createElement(
12635
+ const orgSwitcherElement = orgAdapter ? React41__default.createElement(OrgSwitcher) : void 0;
12636
+ const content = React41__default.createElement(
12495
12637
  PluginRuntimeProvider,
12496
12638
  { value: pluginRuntime },
12497
- React40__default.createElement(
12498
- React40__default.Fragment,
12639
+ React41__default.createElement(
12640
+ React41__default.Fragment,
12499
12641
  null,
12500
- React40__default.createElement(MockModeBanner),
12501
- React40__default.createElement(ImpersonationBanner),
12502
- React40__default.createElement(
12642
+ React41__default.createElement(MockModeBanner),
12643
+ React41__default.createElement(ImpersonationBanner),
12644
+ React41__default.createElement(
12503
12645
  AppShell,
12504
12646
  {
12505
12647
  variant: layout,
@@ -12520,22 +12662,22 @@ function createSaasApp(config) {
12520
12662
  } : void 0,
12521
12663
  logo: logoNode,
12522
12664
  orgSwitcher: orgSwitcherElement,
12523
- topbarStart: React40__default.createElement(WidgetSlot, {
12665
+ topbarStart: React41__default.createElement(WidgetSlot, {
12524
12666
  zone: "shell.topbar.start",
12525
12667
  className: "flex items-center gap-2",
12526
12668
  contextOverrides: { matchedPath }
12527
12669
  }),
12528
- topbarEnd: React40__default.createElement(WidgetSlot, {
12670
+ topbarEnd: React41__default.createElement(WidgetSlot, {
12529
12671
  zone: "shell.topbar.end",
12530
12672
  className: "flex items-center gap-2",
12531
12673
  contextOverrides: { matchedPath }
12532
12674
  }),
12533
- sidebarTopContent: React40__default.createElement(WidgetSlot, {
12675
+ sidebarTopContent: React41__default.createElement(WidgetSlot, {
12534
12676
  zone: "shell.sidebar.before-nav",
12535
12677
  className: "space-y-2",
12536
12678
  contextOverrides: { matchedPath }
12537
12679
  }),
12538
- sidebarFooterContent: React40__default.createElement(WidgetSlot, {
12680
+ sidebarFooterContent: React41__default.createElement(WidgetSlot, {
12539
12681
  zone: "shell.sidebar.footer",
12540
12682
  className: "space-y-2",
12541
12683
  contextOverrides: { matchedPath }
@@ -12545,26 +12687,26 @@ function createSaasApp(config) {
12545
12687
  pageElement
12546
12688
  ),
12547
12689
  // Org initializer
12548
- orgAdapter ? React40__default.createElement(OrgInitializer, { verticalId: config.verticalId }) : null,
12690
+ orgAdapter ? React41__default.createElement(OrgInitializer, { verticalId: config.verticalId }) : null,
12549
12691
  // Location switch overlay
12550
- React40__default.createElement(LocationSwitchOverlay),
12692
+ React41__default.createElement(LocationSwitchOverlay),
12551
12693
  // Chat
12552
- config.chat?.enabled !== false && config.chat ? React40__default.createElement(
12553
- React40__default.Fragment,
12694
+ config.chat?.enabled !== false && config.chat ? React41__default.createElement(
12695
+ React41__default.Fragment,
12554
12696
  null,
12555
- React40__default.createElement(ChatFab, { apiEndpoint: config.chat.apiEndpoint, systemPrompt: config.chat.systemPrompt }),
12556
- React40__default.createElement(ChatPanel, { title: config.chat.title, apiEndpoint: config.chat.apiEndpoint, systemPrompt: config.chat.systemPrompt })
12697
+ React41__default.createElement(ChatFab, { apiEndpoint: config.chat.apiEndpoint, systemPrompt: config.chat.systemPrompt }),
12698
+ React41__default.createElement(ChatPanel, { title: config.chat.title, apiEndpoint: config.chat.apiEndpoint, systemPrompt: config.chat.systemPrompt })
12557
12699
  ) : null,
12558
- React40__default.createElement(WidgetSlot, {
12700
+ React41__default.createElement(WidgetSlot, {
12559
12701
  zone: "shell.floating",
12560
12702
  contextOverrides: { matchedPath }
12561
12703
  }),
12562
12704
  // Command palette
12563
- React40__default.createElement(CommandPaletteWrapper, { navigation, routerAdapter })
12705
+ React41__default.createElement(CommandPaletteWrapper, { navigation, routerAdapter })
12564
12706
  )
12565
12707
  );
12566
12708
  if (requireAuth && authAdapter) {
12567
- return React40__default.createElement(
12709
+ return React41__default.createElement(
12568
12710
  ProtectedRoute,
12569
12711
  { onUnauthenticated: () => {
12570
12712
  routerAdapter.navigate("/login");
@@ -12580,7 +12722,7 @@ function createSaasApp(config) {
12580
12722
  const setPlans = useBillingStore((s) => s.setPlans);
12581
12723
  const setFeatures = usePermissionsStore((s) => s.setFeatures);
12582
12724
  const setMode = useThemeStore((s) => s.setMode);
12583
- React40__default.useEffect(() => {
12725
+ React41__default.useEffect(() => {
12584
12726
  if (config.theme) {
12585
12727
  const isSaasTheme = "brand" in config.theme && ("radius" in config.theme || "sidebar" in config.theme || "font" in config.theme);
12586
12728
  const resolved = isSaasTheme ? resolveTheme(config.theme) : config.theme;
@@ -12591,34 +12733,34 @@ function createSaasApp(config) {
12591
12733
  }
12592
12734
  initialize();
12593
12735
  }, [initialize, setOverrides, setMode]);
12594
- React40__default.useEffect(() => {
12736
+ React41__default.useEffect(() => {
12595
12737
  if (config.billing?.plans) {
12596
12738
  setPlans(config.billing.plans.map(normalizePlanConfig));
12597
12739
  }
12598
12740
  }, [setPlans]);
12599
- React40__default.useEffect(() => {
12741
+ React41__default.useEffect(() => {
12600
12742
  if (config.permissions?.features) {
12601
12743
  setFeatures(config.permissions.features);
12602
12744
  }
12603
12745
  }, [setFeatures]);
12604
- React40__default.useEffect(() => {
12746
+ React41__default.useEffect(() => {
12605
12747
  setCurrentLocale(config.locale?.default ?? "en");
12606
12748
  setDefaultCurrency(config.locale?.currency);
12607
12749
  }, []);
12608
- let inner = React40__default.createElement(
12750
+ let inner = React41__default.createElement(
12609
12751
  RouterProvider,
12610
12752
  { value: routerAdapter },
12611
- React40__default.createElement(ToastProvider, null),
12612
- React40__default.createElement(AppContent)
12753
+ React41__default.createElement(ToastProvider, null),
12754
+ React41__default.createElement(AppContent)
12613
12755
  );
12614
12756
  if (orgAdapter) {
12615
- inner = React40__default.createElement(
12757
+ inner = React41__default.createElement(
12616
12758
  OrgAdapterProvider,
12617
12759
  { value: orgAdapter, children: inner }
12618
12760
  );
12619
12761
  }
12620
12762
  if (authAdapter) {
12621
- inner = React40__default.createElement(
12763
+ inner = React41__default.createElement(
12622
12764
  AuthAdapterProvider,
12623
12765
  { value: authAdapter, children: inner }
12624
12766
  );
@@ -12638,7 +12780,7 @@ function createSaasApp(config) {
12638
12780
  mergedTranslations[loc] = layer;
12639
12781
  }
12640
12782
  registerTranslations(mergedTranslations);
12641
- inner = React40__default.createElement(I18nProvider, {
12783
+ inner = React41__default.createElement(I18nProvider, {
12642
12784
  value: {
12643
12785
  defaultLocale: config.locale?.default ?? "en",
12644
12786
  supported: supportedLocales,
@@ -12737,11 +12879,11 @@ function manifestToFayzConfig(manifest, surfaceName) {
12737
12879
  function AdminScaffold({ manifest, surface }) {
12738
12880
  const liveConfigRef = manifest.surfaces[surface]?.options?.[liveConfigOption];
12739
12881
  const liveConfig = typeof liveConfigRef === "string" ? liveSaasConfigs.get(liveConfigRef) : void 0;
12740
- const LegacyApp = React40.useMemo(() => {
12882
+ const LegacyApp = React41.useMemo(() => {
12741
12883
  if (!liveConfig || !hasLegacyShellConfig(liveConfig)) return null;
12742
12884
  return createSaasApp(liveConfig);
12743
12885
  }, [liveConfig]);
12744
- const config = React40.useMemo(() => manifestToFayzConfig(manifest, surface), [manifest, surface]);
12886
+ const config = React41.useMemo(() => manifestToFayzConfig(manifest, surface), [manifest, surface]);
12745
12887
  if (LegacyApp) return /* @__PURE__ */ jsx(LegacyApp, {});
12746
12888
  return /* @__PURE__ */ jsx(AdminProviders, { config, children: /* @__PURE__ */ jsx(
12747
12889
  AdminShell,
@@ -13076,19 +13218,19 @@ function createArchetypeLookup(config) {
13076
13218
  }
13077
13219
  function createPlaceholder(title, description) {
13078
13220
  return function PlaceholderPage() {
13079
- return React40__default.createElement(
13221
+ return React41__default.createElement(
13080
13222
  "div",
13081
13223
  { className: "mx-auto max-w-4xl" },
13082
- React40__default.createElement("h1", { className: "text-3xl font-bold text-foreground" }, title),
13083
- description ? React40__default.createElement("p", { className: "mt-1 text-muted-foreground" }, description) : null,
13084
- React40__default.createElement(
13224
+ React41__default.createElement("h1", { className: "text-3xl font-bold text-foreground" }, title),
13225
+ description ? React41__default.createElement("p", { className: "mt-1 text-muted-foreground" }, description) : null,
13226
+ React41__default.createElement(
13085
13227
  "div",
13086
13228
  { className: "mt-8 flex items-center justify-center rounded-card border border-dashed border-border bg-card p-16" },
13087
- React40__default.createElement(
13229
+ React41__default.createElement(
13088
13230
  "div",
13089
13231
  { className: "text-center" },
13090
- React40__default.createElement("p", { className: "text-lg font-medium text-muted-foreground" }, "Coming Soon"),
13091
- React40__default.createElement(
13232
+ React41__default.createElement("p", { className: "text-lg font-medium text-muted-foreground" }, "Coming Soon"),
13233
+ React41__default.createElement(
13092
13234
  "p",
13093
13235
  { className: "mt-1 text-sm text-muted-foreground/70" },
13094
13236
  "This module is being built as a plugin."
@@ -13235,9 +13377,9 @@ function ModuleActionBar({
13235
13377
  ] });
13236
13378
  }
13237
13379
  function createPluginContext(displayName) {
13238
- const ConfigContext = React40__default.createContext(null);
13239
- const ProviderContext = React40__default.createContext(null);
13240
- const StoreContext = React40__default.createContext(null);
13380
+ const ConfigContext = React41__default.createContext(null);
13381
+ const ProviderContext = React41__default.createContext(null);
13382
+ const StoreContext = React41__default.createContext(null);
13241
13383
  const ContextProvider = ({
13242
13384
  config,
13243
13385
  provider,
@@ -13246,17 +13388,17 @@ function createPluginContext(displayName) {
13246
13388
  }) => /* @__PURE__ */ jsx(ConfigContext.Provider, { value: config, children: /* @__PURE__ */ jsx(ProviderContext.Provider, { value: provider, children: /* @__PURE__ */ jsx(StoreContext.Provider, { value: store, children }) }) });
13247
13389
  ContextProvider.displayName = `${displayName}ContextProvider`;
13248
13390
  function useConfig() {
13249
- const ctx = React40__default.useContext(ConfigContext);
13391
+ const ctx = React41__default.useContext(ConfigContext);
13250
13392
  if (!ctx) throw new Error(`useConfig must be used within ${displayName}`);
13251
13393
  return ctx;
13252
13394
  }
13253
13395
  function useProvider() {
13254
- const ctx = React40__default.useContext(ProviderContext);
13396
+ const ctx = React41__default.useContext(ProviderContext);
13255
13397
  if (!ctx) throw new Error(`useProvider must be used within ${displayName}`);
13256
13398
  return ctx;
13257
13399
  }
13258
13400
  function useStore$1(selector) {
13259
- const store = React40__default.useContext(StoreContext);
13401
+ const store = React41__default.useContext(StoreContext);
13260
13402
  if (!store) throw new Error(`useStore must be used within ${displayName}`);
13261
13403
  return useStore(store, selector);
13262
13404
  }
@@ -13314,7 +13456,7 @@ function PluginRegistryManager({ registries, routeBase, className }) {
13314
13456
  return registries[0]?.id ?? "";
13315
13457
  };
13316
13458
  const [activeTab, setActiveTab] = useState(getTabFromHash);
13317
- React40__default.useEffect(() => {
13459
+ React41__default.useEffect(() => {
13318
13460
  if (!routeBase) return;
13319
13461
  const handler = () => setActiveTab(getTabFromHash());
13320
13462
  window.addEventListener("hashchange", handler);
@@ -13386,6 +13528,16 @@ function useModuleNavigation(hashBase, depthMap, homeView = "summary") {
13386
13528
  const [view, setView] = useState(getInitialView);
13387
13529
  const [direction, setDirection] = useState("forward");
13388
13530
  const prevDepthRef = useRef(getDepth(getInitialView()));
13531
+ const historyRef = useRef([getInitialView()]);
13532
+ const [previousView, setPreviousView] = useState(null);
13533
+ useEffect(() => {
13534
+ const stack = historyRef.current;
13535
+ if (stack[stack.length - 1] !== view) {
13536
+ stack.push(view);
13537
+ if (stack.length > 30) stack.shift();
13538
+ }
13539
+ setPreviousView(stack.length >= 2 ? stack[stack.length - 2] : null);
13540
+ }, [view]);
13389
13541
  useEffect(() => {
13390
13542
  function handler() {
13391
13543
  const hash = (window.location.hash.slice(1) || "/").split("?")[0];
@@ -13431,8 +13583,15 @@ function useModuleNavigation(hashBase, depthMap, homeView = "summary") {
13431
13583
  }
13432
13584
  }
13433
13585
  }, [hashBase]);
13586
+ const back = useCallback((fallbackView) => {
13587
+ if (historyRef.current.length >= 2) {
13588
+ window.history.back();
13589
+ } else {
13590
+ navigate(fallbackView ?? homeView);
13591
+ }
13592
+ }, [navigate, homeView]);
13434
13593
  const animationClass = direction === "forward" ? "saas-nav-forward" : "saas-nav-back";
13435
- return { view, direction, animationClass, navigate };
13594
+ return { view, direction, animationClass, navigate, previousView, back };
13436
13595
  }
13437
13596
  var personLookup = createArchetypeLookup({ archetype: "person" });
13438
13597
  function formatAge(dob) {
@@ -13619,6 +13778,147 @@ function usePluginPrefs(pluginId, defaults) {
13619
13778
  }, [pluginId, defaults]);
13620
13779
  return useMemo(() => ({ get, set, update, getAll, reset }), [get, set, update, getAll, reset]);
13621
13780
  }
13781
+ function ConnectorCard({ connector }) {
13782
+ const { t } = useTranslation();
13783
+ const [status, setStatus] = useState(null);
13784
+ const [open, setOpen] = useState(false);
13785
+ const [values, setValues] = useState({});
13786
+ const [testing, setTesting] = useState(false);
13787
+ const [saving, setSaving] = useState(false);
13788
+ const [oauthing, setOauthing] = useState(false);
13789
+ const loadStatus = useCallback(async () => {
13790
+ try {
13791
+ setStatus(await connector.getStatus());
13792
+ } catch {
13793
+ setStatus({ connected: false });
13794
+ }
13795
+ }, [connector]);
13796
+ useEffect(() => {
13797
+ void loadStatus();
13798
+ }, [loadStatus]);
13799
+ const connected = !!status?.connected;
13800
+ const Icon = connector.icon ? ICON_MAP$1[connector.icon] ?? Plug : Plug;
13801
+ async function handleTest() {
13802
+ if (!connector.testConnection) return;
13803
+ setTesting(true);
13804
+ try {
13805
+ const res = await connector.testConnection(values);
13806
+ res.ok ? toast.success(res.message ?? t("connectors.connected")) : toast.error(res.message ?? t("common.error"));
13807
+ } catch (e) {
13808
+ toast.error(e?.message ?? t("common.error"));
13809
+ } finally {
13810
+ setTesting(false);
13811
+ }
13812
+ }
13813
+ async function handleSave() {
13814
+ if (!connector.saveConnection) return;
13815
+ setSaving(true);
13816
+ try {
13817
+ await connector.saveConnection(values);
13818
+ toast.success(t("connectors.save"));
13819
+ await loadStatus();
13820
+ } catch (e) {
13821
+ toast.error(e?.message ?? t("common.error"));
13822
+ } finally {
13823
+ setSaving(false);
13824
+ }
13825
+ }
13826
+ async function handleConnectOAuth() {
13827
+ if (!connector.startOAuth) return;
13828
+ setOauthing(true);
13829
+ try {
13830
+ window.location.href = await connector.startOAuth(window.location.href);
13831
+ } catch (e) {
13832
+ toast.error(e?.message ?? t("common.error"));
13833
+ setOauthing(false);
13834
+ }
13835
+ }
13836
+ async function handleDisconnect() {
13837
+ if (!connector.disconnect) return;
13838
+ try {
13839
+ await connector.disconnect();
13840
+ toast.success(t("connectors.disconnect"));
13841
+ await loadStatus();
13842
+ } catch (e) {
13843
+ toast.error(e?.message ?? t("common.error"));
13844
+ }
13845
+ }
13846
+ const ExtraPanel = connector.ExtraPanel;
13847
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card shadow-sm overflow-hidden", children: [
13848
+ /* @__PURE__ */ jsxs(
13849
+ "button",
13850
+ {
13851
+ onClick: () => setOpen((o) => !o),
13852
+ className: "w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-muted/30 transition-colors",
13853
+ children: [
13854
+ /* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-muted shrink-0", children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }) }),
13855
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
13856
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold truncate", children: connector.name }),
13857
+ connector.description && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground truncate", children: connector.description })
13858
+ ] }),
13859
+ /* @__PURE__ */ jsx("span", { className: `inline-flex items-center gap-1 text-[11px] shrink-0 ${connected ? "text-success" : "text-muted-foreground"}`, children: connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
13860
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5" }),
13861
+ " ",
13862
+ t("connectors.connected")
13863
+ ] }) : t("connectors.notConnected") }),
13864
+ /* @__PURE__ */ jsx(ChevronDown, { className: `h-4 w-4 text-muted-foreground shrink-0 transition-transform ${open ? "rotate-180" : ""}` })
13865
+ ]
13866
+ }
13867
+ ),
13868
+ open && /* @__PURE__ */ jsxs("div", { className: "border-t px-4 py-4 space-y-4", children: [
13869
+ connector.authKind === "oauth" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
13870
+ /* @__PURE__ */ jsxs(Button, { onClick: handleConnectOAuth, disabled: oauthing, children: [
13871
+ oauthing ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Plug, { className: "h-4 w-4" }),
13872
+ connected ? t("connectors.connected") : `${t("connectors.connect")} ${connector.name}`
13873
+ ] }),
13874
+ connected && connector.disconnect && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: handleDisconnect, children: [
13875
+ /* @__PURE__ */ jsx(Power, { className: "h-3.5 w-3.5" }),
13876
+ " ",
13877
+ t("connectors.disconnect")
13878
+ ] })
13879
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
13880
+ /* @__PURE__ */ jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: (connector.fields ?? []).map((f) => /* @__PURE__ */ jsxs("label", { className: "block", children: [
13881
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: f.label }),
13882
+ /* @__PURE__ */ jsx(
13883
+ "input",
13884
+ {
13885
+ type: f.type,
13886
+ value: values[f.key] ?? "",
13887
+ onChange: (e) => setValues((v) => ({ ...v, [f.key]: e.target.value })),
13888
+ placeholder: f.placeholder,
13889
+ className: "w-full mt-1 rounded-input border border-input bg-card px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
13890
+ }
13891
+ )
13892
+ ] }, f.key)) }),
13893
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
13894
+ connector.testConnection && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: handleTest, disabled: testing, children: [
13895
+ testing ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Plug, { className: "h-3.5 w-3.5" }),
13896
+ " ",
13897
+ t("connectors.test")
13898
+ ] }),
13899
+ connector.saveConnection && /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: handleSave, disabled: saving, children: [
13900
+ saving ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5" }),
13901
+ " ",
13902
+ t("connectors.save")
13903
+ ] }),
13904
+ connected && connector.disconnect && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: handleDisconnect, children: [
13905
+ /* @__PURE__ */ jsx(Power, { className: "h-3.5 w-3.5" }),
13906
+ " ",
13907
+ t("connectors.disconnect")
13908
+ ] })
13909
+ ] })
13910
+ ] }),
13911
+ ExtraPanel && /* @__PURE__ */ jsx("div", { className: "pt-1", children: /* @__PURE__ */ jsx(ExtraPanel, {}) })
13912
+ ] })
13913
+ ] });
13914
+ }
13915
+ function ConnectorsHub({ connectors }) {
13916
+ const { t } = useTranslation();
13917
+ if (!connectors || connectors.length === 0) {
13918
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-8 text-center", children: t("connectors.empty") });
13919
+ }
13920
+ return /* @__PURE__ */ jsx("div", { className: "space-y-3 max-w-3xl", children: connectors.map((c) => /* @__PURE__ */ jsx(ConnectorCard, { connector: c }, c.id)) });
13921
+ }
13622
13922
  function RegistryCrudView2({ registry: registry2, basePath }) {
13623
13923
  const useStore = useMemo(() => {
13624
13924
  const mockData = registry2.mockData ?? registry2.seedData ?? [];
@@ -13636,8 +13936,59 @@ function RegistryCrudView2({ registry: registry2, basePath }) {
13636
13936
  }
13637
13937
  );
13638
13938
  }
13639
- function PluginSettingsPanel({ title, subtitle, generalSettings, registries, customTabs, routeBase, onClose }) {
13939
+ function PropertiesTab({ registries, routeBase, regLabel }) {
13940
+ function getActive() {
13941
+ const hash = window.location.hash.slice(1);
13942
+ const nested = `${routeBase}/_properties/`;
13943
+ if (hash.startsWith(nested)) {
13944
+ const id = hash.slice(nested.length).split("/")[0];
13945
+ if (registries.find((r) => r.id === id)) return id;
13946
+ }
13947
+ const flat = `${routeBase}/`;
13948
+ if (hash.startsWith(flat)) {
13949
+ const id = hash.slice(flat.length).split("/")[0];
13950
+ if (registries.find((r) => r.id === id)) return id;
13951
+ }
13952
+ return registries[0]?.id ?? "";
13953
+ }
13954
+ const [active, setActive] = useState(getActive);
13955
+ React41__default.useEffect(() => {
13956
+ const handler = () => setActive(getActive());
13957
+ window.addEventListener("hashchange", handler);
13958
+ return () => window.removeEventListener("hashchange", handler);
13959
+ }, [routeBase, registries]);
13960
+ function select(id) {
13961
+ setActive(id);
13962
+ window.location.hash = `${routeBase}/_properties/${id}`;
13963
+ }
13964
+ const activeReg = registries.find((r) => r.id === active) ?? registries[0];
13965
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
13966
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1.5 flex-wrap", children: registries.map((r) => {
13967
+ const Icon = r.icon ? ICON_MAP$1[r.icon] ?? null : null;
13968
+ const on = r.id === activeReg?.id;
13969
+ return /* @__PURE__ */ jsxs(
13970
+ "button",
13971
+ {
13972
+ onClick: () => select(r.id),
13973
+ className: cn(
13974
+ "inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors",
13975
+ on ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/70"
13976
+ ),
13977
+ children: [
13978
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
13979
+ regLabel(r.id, r.entity.name)
13980
+ ]
13981
+ },
13982
+ r.id
13983
+ );
13984
+ }) }),
13985
+ activeReg?.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: regLabel(activeReg.id + ".description", activeReg.description) }),
13986
+ activeReg && /* @__PURE__ */ jsx(RegistryCrudView2, { registry: activeReg, basePath: `${routeBase}/_properties/${activeReg.id}` }, activeReg.id)
13987
+ ] });
13988
+ }
13989
+ function PluginSettingsPanel({ title, subtitle, generalSettings, registries, customTabs, routeBase, hostPluginId, onClose }) {
13640
13990
  const { t } = useTranslation();
13991
+ const connectors = useConnectorsForPlugin(hostPluginId ?? "");
13641
13992
  const regLabel = (id, fallback) => {
13642
13993
  const key = `registry.${id}`;
13643
13994
  const translated = t(key);
@@ -13646,20 +13997,21 @@ function PluginSettingsPanel({ title, subtitle, generalSettings, registries, cus
13646
13997
  const tabs = useMemo(() => {
13647
13998
  const list = [];
13648
13999
  if (generalSettings) {
13649
- list.push({ id: "_general", label: t("settings.general"), icon: "Settings", type: "general" });
14000
+ list.push({ id: "_general", label: t("settings.general"), IconComp: Settings, type: "general" });
13650
14001
  }
13651
14002
  if (customTabs) {
13652
14003
  for (const tab of customTabs) {
13653
14004
  list.push({ id: `_custom_${tab.id}`, label: tab.label, icon: tab.icon, type: "custom" });
13654
14005
  }
13655
14006
  }
13656
- if (registries) {
13657
- for (const reg of registries) {
13658
- list.push({ id: reg.id, label: regLabel(reg.id, reg.entity.name), icon: reg.icon, type: "registry", registryDef: reg });
13659
- }
14007
+ if (connectors.length > 0) {
14008
+ list.push({ id: "_integrations", label: t("connectors.title"), IconComp: Plug, type: "integrations" });
14009
+ }
14010
+ if (registries && registries.length > 0) {
14011
+ list.push({ id: "_properties", label: t("settings.properties"), IconComp: TableProperties, type: "properties" });
13660
14012
  }
13661
14013
  return list;
13662
- }, [generalSettings, customTabs, registries, t]);
14014
+ }, [generalSettings, customTabs, connectors.length, registries, t]);
13663
14015
  const useHashRouting = true;
13664
14016
  function getActiveFromHash() {
13665
14017
  const hash = window.location.hash.slice(1) || "/";
@@ -13667,11 +14019,12 @@ function PluginSettingsPanel({ title, subtitle, generalSettings, registries, cus
13667
14019
  const rest = hash.slice(routeBase.length + 1).split("/")[0];
13668
14020
  const match = tabs.find((t2) => t2.id === rest);
13669
14021
  if (match) return match.id;
14022
+ if (registries?.some((r) => r.id === rest) && tabs.some((t2) => t2.id === "_properties")) return "_properties";
13670
14023
  }
13671
14024
  return tabs[0]?.id ?? "";
13672
14025
  }
13673
14026
  const [activeTab, setActiveTab] = useState(getActiveFromHash);
13674
- React40__default.useEffect(() => {
14027
+ React41__default.useEffect(() => {
13675
14028
  const handler = () => setActiveTab(getActiveFromHash());
13676
14029
  window.addEventListener("hashchange", handler);
13677
14030
  return () => window.removeEventListener("hashchange", handler);
@@ -13693,7 +14046,7 @@ function PluginSettingsPanel({ title, subtitle, generalSettings, registries, cus
13693
14046
  ] })
13694
14047
  ] }),
13695
14048
  /* @__PURE__ */ jsx("div", { className: "flex gap-0.5 overflow-x-auto border-b scrollbar-none", style: { scrollbarWidth: "none" }, children: tabs.map((tab) => {
13696
- const Icon = tab.icon ? ICON_MAP$1[tab.icon] ?? null : null;
14049
+ const Icon = tab.IconComp ?? (tab.icon ? ICON_MAP$1[tab.icon] ?? null : null);
13697
14050
  const active = tab.id === activeTab;
13698
14051
  return /* @__PURE__ */ jsxs(
13699
14052
  "button",
@@ -13711,20 +14064,13 @@ function PluginSettingsPanel({ title, subtitle, generalSettings, registries, cus
13711
14064
  tab.id
13712
14065
  );
13713
14066
  }) }),
13714
- activeTabDef?.type === "registry" && activeTabDef.registryDef?.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: regLabel(activeTabDef.id + ".description", activeTabDef.registryDef.description) }),
13715
14067
  activeTabDef?.type === "general" && generalSettings && /* @__PURE__ */ jsx("div", { children: generalSettings }),
13716
14068
  activeTabDef?.type === "custom" && customTabContent && /* @__PURE__ */ jsx("div", { children: customTabContent.content }),
13717
- activeTabDef?.type === "registry" && activeTabDef.registryDef && /* @__PURE__ */ jsx(
13718
- RegistryCrudView2,
13719
- {
13720
- registry: activeTabDef.registryDef,
13721
- basePath: `${routeBase}/${activeTabDef.id}`
13722
- },
13723
- activeTabDef.id
13724
- )
14069
+ activeTabDef?.type === "integrations" && /* @__PURE__ */ jsx(ConnectorsHub, { connectors }),
14070
+ activeTabDef?.type === "properties" && registries && registries.length > 0 && /* @__PURE__ */ jsx(PropertiesTab, { registries, routeBase, regLabel })
13725
14071
  ] });
13726
14072
  }
13727
14073
 
13728
- export { AdminProviders, AdminScaffold, AdminShell, ClientOrdersTab, CrudCardGrid, CrudDetailPage, CrudFormPage, CrudListView, CrudPage, DeleteConfirmDialog, ImportWizard, LoginPage, ModuleActionBar, PermissionGate, PersonLink2 as PersonLink, PluginRegistryManager, PluginSettingsPanel, QuickActionsButton, SelectRow, SettingsGroup, ToggleRow2 as ToggleRow, WidgetSlot, navigateTo as adminNavigateTo, createArchetypeLookup, createClientOrdersProvider, createCrudPage, createFayzApp, createCrudPage as createNativeCrudPage, createPlaceholder, createPluginContext, createSaasApp, createViewRouter, dedup, defineSaas, exportToCSV, formatCurrency4 as formatCurrency, getScheduleBlockConfig, parseViewId, resolveEntityHref, setScheduleBlockConfig, subscribeScheduleBlockConfig, useAdminPath, useFieldRules, useModuleNavigation, usePluginPrefs };
14074
+ export { AdminProviders, AdminScaffold, AdminShell, ClientOrdersTab, ConnectorsHub, CrudCardGrid, CrudDetailPage, CrudFormPage, CrudListView, CrudPage, DeleteConfirmDialog, ImportWizard, LoginPage, ModuleActionBar, PermissionGate, PersonLink2 as PersonLink, PluginRegistryManager, PluginSettingsPanel, QuickActionsButton, SelectRow, SettingsGroup, ToggleRow2 as ToggleRow, WidgetSlot, navigateTo as adminNavigateTo, createArchetypeLookup, createClientOrdersProvider, createCrudPage, createFayzApp, createCrudPage as createNativeCrudPage, createPlaceholder, createPluginContext, createSaasApp, createViewRouter, dedup, defineSaas, exportToCSV, formatCurrency4 as formatCurrency, getScheduleBlockConfig, parseViewId, resolveEntityHref, setScheduleBlockConfig, subscribeScheduleBlockConfig, useAdminPath, useFieldRules, useModuleNavigation, usePluginPrefs };
13729
14075
  //# sourceMappingURL=index.js.map
13730
14076
  //# sourceMappingURL=index.js.map