@fayz-ai/saas 0.5.0 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/AdminShell.d.ts +14 -1
- package/dist/app/AdminShell.d.ts.map +1 -1
- package/dist/app/LoginPage.d.ts +2 -16
- package/dist/app/LoginPage.d.ts.map +1 -1
- package/dist/app/admin-app.d.ts +21 -0
- package/dist/app/admin-app.d.ts.map +1 -0
- package/dist/app/config.d.ts +40 -3
- package/dist/app/config.d.ts.map +1 -1
- package/dist/app/scaffold.d.ts +1 -2
- package/dist/app/scaffold.d.ts.map +1 -1
- package/dist/{chunk-DJW67TCY.js → chunk-DCRT47W4.js} +155 -28
- package/dist/chunk-DCRT47W4.js.map +1 -0
- package/dist/{chunk-QGMDVLLW.cjs → chunk-SRIUD35R.cjs} +155 -28
- package/dist/chunk-SRIUD35R.cjs.map +1 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -1
- package/dist/index.cjs +1127 -5792
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +839 -5509
- package/dist/index.js.map +1 -1
- package/dist/org/adapters/supabase.d.ts +18 -0
- package/dist/org/adapters/supabase.d.ts.map +1 -1
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/index.cjs +11 -10
- package/dist/org/index.js +2 -1
- package/dist/shell/components/crud/archetypes/ClientOrdersTab.d.ts +4 -2
- package/dist/shell/components/crud/archetypes/ClientOrdersTab.d.ts.map +1 -1
- package/dist/shell/components/layout/BottomNav.d.ts.map +1 -1
- package/dist/shell/components/layout/CommandPalette.d.ts.map +1 -1
- package/dist/shell/components/organization/InviteMemberDialog.d.ts.map +1 -1
- package/dist/shell/components/organization/PermissionMatrixEditor.d.ts +6 -1
- package/dist/shell/components/organization/PermissionMatrixEditor.d.ts.map +1 -1
- package/dist/shell/components/organization/PermissionProfilesTab.d.ts.map +1 -1
- package/dist/shell/components/organization/TeamTab.d.ts.map +1 -1
- package/dist/shell/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
- package/dist/shell/components/settings/ConnectedUserProfile.d.ts +2 -1
- package/dist/shell/components/settings/ConnectedUserProfile.d.ts.map +1 -1
- package/dist/shell/components/settings/SettingsPage.d.ts +8 -1
- package/dist/shell/components/settings/SettingsPage.d.ts.map +1 -1
- package/dist/shell/config/theme/tokens.d.ts +12 -1
- package/dist/shell/config/theme/tokens.d.ts.map +1 -1
- package/dist/shell/config/theme/utils.d.ts +8 -0
- package/dist/shell/config/theme/utils.d.ts.map +1 -1
- package/dist/shell/lib/i18n.d.ts.map +1 -1
- package/dist/shell/types/client-orders.d.ts +4 -0
- package/dist/shell/types/client-orders.d.ts.map +1 -1
- package/dist/shell/types/crud.d.ts +4 -0
- package/dist/shell/types/crud.d.ts.map +1 -1
- package/package.json +7 -9
- package/src/app/AdminShell.tsx +103 -35
- package/src/app/LoginPage.tsx +2 -264
- package/src/app/{createFayzApp.tsx → admin-app.tsx} +98 -40
- package/src/app/config.ts +44 -2
- package/src/app/scaffold.tsx +13 -26
- package/src/crud/CrudDetailPage.tsx +43 -9
- package/src/crud/CrudPage.tsx +1 -1
- package/src/index.ts +12 -5
- package/src/org/adapters/supabase.ts +176 -28
- package/src/org/context.tsx +33 -2
- package/src/shell/components/crud/archetypes/ClientOrdersTab.tsx +12 -4
- package/src/shell/components/layout/BottomNav.tsx +39 -4
- package/src/shell/components/layout/CommandPalette.tsx +14 -10
- package/src/shell/components/organization/InviteMemberDialog.tsx +14 -3
- package/src/shell/components/organization/PermissionMatrixEditor.tsx +48 -10
- package/src/shell/components/organization/PermissionProfilesTab.tsx +65 -23
- package/src/shell/components/organization/TeamTab.tsx +36 -9
- package/src/shell/components/plugins/PluginSettingsPanel.tsx +5 -3
- package/src/shell/components/settings/ConnectedUserProfile.tsx +68 -25
- package/src/shell/components/settings/SettingsPage.tsx +12 -3
- package/src/shell/config/theme/tokens.ts +13 -1
- package/src/shell/config/theme/utils.ts +76 -15
- package/src/shell/lib/i18n.ts +4 -0
- package/src/shell/types/client-orders.ts +5 -0
- package/src/shell/types/crud.ts +4 -0
- package/dist/app/createFayzApp.d.ts +0 -11
- package/dist/app/createFayzApp.d.ts.map +0 -1
- package/dist/chunk-DJW67TCY.js.map +0 -1
- package/dist/chunk-QGMDVLLW.cjs.map +0 -1
- package/dist/shell/components/organization/ImpersonationBanner.d.ts +0 -3
- package/dist/shell/components/organization/ImpersonationBanner.d.ts.map +0 -1
- package/dist/shell/components/settings/ConnectedHolidaysSettings.d.ts +0 -3
- package/dist/shell/components/settings/ConnectedHolidaysSettings.d.ts.map +0 -1
- package/dist/shell/components/settings/ConnectedLocationsOverview.d.ts +0 -3
- package/dist/shell/components/settings/ConnectedLocationsOverview.d.ts.map +0 -1
- package/dist/shell/createSaasApp.d.ts +0 -145
- package/dist/shell/createSaasApp.d.ts.map +0 -1
- package/src/shell/components/organization/ImpersonationBanner.tsx +0 -29
- package/src/shell/components/settings/ConnectedHolidaysSettings.tsx +0 -99
- package/src/shell/components/settings/ConnectedLocationsOverview.tsx +0 -57
- package/src/shell/createSaasApp.tsx +0 -943
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { type RouterAdapter } from './lib/router';
|
|
3
|
-
import type { AuthAdapter } from './types/auth-adapter';
|
|
4
|
-
import type { OrgAdapter } from './types/org-adapter';
|
|
5
|
-
import type { PlanConfig } from './types/billing';
|
|
6
|
-
import type { PluginManifest, TenantPluginBinding } from '@fayz-ai/core';
|
|
7
|
-
import type { CreateThemeOptions } from './config/theme/utils';
|
|
8
|
-
import type { SaasTheme } from './config/theme/tokens';
|
|
9
|
-
import type { AuthProvider } from './types/auth';
|
|
10
|
-
import type { PermissionsConfig, PermissionAction } from './types/permissions';
|
|
11
|
-
export interface PageConfig {
|
|
12
|
-
/** URL path, e.g. '/' or '/clients' */
|
|
13
|
-
path: string;
|
|
14
|
-
/** Sidebar/nav label */
|
|
15
|
-
label: string;
|
|
16
|
-
/** Lucide icon name */
|
|
17
|
-
icon: string;
|
|
18
|
-
/** The page component to render */
|
|
19
|
-
component: React.ComponentType;
|
|
20
|
-
/** Navigation section (default: 'main') */
|
|
21
|
-
section?: 'main' | 'secondary' | 'settings';
|
|
22
|
-
/** Sort order within section (auto-incremented if omitted) */
|
|
23
|
-
position?: number;
|
|
24
|
-
/** Optional badge */
|
|
25
|
-
badge?: string | number;
|
|
26
|
-
/** Permission required to access this page */
|
|
27
|
-
permission?: {
|
|
28
|
-
feature: string;
|
|
29
|
-
action: PermissionAction;
|
|
30
|
-
};
|
|
31
|
-
/** Child pages — renders as a dropdown in topbar, collapsible in sidebar.
|
|
32
|
-
* Children can omit `component` to be navigation-only links (pointing to routes the parent handles). */
|
|
33
|
-
children?: Array<Omit<PageConfig, 'component'> & {
|
|
34
|
-
component?: React.ComponentType;
|
|
35
|
-
}>;
|
|
36
|
-
}
|
|
37
|
-
export interface SaasAppConfig {
|
|
38
|
-
/** App display name (used in sidebar header) */
|
|
39
|
-
name: string;
|
|
40
|
-
/** Logo — string renders as initial badge, ReactNode renders as-is */
|
|
41
|
-
logo?: string | React.ReactNode;
|
|
42
|
-
/** Supabase project URL — one project per SaaS (core in saas_core schema, data in public) */
|
|
43
|
-
supabaseUrl?: string;
|
|
44
|
-
/** Supabase anon key */
|
|
45
|
-
supabaseAnonKey?: string;
|
|
46
|
-
/** Theme overrides — use SaasTheme (friendly) or CreateThemeOptions (granular) */
|
|
47
|
-
theme?: CreateThemeOptions | SaasTheme;
|
|
48
|
-
/** Default theme mode: 'light' or 'dark'. User can toggle. Default: 'light' */
|
|
49
|
-
defaultThemeMode?: 'light' | 'dark';
|
|
50
|
-
/** Layout variant (default: 'sidebar') */
|
|
51
|
-
layout?: 'sidebar' | 'topbar' | 'minimal';
|
|
52
|
-
/** Content floats in a rounded frame over the sidebar background (default: true). Set false to disable. */
|
|
53
|
-
sidebarFrame?: boolean;
|
|
54
|
-
/** Vertical-specific pages */
|
|
55
|
-
pages: PageConfig[];
|
|
56
|
-
/** Plugins to register */
|
|
57
|
-
plugins?: PluginManifest[];
|
|
58
|
-
/** Runtime plugin activation for tenant-aware apps */
|
|
59
|
-
pluginRuntime?: {
|
|
60
|
-
/** Opt into loading tenant plugin bindings from the backend RPC. Static apps should leave this off. */
|
|
61
|
-
hydrateTenantPlugins?: boolean;
|
|
62
|
-
tenantPlugins?: TenantPluginBinding[];
|
|
63
|
-
resolveTenantPlugins?: (context: {
|
|
64
|
-
tenant: {
|
|
65
|
-
id: string;
|
|
66
|
-
slug: string;
|
|
67
|
-
verticalId?: string;
|
|
68
|
-
plan?: string;
|
|
69
|
-
} | null;
|
|
70
|
-
user: {
|
|
71
|
-
id: string;
|
|
72
|
-
} | null;
|
|
73
|
-
}) => TenantPluginBinding[] | undefined;
|
|
74
|
-
};
|
|
75
|
-
/** Router adapter (default: hashRouterAdapter) */
|
|
76
|
-
router?: RouterAdapter;
|
|
77
|
-
/** Billing configuration */
|
|
78
|
-
billing?: {
|
|
79
|
-
plans: PlanConfig[];
|
|
80
|
-
stripePublishableKey?: string;
|
|
81
|
-
};
|
|
82
|
-
/** AI chat configuration */
|
|
83
|
-
chat?: {
|
|
84
|
-
enabled?: boolean;
|
|
85
|
-
systemPrompt?: string;
|
|
86
|
-
apiEndpoint?: string;
|
|
87
|
-
title?: string;
|
|
88
|
-
};
|
|
89
|
-
/** Notification configuration */
|
|
90
|
-
notifications?: {
|
|
91
|
-
changelogUrl?: string;
|
|
92
|
-
};
|
|
93
|
-
/** Locale / i18n configuration */
|
|
94
|
-
locale?: {
|
|
95
|
-
default?: string;
|
|
96
|
-
supported?: string[];
|
|
97
|
-
translations?: Record<string, Record<string, string>>;
|
|
98
|
-
/** ISO 4217 currency code for all currency fields (e.g. 'BRL'). Inferred from locale when omitted. */
|
|
99
|
-
currency?: string;
|
|
100
|
-
};
|
|
101
|
-
/** Mock user for demo/dev (will be replaced by auth in production) */
|
|
102
|
-
user?: {
|
|
103
|
-
fullName: string;
|
|
104
|
-
email: string;
|
|
105
|
-
avatarUrl?: string;
|
|
106
|
-
};
|
|
107
|
-
/** Auth configuration */
|
|
108
|
-
auth?: {
|
|
109
|
-
adapter?: AuthAdapter | 'mock' | 'supabase';
|
|
110
|
-
requireAuth?: boolean;
|
|
111
|
-
loginLogo?: React.ReactNode;
|
|
112
|
-
loginLayout?: 'split' | 'centered';
|
|
113
|
-
loginTagline?: string;
|
|
114
|
-
loginDescription?: string;
|
|
115
|
-
showOAuth?: boolean;
|
|
116
|
-
oauthProviders?: Exclude<AuthProvider, 'email'>[];
|
|
117
|
-
};
|
|
118
|
-
/** Organization / multi-tenant configuration */
|
|
119
|
-
organization?: {
|
|
120
|
-
adapter?: OrgAdapter | 'mock' | 'supabase';
|
|
121
|
-
multiOrg?: boolean;
|
|
122
|
-
};
|
|
123
|
-
/** Vertical/niche ID — auto-set on tenant creation, skips niche selection in onboarding */
|
|
124
|
-
verticalId?: string;
|
|
125
|
-
/** Permission profiles configuration */
|
|
126
|
-
permissions?: PermissionsConfig;
|
|
127
|
-
/** Additional settings tabs to merge with built-in ones */
|
|
128
|
-
settingsTabs?: {
|
|
129
|
-
id: string;
|
|
130
|
-
label: string;
|
|
131
|
-
component: React.ReactNode;
|
|
132
|
-
}[];
|
|
133
|
-
/** Set to false to hide built-in Settings page (default: true) */
|
|
134
|
-
showSettings?: boolean;
|
|
135
|
-
/** Set to false to hide built-in Billing page (default: true when plans provided) */
|
|
136
|
-
showBilling?: boolean;
|
|
137
|
-
/** Custom mobile bottom nav items (max 5). If omitted, auto-picks first main items. */
|
|
138
|
-
bottomNav?: Array<{
|
|
139
|
-
label: string;
|
|
140
|
-
icon: string;
|
|
141
|
-
route: string;
|
|
142
|
-
}>;
|
|
143
|
-
}
|
|
144
|
-
export declare function createSaasApp(config: SaasAppConfig): React.FC;
|
|
145
|
-
//# sourceMappingURL=createSaasApp.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createSaasApp.d.ts","sourceRoot":"","sources":["../../src/shell/createSaasApp.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
import { Eye, X } from 'lucide-react'
|
|
3
|
-
import { usePermissionsStore } from '../../stores/permissions.store'
|
|
4
|
-
import { useTranslation } from '../../hooks/useTranslation'
|
|
5
|
-
|
|
6
|
-
export function ImpersonationBanner() {
|
|
7
|
-
const isImpersonating = usePermissionsStore((s) => s.isImpersonating)
|
|
8
|
-
const impersonatedProfile = usePermissionsStore((s) => s.impersonatedProfile)
|
|
9
|
-
const stopImpersonation = usePermissionsStore((s) => s.stopImpersonation)
|
|
10
|
-
const { t } = useTranslation()
|
|
11
|
-
|
|
12
|
-
if (!isImpersonating || !impersonatedProfile) return null
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div className="fixed top-0 left-0 right-0 z-[90] flex items-center justify-center gap-3 bg-warning px-4 py-1.5 text-sm font-medium text-amber-950 shadow-sm">
|
|
16
|
-
<Eye className="h-3.5 w-3.5" />
|
|
17
|
-
<span>
|
|
18
|
-
{t('organization.permissions.previewingAs')}: <strong>{impersonatedProfile.name}</strong>
|
|
19
|
-
</span>
|
|
20
|
-
<button
|
|
21
|
-
onClick={stopImpersonation}
|
|
22
|
-
className="ml-2 inline-flex items-center gap-1 rounded-md bg-warning/30 px-2 py-0.5 text-xs font-semibold hover:bg-warning/50 transition-colors"
|
|
23
|
-
>
|
|
24
|
-
<X className="h-3 w-3" />
|
|
25
|
-
{t('organization.permissions.exitPreview')}
|
|
26
|
-
</button>
|
|
27
|
-
</div>
|
|
28
|
-
)
|
|
29
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
import { HolidaysSettings, type Holiday } from './HolidaysSettings'
|
|
3
|
-
import { usePermission } from '../../hooks/usePermission'
|
|
4
|
-
import { useOrganizationStore } from '../../stores/organization.store'
|
|
5
|
-
import { toast } from '../notifications/ToastProvider'
|
|
6
|
-
import { getSupabaseClientOptional } from '../../lib/supabase'
|
|
7
|
-
|
|
8
|
-
const TABLE = 'holidays'
|
|
9
|
-
const SCHEMA = 'saas_core'
|
|
10
|
-
|
|
11
|
-
export function ConnectedHolidaysSettings() {
|
|
12
|
-
const { hasSystemPermission } = usePermission()
|
|
13
|
-
const currentOrg = useOrganizationStore((s) => s.currentOrg)
|
|
14
|
-
const [holidays, setHolidays] = React.useState<Holiday[]>([])
|
|
15
|
-
const [loading, setLoading] = React.useState(true)
|
|
16
|
-
|
|
17
|
-
const canManage = true // User is already in settings — allow managing
|
|
18
|
-
const supabase = getSupabaseClientOptional()
|
|
19
|
-
|
|
20
|
-
const fetchHolidays = React.useCallback(async () => {
|
|
21
|
-
if (!supabase || !currentOrg) return
|
|
22
|
-
try {
|
|
23
|
-
const { data, error } = await supabase
|
|
24
|
-
.schema(SCHEMA)
|
|
25
|
-
.from(TABLE)
|
|
26
|
-
.select('*')
|
|
27
|
-
.eq('tenant_id', currentOrg.id)
|
|
28
|
-
.eq('is_active', true)
|
|
29
|
-
.order('date', { ascending: true })
|
|
30
|
-
if (error) throw error
|
|
31
|
-
setHolidays(
|
|
32
|
-
(data ?? []).map((row: any) => ({
|
|
33
|
-
id: row.id,
|
|
34
|
-
name: row.name,
|
|
35
|
-
date: row.date,
|
|
36
|
-
recurring: row.recurring ?? false,
|
|
37
|
-
description: row.description,
|
|
38
|
-
isActive: row.is_active,
|
|
39
|
-
}))
|
|
40
|
-
)
|
|
41
|
-
} catch {
|
|
42
|
-
// If table doesn't exist yet, show empty state
|
|
43
|
-
setHolidays([])
|
|
44
|
-
}
|
|
45
|
-
}, [supabase, currentOrg?.id])
|
|
46
|
-
|
|
47
|
-
React.useEffect(() => {
|
|
48
|
-
fetchHolidays().finally(() => setLoading(false))
|
|
49
|
-
}, [fetchHolidays])
|
|
50
|
-
|
|
51
|
-
const handleAdd = async (data: Omit<Holiday, 'id' | 'isActive'> & { isActive?: boolean }) => {
|
|
52
|
-
if (!supabase || !currentOrg) return
|
|
53
|
-
try {
|
|
54
|
-
const { error } = await supabase
|
|
55
|
-
.schema(SCHEMA)
|
|
56
|
-
.from(TABLE)
|
|
57
|
-
.insert({
|
|
58
|
-
tenant_id: currentOrg.id,
|
|
59
|
-
name: data.name,
|
|
60
|
-
date: data.date,
|
|
61
|
-
recurring: data.recurring,
|
|
62
|
-
description: data.description ?? null,
|
|
63
|
-
is_active: true,
|
|
64
|
-
})
|
|
65
|
-
if (error) throw error
|
|
66
|
-
await fetchHolidays()
|
|
67
|
-
toast.success('Holiday added')
|
|
68
|
-
} catch (err: any) {
|
|
69
|
-
toast.error('Failed to add holiday', { description: err?.message })
|
|
70
|
-
throw err
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const handleRemove = async (id: string) => {
|
|
75
|
-
if (!supabase) return
|
|
76
|
-
try {
|
|
77
|
-
const { error } = await supabase
|
|
78
|
-
.schema(SCHEMA)
|
|
79
|
-
.from(TABLE)
|
|
80
|
-
.update({ is_active: false })
|
|
81
|
-
.eq('id', id)
|
|
82
|
-
if (error) throw error
|
|
83
|
-
await fetchHolidays()
|
|
84
|
-
toast.success('Holiday removed')
|
|
85
|
-
} catch (err: any) {
|
|
86
|
-
toast.error('Failed to remove holiday', { description: err?.message })
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<HolidaysSettings
|
|
92
|
-
holidays={holidays}
|
|
93
|
-
loading={loading}
|
|
94
|
-
canManage={canManage}
|
|
95
|
-
onAdd={handleAdd}
|
|
96
|
-
onRemove={handleRemove}
|
|
97
|
-
/>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
import { LocationsOverview } from './LocationsOverview'
|
|
3
|
-
import { useOrgAdapterOptional } from '../../lib/org-context'
|
|
4
|
-
import { useOrganizationStore } from '../../stores/organization.store'
|
|
5
|
-
import { usePermission } from '../../hooks/usePermission'
|
|
6
|
-
import { toast } from '../notifications/ToastProvider'
|
|
7
|
-
import { dedup } from '../../lib/dedup'
|
|
8
|
-
import type { Location } from '../../types/tenant'
|
|
9
|
-
|
|
10
|
-
export function ConnectedLocationsOverview() {
|
|
11
|
-
const adapter = useOrgAdapterOptional()
|
|
12
|
-
const currentOrg = useOrganizationStore((s) => s.currentOrg)
|
|
13
|
-
const { hasSystemPermission } = usePermission()
|
|
14
|
-
const [locations, setLocations] = React.useState<Location[]>([])
|
|
15
|
-
const [loading, setLoading] = React.useState(true)
|
|
16
|
-
|
|
17
|
-
const canManage = hasSystemPermission('manage_settings')
|
|
18
|
-
|
|
19
|
-
const fetchLocations = React.useCallback(async () => {
|
|
20
|
-
if (!adapter || !currentOrg) return
|
|
21
|
-
try {
|
|
22
|
-
const data = await dedup('locations:' + currentOrg.id, () => adapter.listLocations(currentOrg.id))
|
|
23
|
-
setLocations(data)
|
|
24
|
-
} catch {
|
|
25
|
-
// ignore
|
|
26
|
-
}
|
|
27
|
-
}, [adapter, currentOrg?.id])
|
|
28
|
-
|
|
29
|
-
React.useEffect(() => {
|
|
30
|
-
if (!adapter || !currentOrg) {
|
|
31
|
-
setLoading(false)
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
fetchLocations().finally(() => setLoading(false))
|
|
35
|
-
}, [fetchLocations])
|
|
36
|
-
|
|
37
|
-
const handleAdd = async (data: { name: string; address?: string; city?: string; state?: string; country?: string; phone?: string; isHeadquarters?: boolean }) => {
|
|
38
|
-
if (!adapter || !currentOrg) return
|
|
39
|
-
try {
|
|
40
|
-
await adapter.createLocation(currentOrg.id, data)
|
|
41
|
-
await fetchLocations()
|
|
42
|
-
toast.success('Location added')
|
|
43
|
-
} catch (err: any) {
|
|
44
|
-
toast.error('Failed to add location', { description: err?.message })
|
|
45
|
-
throw err
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<LocationsOverview
|
|
51
|
-
locations={locations}
|
|
52
|
-
loading={loading}
|
|
53
|
-
canManage={canManage}
|
|
54
|
-
onAdd={handleAdd}
|
|
55
|
-
/>
|
|
56
|
-
)
|
|
57
|
-
}
|