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