@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.
- package/LICENSE +21 -0
- package/README.md +42 -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/hooks/useModuleNavigation.d.ts +2 -0
- package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
- package/dist/index.cjs +726 -379
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +550 -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/package.json +4 -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/hooks/useModuleNavigation.ts +27 -1
- package/src/index.ts +1 -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/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
|
|
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
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
|
|
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] =
|
|
268
|
-
const indexRef =
|
|
269
|
-
|
|
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] =
|
|
315
|
-
const [activeSuggestion, setActiveSuggestion] =
|
|
316
|
-
const timersRef =
|
|
317
|
-
const prevSuggestionRef =
|
|
318
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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] =
|
|
502
|
-
const [showTools, setShowTools] =
|
|
503
|
-
const scrollRef =
|
|
504
|
-
const inputRef =
|
|
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
|
-
|
|
507
|
+
React41.useEffect(() => {
|
|
508
508
|
scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
|
|
509
509
|
}, [messages]);
|
|
510
|
-
|
|
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] =
|
|
1068
|
-
|
|
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] =
|
|
1124
|
-
const [email, setEmail] =
|
|
1125
|
-
const [password, setPassword] =
|
|
1126
|
-
const [fullName, setFullName] =
|
|
1127
|
-
const [notice, setNotice] =
|
|
1128
|
-
const [submitting, setSubmitting] =
|
|
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] =
|
|
1370
|
-
const [timezone, setTimezone] =
|
|
1371
|
-
const [currency, setCurrency] =
|
|
1372
|
-
const [saving, setSaving] =
|
|
1373
|
-
|
|
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] =
|
|
1524
|
-
const [saving, setSaving] =
|
|
1525
|
-
const fileInputRef =
|
|
1526
|
-
|
|
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] =
|
|
1736
|
-
const [resetSent, setResetSent] =
|
|
1737
|
-
const [savingTenant, setSavingTenant] =
|
|
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 =
|
|
1898
|
-
const [preview, setPreview] =
|
|
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] =
|
|
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
|
-
|
|
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 =
|
|
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] =
|
|
2191
|
+
const [activeTab, setActiveTab] = React41.useState(() => {
|
|
2192
2192
|
return getTabFromHash() ?? defaultTab ?? null;
|
|
2193
2193
|
});
|
|
2194
|
-
|
|
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(
|
|
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] =
|
|
2275
|
-
const [profileId, setProfileId] =
|
|
2276
|
-
const [sending, setSending] =
|
|
2277
|
-
const [sent, setSent] =
|
|
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] =
|
|
2476
|
-
|
|
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] =
|
|
2624
|
-
const [description, setDescription] =
|
|
2625
|
-
const [systemPerms, setSystemPerms] =
|
|
2626
|
-
const [grants, setGrants] =
|
|
2627
|
-
const [collapsed, setCollapsed] =
|
|
2628
|
-
const [search, setSearch] =
|
|
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 =
|
|
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(
|
|
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] =
|
|
2759
|
-
const [creating, setCreating] =
|
|
2760
|
-
const [saving, setSaving] =
|
|
2761
|
-
const memberCountByProfile =
|
|
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:
|
|
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 =
|
|
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 ?
|
|
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(
|
|
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
|
-
|
|
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:
|
|
7411
|
-
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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] =
|
|
7869
|
+
const [pathname, setPathname] = React41.useState(
|
|
7740
7870
|
typeof window !== "undefined" ? window.location.hash.slice(1) || "/" : "/"
|
|
7741
7871
|
);
|
|
7742
|
-
|
|
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 =
|
|
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] =
|
|
8035
|
-
|
|
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
|
-
|
|
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] =
|
|
9666
|
-
const [newOrgName, setNewOrgName] =
|
|
9667
|
-
const [creating, setCreating] =
|
|
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] =
|
|
9854
|
-
const [error, setError] =
|
|
9855
|
-
const [companyName, setCompanyName] =
|
|
9856
|
-
const [timezone, setTimezone] =
|
|
9857
|
-
const [currency, setCurrency] =
|
|
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 =
|
|
12192
|
-
const commands =
|
|
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 =
|
|
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
|
|
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
|
|
12367
|
+
return React41__default.createElement(
|
|
12238
12368
|
"div",
|
|
12239
12369
|
{ className: "flex items-center gap-3" },
|
|
12240
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
12292
|
-
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
|
|
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
|
-
|
|
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
|
|
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 = () =>
|
|
12558
|
+
const NotFoundPage = () => React41__default.createElement(
|
|
12417
12559
|
"div",
|
|
12418
12560
|
{ className: "flex flex-col items-center justify-center py-24 text-center" },
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
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 ?
|
|
12584
|
+
const renderedPage = matchedPath === "/settings" && config.showSettings !== false ? React41__default.createElement(SettingsPage, {
|
|
12443
12585
|
extraTabs: settingsTabs,
|
|
12444
|
-
beforeContent:
|
|
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:
|
|
12591
|
+
afterContent: React41__default.createElement(WidgetSlot, {
|
|
12450
12592
|
zone: "settings.after",
|
|
12451
12593
|
className: "space-y-4",
|
|
12452
12594
|
contextOverrides: { matchedPath }
|
|
12453
12595
|
})
|
|
12454
|
-
}) :
|
|
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 ?
|
|
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
|
-
|
|
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
|
-
|
|
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 ?
|
|
12617
|
+
const pageElement = pagePermission ? React41__default.createElement(
|
|
12476
12618
|
PermissionGate,
|
|
12477
12619
|
{
|
|
12478
12620
|
feature: pagePermission.feature,
|
|
12479
12621
|
action: pagePermission.action,
|
|
12480
|
-
fallback:
|
|
12622
|
+
fallback: React41__default.createElement(
|
|
12481
12623
|
"div",
|
|
12482
12624
|
{ className: "flex items-center justify-center py-24" },
|
|
12483
|
-
|
|
12625
|
+
React41__default.createElement(
|
|
12484
12626
|
"div",
|
|
12485
12627
|
{ className: "text-center" },
|
|
12486
|
-
|
|
12487
|
-
|
|
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 ?
|
|
12494
|
-
const content =
|
|
12635
|
+
const orgSwitcherElement = orgAdapter ? React41__default.createElement(OrgSwitcher) : void 0;
|
|
12636
|
+
const content = React41__default.createElement(
|
|
12495
12637
|
PluginRuntimeProvider,
|
|
12496
12638
|
{ value: pluginRuntime },
|
|
12497
|
-
|
|
12498
|
-
|
|
12639
|
+
React41__default.createElement(
|
|
12640
|
+
React41__default.Fragment,
|
|
12499
12641
|
null,
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 ?
|
|
12690
|
+
orgAdapter ? React41__default.createElement(OrgInitializer, { verticalId: config.verticalId }) : null,
|
|
12549
12691
|
// Location switch overlay
|
|
12550
|
-
|
|
12692
|
+
React41__default.createElement(LocationSwitchOverlay),
|
|
12551
12693
|
// Chat
|
|
12552
|
-
config.chat?.enabled !== false && config.chat ?
|
|
12553
|
-
|
|
12694
|
+
config.chat?.enabled !== false && config.chat ? React41__default.createElement(
|
|
12695
|
+
React41__default.Fragment,
|
|
12554
12696
|
null,
|
|
12555
|
-
|
|
12556
|
-
|
|
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
|
-
|
|
12700
|
+
React41__default.createElement(WidgetSlot, {
|
|
12559
12701
|
zone: "shell.floating",
|
|
12560
12702
|
contextOverrides: { matchedPath }
|
|
12561
12703
|
}),
|
|
12562
12704
|
// Command palette
|
|
12563
|
-
|
|
12705
|
+
React41__default.createElement(CommandPaletteWrapper, { navigation, routerAdapter })
|
|
12564
12706
|
)
|
|
12565
12707
|
);
|
|
12566
12708
|
if (requireAuth && authAdapter) {
|
|
12567
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
12736
|
+
React41__default.useEffect(() => {
|
|
12595
12737
|
if (config.billing?.plans) {
|
|
12596
12738
|
setPlans(config.billing.plans.map(normalizePlanConfig));
|
|
12597
12739
|
}
|
|
12598
12740
|
}, [setPlans]);
|
|
12599
|
-
|
|
12741
|
+
React41__default.useEffect(() => {
|
|
12600
12742
|
if (config.permissions?.features) {
|
|
12601
12743
|
setFeatures(config.permissions.features);
|
|
12602
12744
|
}
|
|
12603
12745
|
}, [setFeatures]);
|
|
12604
|
-
|
|
12746
|
+
React41__default.useEffect(() => {
|
|
12605
12747
|
setCurrentLocale(config.locale?.default ?? "en");
|
|
12606
12748
|
setDefaultCurrency(config.locale?.currency);
|
|
12607
12749
|
}, []);
|
|
12608
|
-
let inner =
|
|
12750
|
+
let inner = React41__default.createElement(
|
|
12609
12751
|
RouterProvider,
|
|
12610
12752
|
{ value: routerAdapter },
|
|
12611
|
-
|
|
12612
|
-
|
|
12753
|
+
React41__default.createElement(ToastProvider, null),
|
|
12754
|
+
React41__default.createElement(AppContent)
|
|
12613
12755
|
);
|
|
12614
12756
|
if (orgAdapter) {
|
|
12615
|
-
inner =
|
|
12757
|
+
inner = React41__default.createElement(
|
|
12616
12758
|
OrgAdapterProvider,
|
|
12617
12759
|
{ value: orgAdapter, children: inner }
|
|
12618
12760
|
);
|
|
12619
12761
|
}
|
|
12620
12762
|
if (authAdapter) {
|
|
12621
|
-
inner =
|
|
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 =
|
|
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 =
|
|
12882
|
+
const LegacyApp = React41.useMemo(() => {
|
|
12741
12883
|
if (!liveConfig || !hasLegacyShellConfig(liveConfig)) return null;
|
|
12742
12884
|
return createSaasApp(liveConfig);
|
|
12743
12885
|
}, [liveConfig]);
|
|
12744
|
-
const config =
|
|
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
|
|
13221
|
+
return React41__default.createElement(
|
|
13080
13222
|
"div",
|
|
13081
13223
|
{ className: "mx-auto max-w-4xl" },
|
|
13082
|
-
|
|
13083
|
-
description ?
|
|
13084
|
-
|
|
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
|
-
|
|
13229
|
+
React41__default.createElement(
|
|
13088
13230
|
"div",
|
|
13089
13231
|
{ className: "text-center" },
|
|
13090
|
-
|
|
13091
|
-
|
|
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 =
|
|
13239
|
-
const ProviderContext =
|
|
13240
|
-
const StoreContext =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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"),
|
|
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 (
|
|
13657
|
-
|
|
13658
|
-
|
|
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
|
-
|
|
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 === "
|
|
13718
|
-
|
|
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
|