@ankhorage/studio 0.6.10 → 0.7.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/README.md +1 -1
- package/dist/authSettings.d.ts +1 -0
- package/dist/authSettings.d.ts.map +1 -1
- package/dist/authSettings.js.map +1 -1
- package/dist/core/StudioProvider.d.ts +5 -2
- package/dist/core/StudioProvider.d.ts.map +1 -1
- package/dist/core/StudioProvider.js +212 -16
- package/dist/core/StudioProvider.js.map +1 -1
- package/dist/core/studioManifestDraftModel.d.ts +13 -0
- package/dist/core/studioManifestDraftModel.d.ts.map +1 -0
- package/dist/core/studioManifestDraftModel.js +34 -0
- package/dist/core/studioManifestDraftModel.js.map +1 -0
- package/dist/core/studioManifestPersistenceModel.d.ts +21 -0
- package/dist/core/studioManifestPersistenceModel.d.ts.map +1 -0
- package/dist/core/studioManifestPersistenceModel.js +46 -0
- package/dist/core/studioManifestPersistenceModel.js.map +1 -0
- package/dist/host/http/authRoutes.d.ts.map +1 -1
- package/dist/host/http/authRoutes.js +0 -31
- package/dist/host/http/authRoutes.js.map +1 -1
- package/dist/host/http/secretRoutes.d.ts.map +1 -1
- package/dist/host/http/secretRoutes.js +1 -24
- package/dist/host/http/secretRoutes.js.map +1 -1
- package/dist/host/index.d.ts +1 -2
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +1 -2
- package/dist/host/index.js.map +1 -1
- package/dist/host/layout/layoutGenerator.d.ts.map +1 -1
- package/dist/host/layout/layoutGenerator.js +55 -37
- package/dist/host/layout/layoutGenerator.js.map +1 -1
- package/dist/host/layout/templates/rootLayout.d.ts.map +1 -1
- package/dist/host/layout/templates/rootLayout.js +99 -15
- package/dist/host/layout/templates/rootLayout.js.map +1 -1
- package/dist/host/secrets/projectSecretService.d.ts +2 -12
- package/dist/host/secrets/projectSecretService.d.ts.map +1 -1
- package/dist/host/secrets/projectSecretService.js +1 -61
- package/dist/host/secrets/projectSecretService.js.map +1 -1
- package/dist/index.d.ts +13 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/projectAuthApi.d.ts +0 -7
- package/dist/projectAuthApi.d.ts.map +1 -1
- package/dist/projectAuthApi.js +0 -34
- package/dist/projectAuthApi.js.map +1 -1
- package/dist/projectSecretApi.d.ts +1 -8
- package/dist/projectSecretApi.d.ts.map +1 -1
- package/dist/projectSecretApi.js +1 -8
- package/dist/projectSecretApi.js.map +1 -1
- package/dist/root.d.ts +2 -0
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +2 -0
- package/dist/root.js.map +1 -1
- package/dist/studioAdminRouteModel.d.ts +43 -9
- package/dist/studioAdminRouteModel.d.ts.map +1 -1
- package/dist/studioAdminRouteModel.js +176 -11
- package/dist/studioAdminRouteModel.js.map +1 -1
- package/dist/ui/admin/AnkhAdminPage.d.ts +7 -0
- package/dist/ui/admin/AnkhAdminPage.d.ts.map +1 -0
- package/dist/ui/admin/AnkhAdminPage.js +31 -0
- package/dist/ui/admin/AnkhAdminPage.js.map +1 -0
- package/dist/ui/admin/AnkhAdminShell.d.ts +6 -0
- package/dist/ui/admin/AnkhAdminShell.d.ts.map +1 -0
- package/dist/ui/admin/AnkhAdminShell.js +87 -0
- package/dist/ui/admin/AnkhAdminShell.js.map +1 -0
- package/dist/ui/admin/AuthAdminSession.d.ts +25 -0
- package/dist/ui/admin/AuthAdminSession.d.ts.map +1 -0
- package/dist/ui/admin/AuthAdminSession.js +96 -0
- package/dist/ui/admin/AuthAdminSession.js.map +1 -0
- package/dist/ui/admin/adminPagePrimitives.d.ts +34 -0
- package/dist/ui/admin/adminPagePrimitives.d.ts.map +1 -0
- package/dist/ui/admin/adminPagePrimitives.js +69 -0
- package/dist/ui/admin/adminPagePrimitives.js.map +1 -0
- package/dist/ui/admin/adminPageUtils.d.ts +4 -0
- package/dist/ui/admin/adminPageUtils.d.ts.map +1 -0
- package/dist/ui/admin/adminPageUtils.js +14 -0
- package/dist/ui/admin/adminPageUtils.js.map +1 -0
- package/dist/ui/admin/pages/ApisAdminPage.d.ts +7 -0
- package/dist/ui/admin/pages/ApisAdminPage.d.ts.map +1 -0
- package/dist/ui/admin/pages/ApisAdminPage.js +21 -0
- package/dist/ui/admin/pages/ApisAdminPage.js.map +1 -0
- package/dist/ui/admin/pages/AuthAdminPage.d.ts +10 -0
- package/dist/ui/admin/pages/AuthAdminPage.d.ts.map +1 -0
- package/dist/ui/admin/pages/AuthAdminPage.js +596 -0
- package/dist/ui/admin/pages/AuthAdminPage.js.map +1 -0
- package/dist/ui/admin/pages/OverviewAdminPage.d.ts +3 -0
- package/dist/ui/admin/pages/OverviewAdminPage.d.ts.map +1 -0
- package/dist/ui/admin/pages/OverviewAdminPage.js +16 -0
- package/dist/ui/admin/pages/OverviewAdminPage.js.map +1 -0
- package/dist/ui/admin/pages/PropertiesAdminPage.d.ts +5 -0
- package/dist/ui/admin/pages/PropertiesAdminPage.d.ts.map +1 -0
- package/dist/ui/admin/pages/PropertiesAdminPage.js +28 -0
- package/dist/ui/admin/pages/PropertiesAdminPage.js.map +1 -0
- package/dist/ui/admin/pages/SecretsAdminPage.d.ts +5 -0
- package/dist/ui/admin/pages/SecretsAdminPage.d.ts.map +1 -0
- package/dist/ui/{StudioAdminOverlay.js → admin/pages/SecretsAdminPage.js} +57 -44
- package/dist/ui/{StudioAdminOverlay.js.map → admin/pages/SecretsAdminPage.js.map} +1 -1
- package/dist/ui/admin/pages/ThemeAdminPage.d.ts +3 -0
- package/dist/ui/admin/pages/ThemeAdminPage.d.ts.map +1 -0
- package/dist/ui/admin/pages/ThemeAdminPage.js +51 -0
- package/dist/ui/admin/pages/ThemeAdminPage.js.map +1 -0
- package/dist/ui/admin/pages/adminAuthCredentialFlow.d.ts +32 -0
- package/dist/ui/admin/pages/adminAuthCredentialFlow.d.ts.map +1 -0
- package/dist/ui/admin/pages/adminAuthCredentialFlow.js +168 -0
- package/dist/ui/admin/pages/adminAuthCredentialFlow.js.map +1 -0
- package/dist/ui/admin/pages/adminAuthHealthFlow.d.ts +18 -0
- package/dist/ui/admin/pages/adminAuthHealthFlow.d.ts.map +1 -0
- package/dist/ui/admin/pages/adminAuthHealthFlow.js +24 -0
- package/dist/ui/admin/pages/adminAuthHealthFlow.js.map +1 -0
- package/dist/ui/admin/pages/adminAuthSessionModel.d.ts +67 -0
- package/dist/ui/admin/pages/adminAuthSessionModel.d.ts.map +1 -0
- package/dist/ui/admin/pages/adminAuthSessionModel.js +176 -0
- package/dist/ui/admin/pages/adminAuthSessionModel.js.map +1 -0
- package/dist/ui/admin/pages/adminDataSourceOperations.d.ts +10 -0
- package/dist/ui/admin/pages/adminDataSourceOperations.d.ts.map +1 -0
- package/dist/ui/admin/pages/adminDataSourceOperations.js +11 -0
- package/dist/ui/admin/pages/adminDataSourceOperations.js.map +1 -0
- package/dist/ui/admin/pages/adminThemeHarmony.d.ts +4 -0
- package/dist/ui/admin/pages/adminThemeHarmony.d.ts.map +1 -0
- package/dist/ui/admin/pages/adminThemeHarmony.js +12 -0
- package/dist/ui/admin/pages/adminThemeHarmony.js.map +1 -0
- package/dist/ui/admin/pages/adminThemeModel.d.ts +22 -0
- package/dist/ui/admin/pages/adminThemeModel.d.ts.map +1 -0
- package/dist/ui/admin/pages/adminThemeModel.js +22 -0
- package/dist/ui/admin/pages/adminThemeModel.js.map +1 -0
- package/dist/ui/useStudioAppBarAugmentation.d.ts +0 -1
- package/dist/ui/useStudioAppBarAugmentation.d.ts.map +1 -1
- package/dist/ui/useStudioAppBarAugmentation.js +20 -40
- package/dist/ui/useStudioAppBarAugmentation.js.map +1 -1
- package/package.json +1 -1
- package/dist/host/auth/projectAuthService.d.ts +0 -23
- package/dist/host/auth/projectAuthService.d.ts.map +0 -1
- package/dist/host/auth/projectAuthService.js +0 -63
- package/dist/host/auth/projectAuthService.js.map +0 -1
- package/dist/ui/StudioAdminOverlay.d.ts +0 -7
- package/dist/ui/StudioAdminOverlay.d.ts.map +0 -1
- package/dist/ui/StudioAuthSettingsOverlay.d.ts +0 -9
- package/dist/ui/StudioAuthSettingsOverlay.d.ts.map +0 -1
- package/dist/ui/StudioAuthSettingsOverlay.js +0 -607
- package/dist/ui/StudioAuthSettingsOverlay.js.map +0 -1
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { DEFAULT_AUTH_FLOW, } from '@ankhorage/contracts';
|
|
3
|
+
import { getSupabaseOAuthProviderDefinition, SUPABASE_OAUTH_PROVIDER_IDS, } from '@ankhorage/supabase-auth';
|
|
4
|
+
import { Heading, Text, useZoraTheme } from '@ankhorage/zora';
|
|
5
|
+
import { useRouter } from 'expo-router';
|
|
6
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
7
|
+
import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Switch, TextInput, View, } from 'react-native';
|
|
8
|
+
import { readStudioAuthSettings } from '../../../authSettings';
|
|
9
|
+
import { useStudio } from '../../../core/StudioContext';
|
|
10
|
+
import { getProjectAuthHealth, ProjectAuthApiError } from '../../../projectAuthApi';
|
|
11
|
+
import { configureProjectOAuthProvider } from '../../../projectSecretApi';
|
|
12
|
+
import { useAuthAdminSession } from '../AuthAdminSession';
|
|
13
|
+
import { AuthHealthRefreshCoordinator } from './adminAuthHealthFlow';
|
|
14
|
+
import { persistStoredOAuthCredentialLinkAndPatchLocalDraft, persistStoredOAuthCredentialLink, } from './adminAuthCredentialFlow';
|
|
15
|
+
import { rebaseAuthDraftOntoCanonicalCredentialRefs, } from './adminAuthSessionModel';
|
|
16
|
+
const SIGN_IN_IDENTIFIERS = ['email', 'phone', 'username'];
|
|
17
|
+
const PROFILE_FIELDS = [
|
|
18
|
+
'email',
|
|
19
|
+
'phone',
|
|
20
|
+
'username',
|
|
21
|
+
'firstName',
|
|
22
|
+
'lastName',
|
|
23
|
+
'displayName',
|
|
24
|
+
'avatarUrl',
|
|
25
|
+
];
|
|
26
|
+
export function AuthAdminPage(props) {
|
|
27
|
+
const { projectId, manifest, routeId } = props;
|
|
28
|
+
const studio = useStudio();
|
|
29
|
+
const authAdminSession = useAuthAdminSession();
|
|
30
|
+
const { flushManifest, manifest: studioManifest, mutateAuthSettings, updateAuthSettings, } = studio;
|
|
31
|
+
const router = useRouter();
|
|
32
|
+
const [draft, setDraft] = useState(() => readStudioAuthSettings(manifest ?? createFallbackManifest()) ?? createDefaultSettings());
|
|
33
|
+
const [health, setHealth] = useState(null);
|
|
34
|
+
const [loading, setLoading] = useState(true);
|
|
35
|
+
const [saving, setSaving] = useState(false);
|
|
36
|
+
const [message, setMessage] = useState(null);
|
|
37
|
+
const canonicalManifestRef = useRef(manifest);
|
|
38
|
+
const initializedDraftFromManifestRef = useRef(manifest !== null);
|
|
39
|
+
const healthRefreshCoordinatorRef = useRef(new AuthHealthRefreshCoordinator());
|
|
40
|
+
canonicalManifestRef.current = studioManifest ?? manifest;
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (initializedDraftFromManifestRef.current || !manifest)
|
|
43
|
+
return;
|
|
44
|
+
initializedDraftFromManifestRef.current = true;
|
|
45
|
+
setDraft(readStudioAuthSettings(manifest) ?? createDefaultSettings());
|
|
46
|
+
}, [manifest]);
|
|
47
|
+
const refreshHealth = useCallback(async () => {
|
|
48
|
+
await healthRefreshCoordinatorRef.current.refresh({
|
|
49
|
+
loadHealth: () => getProjectAuthHealth({ projectId, environment: 'local' }),
|
|
50
|
+
onHealth: setHealth,
|
|
51
|
+
onError: (error) => setMessage(toMessage(error)),
|
|
52
|
+
});
|
|
53
|
+
}, [projectId]);
|
|
54
|
+
const reload = useCallback(async () => {
|
|
55
|
+
setLoading(true);
|
|
56
|
+
const result = await healthRefreshCoordinatorRef.current.refresh({
|
|
57
|
+
loadHealth: () => getProjectAuthHealth({ projectId, environment: 'local' }),
|
|
58
|
+
onHealth: (loadedHealth) => {
|
|
59
|
+
const canonicalAuthSettings = canonicalManifestRef.current
|
|
60
|
+
? readStudioAuthSettings(canonicalManifestRef.current)
|
|
61
|
+
: null;
|
|
62
|
+
setDraft(canonicalAuthSettings ?? createDefaultSettings());
|
|
63
|
+
setHealth(loadedHealth);
|
|
64
|
+
setMessage(null);
|
|
65
|
+
},
|
|
66
|
+
onError: (error) => {
|
|
67
|
+
const canonicalAuthSettings = canonicalManifestRef.current
|
|
68
|
+
? readStudioAuthSettings(canonicalManifestRef.current)
|
|
69
|
+
: null;
|
|
70
|
+
if (canonicalAuthSettings)
|
|
71
|
+
setDraft(canonicalAuthSettings);
|
|
72
|
+
setMessage(toMessage(error));
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
if (result.applied || result.error)
|
|
76
|
+
setLoading(false);
|
|
77
|
+
}, [projectId]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
void refreshHealth().finally(() => {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
});
|
|
82
|
+
}, [refreshHealth]);
|
|
83
|
+
const persistAuthDraft = useCallback(async (nextDraft, nextMessage) => {
|
|
84
|
+
const canonicalAuthSettings = canonicalManifestRef.current
|
|
85
|
+
? readStudioAuthSettings(canonicalManifestRef.current)
|
|
86
|
+
: null;
|
|
87
|
+
const rebasedDraft = rebaseAuthDraftOntoCanonicalCredentialRefs({
|
|
88
|
+
draft: nextDraft,
|
|
89
|
+
canonical: canonicalAuthSettings,
|
|
90
|
+
});
|
|
91
|
+
updateAuthSettings(rebasedDraft);
|
|
92
|
+
await flushManifest();
|
|
93
|
+
setMessage(nextMessage);
|
|
94
|
+
await refreshHealth();
|
|
95
|
+
}, [flushManifest, refreshHealth, updateAuthSettings]);
|
|
96
|
+
const persistCredentialLink = useCallback(async (link) => {
|
|
97
|
+
const result = await persistStoredOAuthCredentialLink({
|
|
98
|
+
link,
|
|
99
|
+
mutateAuthSettings,
|
|
100
|
+
flushManifest,
|
|
101
|
+
refreshHealth,
|
|
102
|
+
toMessage,
|
|
103
|
+
});
|
|
104
|
+
if (result.ok) {
|
|
105
|
+
authAdminSession.clearPendingCredentialLink(link.providerId);
|
|
106
|
+
setMessage(result.message);
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
authAdminSession.setPendingCredentialLink(result.pendingLink);
|
|
110
|
+
setMessage(result.message);
|
|
111
|
+
return result;
|
|
112
|
+
}, [authAdminSession, flushManifest, mutateAuthSettings, refreshHealth]);
|
|
113
|
+
const persistCredentialLinkAndPatchDraft = useCallback(async (link) => persistStoredOAuthCredentialLinkAndPatchLocalDraft({
|
|
114
|
+
link,
|
|
115
|
+
persistCredentialLink,
|
|
116
|
+
patchDraft: (mutation) => setDraft(mutation),
|
|
117
|
+
}), [persistCredentialLink]);
|
|
118
|
+
const retryPendingCredentialLink = useCallback(async (link) => {
|
|
119
|
+
const result = await authAdminSession.runCredentialTransaction(link.providerId, link.credentialsRef, () => persistCredentialLinkAndPatchDraft(link));
|
|
120
|
+
if (!result.ok) {
|
|
121
|
+
setMessage(formatAuthAdminWriteBusyReason(result.reason));
|
|
122
|
+
}
|
|
123
|
+
}, [authAdminSession, persistCredentialLinkAndPatchDraft]);
|
|
124
|
+
const save = useCallback(async () => {
|
|
125
|
+
setSaving(true);
|
|
126
|
+
setMessage(null);
|
|
127
|
+
try {
|
|
128
|
+
const result = await authAdminSession.runFullAuthSave(() => persistAuthDraft(draft, 'Authentication configuration saved to the Studio manifest.'));
|
|
129
|
+
if (!result.ok)
|
|
130
|
+
setMessage(formatAuthAdminWriteBusyReason(result.reason));
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
setMessage(toMessage(error));
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
setSaving(false);
|
|
137
|
+
}
|
|
138
|
+
}, [authAdminSession, draft, persistAuthDraft]);
|
|
139
|
+
const authEnabled = draft.scope !== 'none';
|
|
140
|
+
const signUpEnabled = draft.signUp !== undefined;
|
|
141
|
+
const profileEnabled = draft.profile !== undefined;
|
|
142
|
+
const oauth = draft.oauth ?? createDefaultOAuth();
|
|
143
|
+
const showGeneral = routeId === 'auth';
|
|
144
|
+
const showProviders = routeId === 'auth-providers';
|
|
145
|
+
const showRoutes = routeId === 'auth-routes';
|
|
146
|
+
const showProfile = routeId === 'auth-profile';
|
|
147
|
+
return (_jsxs(ScrollView, { contentContainerStyle: styles.content, keyboardShouldPersistTaps: "handled", children: [loading ? _jsx(ActivityIndicator, {}) : null, showGeneral || showProviders ? _jsx(AuthHealthCard, { health: health }) : null, authAdminSession.pendingCredentialLinks.map((pendingCredentialLink) => (_jsx(PendingCredentialLinkCard, { link: pendingCredentialLink, loading: authAdminSession.fullAuthSaveBusy ||
|
|
148
|
+
authAdminSession.busyCredentialProviderIds.has(pendingCredentialLink.providerId), onRetry: () => void retryPendingCredentialLink(pendingCredentialLink), onOpenSecrets: () => {
|
|
149
|
+
router.push('/ankh/secrets');
|
|
150
|
+
} }, pendingCredentialLink.providerId))), showGeneral ? (_jsxs(Card, { title: "Overview", children: [_jsx(SwitchSetting, { title: "Authentication enabled", description: "Disabling auth keeps the configuration but sets the canonical scope to none.", value: authEnabled, onValueChange: (enabled) => setDraft((current) => ({ ...current, scope: enabled ? 'global' : 'none' })) }), _jsx(KeyValue, { label: "Provider", value: "Supabase" }), _jsx(KeyValue, { label: "Configuration source", value: "infra.auth" }), _jsx(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: "Roles, RBAC, ABAC, registered users, and user passwords are intentionally not managed here." })] })) : null, showGeneral ? (_jsxs(Card, { title: "Email and password", children: [_jsx(Text, { weight: "semiBold", children: "Sign-in identifiers" }), _jsx(View, { style: styles.choiceRow, children: SIGN_IN_IDENTIFIERS.map((identifier) => {
|
|
151
|
+
const selected = draft.signIn.identifiers.includes(identifier);
|
|
152
|
+
return (_jsx(Choice, { label: identifier, selected: selected, onPress: () => setDraft((current) => {
|
|
153
|
+
const identifiers = selected
|
|
154
|
+
? current.signIn.identifiers.filter((value) => value !== identifier)
|
|
155
|
+
: [...current.signIn.identifiers, identifier];
|
|
156
|
+
if (identifiers.length === 0)
|
|
157
|
+
return current;
|
|
158
|
+
return { ...current, signIn: { identifiers } };
|
|
159
|
+
}) }, identifier));
|
|
160
|
+
}) }), _jsx(SwitchSetting, { title: "Public sign-up enabled", description: "When disabled, sign-up configuration is removed from the desired state.", value: signUpEnabled, onValueChange: (enabled) => setDraft((current) => enabled
|
|
161
|
+
? {
|
|
162
|
+
...current,
|
|
163
|
+
signUp: current.signUp ?? {
|
|
164
|
+
requiredFields: ['email', 'password'],
|
|
165
|
+
optionalFields: [],
|
|
166
|
+
signUpPolicy: 'requireVerification',
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
: omitSignUp(current)) }), draft.signUp ? (_jsxs(_Fragment, { children: [_jsx(Field, { label: "Required sign-up fields (comma-separated)", children: _jsx(Input, { value: draft.signUp.requiredFields.join(', '), autoCapitalize: "none", onChangeText: (value) => setDraft((current) => current.signUp
|
|
170
|
+
? {
|
|
171
|
+
...current,
|
|
172
|
+
signUp: {
|
|
173
|
+
...current.signUp,
|
|
174
|
+
requiredFields: splitList(value),
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
: current) }) }), _jsx(Field, { label: "Optional sign-up fields (comma-separated)", children: _jsx(Input, { value: (draft.signUp.optionalFields ?? []).join(', '), autoCapitalize: "none", onChangeText: (value) => setDraft((current) => current.signUp
|
|
178
|
+
? {
|
|
179
|
+
...current,
|
|
180
|
+
signUp: {
|
|
181
|
+
...current.signUp,
|
|
182
|
+
optionalFields: splitList(value),
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
: current) }) }), _jsx(SwitchSetting, { title: "Email confirmation required", description: "Uses the canonical requireVerification sign-up policy.", value: draft.signUp.signUpPolicy === 'requireVerification', onValueChange: (required) => setDraft((current) => current.signUp
|
|
186
|
+
? {
|
|
187
|
+
...current,
|
|
188
|
+
signUp: {
|
|
189
|
+
...current.signUp,
|
|
190
|
+
signUpPolicy: required ? 'requireVerification' : 'autoSignIn',
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
: current) })] })) : null] })) : null, showRoutes ? (_jsxs(Card, { title: "Routes", children: [_jsx(RouteField, { label: "Sign-in route", value: draft.flow.signInRoute, onChange: (signInRoute) => updateFlow(setDraft, { signInRoute }) }), _jsx(RouteField, { label: "Sign-up route", value: draft.flow.signUpRoute ?? '', onChange: (signUpRoute) => updateFlow(setDraft, { signUpRoute }) }), _jsx(RouteField, { label: "Sign-out route", value: draft.flow.signOutRoute ?? '', onChange: (signOutRoute) => updateFlow(setDraft, { signOutRoute }) }), _jsx(RouteField, { label: "Post-sign-in route", value: draft.flow.postSignInRoute, onChange: (postSignInRoute) => updateFlow(setDraft, { postSignInRoute }) }), _jsx(RouteField, { label: "Unauthorized route", value: draft.flow.unauthorizedRoute ?? '', onChange: (unauthorizedRoute) => updateFlow(setDraft, { unauthorizedRoute }) }), _jsx(RouteField, { label: "Forgot-password route", value: draft.flow.forgotPasswordRoute ?? '', onChange: (forgotPasswordRoute) => updateFlow(setDraft, { forgotPasswordRoute }) }), _jsx(Field, { label: "OAuth callback route", children: _jsx(Input, { value: oauth.callbackRoute, autoCapitalize: "none", onChangeText: (callbackRoute) => setDraft((current) => ({
|
|
194
|
+
...current,
|
|
195
|
+
oauth: { ...(current.oauth ?? createDefaultOAuth()), callbackRoute },
|
|
196
|
+
})) }) })] })) : null, showProviders ? (_jsxs(Card, { title: "OAuth providers", children: [_jsx(SwitchSetting, { title: "OAuth enabled", description: "Provider credentials remain in the server-side secret store.", value: oauth.enabled, onValueChange: (enabled) => setDraft((current) => ({
|
|
197
|
+
...current,
|
|
198
|
+
oauth: { ...(current.oauth ?? createDefaultOAuth()), enabled },
|
|
199
|
+
})) }), SUPABASE_OAUTH_PROVIDER_IDS.map((providerId) => (_jsx(OAuthProviderSetting, { projectId: projectId, providerId: providerId, oauth: oauth, providerHealth: health?.providers.find((provider) => provider.providerId === providerId), onChange: (nextOAuth, nextMessage) => {
|
|
200
|
+
setDraft((current) => ({ ...current, oauth: nextOAuth }));
|
|
201
|
+
setMessage(nextMessage);
|
|
202
|
+
}, onSaved: persistCredentialLinkAndPatchDraft, runCredentialTransaction: authAdminSession.runCredentialTransaction, transactionBusy: authAdminSession.fullAuthSaveBusy ||
|
|
203
|
+
authAdminSession.busyCredentialProviderIds.has(providerId) }, providerId))), _jsx(View, { style: styles.actions, children: _jsx(SecondaryButton, { label: "Open project secrets", onPress: () => {
|
|
204
|
+
router.push('/ankh/secrets');
|
|
205
|
+
} }) })] })) : null, showProfile ? (_jsxs(Card, { title: "Profile", children: [_jsx(SwitchSetting, { title: "Profile table enabled", description: "Profiles are separate from Supabase Auth users. The users table is not an option.", value: profileEnabled, onValueChange: (enabled) => setDraft((current) => enabled
|
|
206
|
+
? {
|
|
207
|
+
...current,
|
|
208
|
+
profile: current.profile ?? {
|
|
209
|
+
table: 'profiles',
|
|
210
|
+
fields: ['email', 'displayName', 'avatarUrl'],
|
|
211
|
+
primaryKey: 'authUserId',
|
|
212
|
+
createStrategy: 'trigger',
|
|
213
|
+
updateStrategy: 'api',
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
: omitProfile(current)) }), draft.profile ? (_jsxs(_Fragment, { children: [_jsx(Field, { label: "Profile table", children: _jsx(Input, { value: draft.profile.table ?? 'profiles', autoCapitalize: "none", onChangeText: (table) => setDraft((current) => current.profile
|
|
217
|
+
? { ...current, profile: { ...current.profile, table } }
|
|
218
|
+
: current) }) }), _jsx(Text, { weight: "semiBold", children: "Profile fields" }), _jsx(View, { style: styles.choiceRow, children: PROFILE_FIELDS.map((field) => {
|
|
219
|
+
const selected = draft.profile?.fields.includes(field) ?? false;
|
|
220
|
+
return (_jsx(Choice, { label: field, selected: selected, onPress: () => setDraft((current) => {
|
|
221
|
+
if (!current.profile)
|
|
222
|
+
return current;
|
|
223
|
+
const fields = selected
|
|
224
|
+
? current.profile.fields.filter((value) => value !== field)
|
|
225
|
+
: [...current.profile.fields, field];
|
|
226
|
+
return {
|
|
227
|
+
...current,
|
|
228
|
+
profile: { ...current.profile, fields },
|
|
229
|
+
};
|
|
230
|
+
}) }, field));
|
|
231
|
+
}) }), _jsx(KeyValue, { label: "Primary key", value: "authUserId" }), _jsx(Field, { label: "Create strategy", children: _jsx(View, { style: styles.choiceRow, children: ['trigger', 'api', 'app'].map((strategy) => (_jsx(Choice, { label: strategy, selected: draft.profile?.createStrategy === strategy, onPress: () => setDraft((current) => current.profile
|
|
232
|
+
? {
|
|
233
|
+
...current,
|
|
234
|
+
profile: { ...current.profile, createStrategy: strategy },
|
|
235
|
+
}
|
|
236
|
+
: current) }, strategy))) }) }), _jsx(Field, { label: "Update strategy", children: _jsx(View, { style: styles.choiceRow, children: ['api', 'app'].map((strategy) => (_jsx(Choice, { label: strategy, selected: draft.profile?.updateStrategy === strategy, onPress: () => setDraft((current) => current.profile
|
|
237
|
+
? {
|
|
238
|
+
...current,
|
|
239
|
+
profile: { ...current.profile, updateStrategy: strategy },
|
|
240
|
+
}
|
|
241
|
+
: current) }, strategy))) }) })] })) : null] })) : null, _jsxs(View, { style: styles.footerActions, children: [_jsx(SecondaryButton, { label: "Reload", onPress: () => void reload() }), _jsx(PrimaryButton, { label: "Save authentication", loading: saving || authAdminSession.authWriteBusy, onPress: () => void save() })] }), message ? _jsx(Message, { text: message }) : null] }));
|
|
242
|
+
}
|
|
243
|
+
function PendingCredentialLinkCard(props) {
|
|
244
|
+
return (_jsxs(Card, { title: "Credential link pending", children: [_jsxs(Text, { color: "warning", weight: "semiBold", children: [props.link.providerLabel, " credentials were saved, but the Studio manifest link still needs to be persisted."] }), _jsx(KeyValue, { label: "Credentials ref", value: props.link.credentialsRef }), _jsxs(View, { style: styles.actions, children: [_jsx(PrimaryButton, { label: "Retry manifest link", loading: props.loading, onPress: props.onRetry }), _jsx(SecondaryButton, { label: "Open project secrets", onPress: props.onOpenSecrets })] })] }));
|
|
245
|
+
}
|
|
246
|
+
function OAuthProviderSetting(props) {
|
|
247
|
+
const definition = getSupabaseOAuthProviderDefinition(props.providerId);
|
|
248
|
+
const current = props.oauth.providers.find((provider) => provider.id === props.providerId);
|
|
249
|
+
const requiredFields = props.providerHealth?.requiredFields ??
|
|
250
|
+
definition?.secretFields.map((field) => field.name) ??
|
|
251
|
+
[];
|
|
252
|
+
const configuredFields = props.providerHealth?.configuredFields ?? [];
|
|
253
|
+
const credentialsComplete = definition !== null &&
|
|
254
|
+
Boolean(current?.credentialsRef) &&
|
|
255
|
+
requiredFields.length > 0 &&
|
|
256
|
+
requiredFields.every((field) => configuredFields.includes(field));
|
|
257
|
+
const enabled = current?.enabled === true;
|
|
258
|
+
const [credentialValues, setCredentialValues] = useState({});
|
|
259
|
+
const [savingCredentials, setSavingCredentials] = useState(false);
|
|
260
|
+
const [credentialMessage, setCredentialMessage] = useState(null);
|
|
261
|
+
const setEnabled = (nextEnabled) => {
|
|
262
|
+
if (nextEnabled && !credentialsComplete) {
|
|
263
|
+
props.onChange(props.oauth, `Complete ${definition?.label ?? props.providerId} credentials before enabling the provider.`);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const nextProvider = {
|
|
267
|
+
...(current ?? {
|
|
268
|
+
id: props.providerId,
|
|
269
|
+
label: definition?.label ?? props.providerId,
|
|
270
|
+
scopes: [...(definition?.defaultScopes ?? [])],
|
|
271
|
+
}),
|
|
272
|
+
enabled: nextEnabled,
|
|
273
|
+
};
|
|
274
|
+
props.onChange({
|
|
275
|
+
...props.oauth,
|
|
276
|
+
providers: upsertProvider(props.oauth.providers, nextProvider),
|
|
277
|
+
}, null);
|
|
278
|
+
};
|
|
279
|
+
const saveCredentials = async () => {
|
|
280
|
+
if (props.transactionBusy) {
|
|
281
|
+
setCredentialMessage(`${definition?.label ?? props.providerId} credentials are already being saved.`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (!definition) {
|
|
285
|
+
setCredentialMessage('The selected OAuth provider is not supported.');
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const entries = definition.secretFields.map((field) => [field.name, credentialValues[field.name] ?? '']);
|
|
289
|
+
if (entries.some(([, value]) => !value)) {
|
|
290
|
+
setCredentialMessage('Enter a complete credential payload. Existing values cannot be merged.');
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const credentialsRef = current?.credentialsRef ?? `auth/oauth/${props.providerId}`;
|
|
294
|
+
const transaction = await props.runCredentialTransaction(props.providerId, credentialsRef, async () => {
|
|
295
|
+
setSavingCredentials(true);
|
|
296
|
+
setCredentialMessage(null);
|
|
297
|
+
try {
|
|
298
|
+
const result = await configureProjectOAuthProvider({
|
|
299
|
+
projectId: props.projectId,
|
|
300
|
+
providerId: props.providerId,
|
|
301
|
+
environment: 'local',
|
|
302
|
+
credentialsRef,
|
|
303
|
+
payload: Object.freeze(Object.fromEntries(entries)),
|
|
304
|
+
});
|
|
305
|
+
if (result.ok) {
|
|
306
|
+
const linkResult = await props.onSaved({
|
|
307
|
+
providerId: props.providerId,
|
|
308
|
+
providerLabel: definition.label,
|
|
309
|
+
credentialsRef: result.credentialsRef,
|
|
310
|
+
providerDefaults: {
|
|
311
|
+
label: definition.label,
|
|
312
|
+
scopes: [...definition.defaultScopes],
|
|
313
|
+
},
|
|
314
|
+
successMessage: `${definition.label} credentials saved through ${result.credentialsRef}.`,
|
|
315
|
+
});
|
|
316
|
+
if (!linkResult.ok)
|
|
317
|
+
setCredentialMessage(linkResult.message);
|
|
318
|
+
return linkResult;
|
|
319
|
+
}
|
|
320
|
+
setCredentialMessage(result.error.message);
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
setCredentialMessage(toMessage(error));
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
setCredentialValues({});
|
|
329
|
+
setSavingCredentials(false);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
if (!transaction.ok) {
|
|
333
|
+
setCredentialMessage(formatAuthAdminWriteBusyReason(transaction.reason));
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
return (_jsxs(View, { style: styles.providerPanel, children: [_jsxs(View, { style: styles.providerRow, children: [_jsxs(View, { style: styles.grow, children: [_jsx(Text, { weight: "semiBold", children: definition?.label ?? props.providerId }), _jsxs(Text, { color: credentialsComplete ? 'success' : 'warning', emphasis: "muted", variant: "caption", children: [formatProviderHealthStatus(props.providerHealth?.status ?? 'missing'), current?.credentialsRef ? `: ${current.credentialsRef}` : ''] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Required fields: ", props.providerHealth?.requiredFields.join(', ') || 'loading'] })] }), _jsx(Switch, { value: enabled, onValueChange: setEnabled })] }), definition?.secretFields.map((field) => (_jsx(Field, { label: field.label, children: _jsx(Input, { value: credentialValues[field.name] ?? '', secureTextEntry: field.secret, autoCapitalize: "none", placeholder: credentialsComplete ? 'Enter complete replacement value' : field.label, onChangeText: (value) => setCredentialValues((currentValues) => ({
|
|
337
|
+
...currentValues,
|
|
338
|
+
[field.name]: value,
|
|
339
|
+
})) }) }, field.name))), _jsx(View, { style: styles.actions, children: _jsx(PrimaryButton, { label: credentialsComplete ? 'Replace credentials' : 'Save credentials', loading: savingCredentials || props.transactionBusy, onPress: () => void saveCredentials() }) }), credentialMessage ? _jsx(Message, { text: credentialMessage }) : null] }));
|
|
340
|
+
}
|
|
341
|
+
function upsertProvider(providers, provider) {
|
|
342
|
+
const index = providers.findIndex((candidate) => candidate.id === provider.id);
|
|
343
|
+
if (index < 0)
|
|
344
|
+
return [...providers, provider];
|
|
345
|
+
return providers.map((candidate, candidateIndex) => candidateIndex === index ? provider : candidate);
|
|
346
|
+
}
|
|
347
|
+
function updateFlow(setDraft, patch) {
|
|
348
|
+
setDraft((current) => ({ ...current, flow: { ...current.flow, ...patch } }));
|
|
349
|
+
}
|
|
350
|
+
function omitSignUp(settings) {
|
|
351
|
+
const { signUp: _signUp, ...rest } = settings;
|
|
352
|
+
return rest;
|
|
353
|
+
}
|
|
354
|
+
function omitProfile(settings) {
|
|
355
|
+
const { profile: _profile, ...rest } = settings;
|
|
356
|
+
return rest;
|
|
357
|
+
}
|
|
358
|
+
function createDefaultSettings() {
|
|
359
|
+
return {
|
|
360
|
+
scope: 'none',
|
|
361
|
+
provider: 'supabase',
|
|
362
|
+
flow: { ...DEFAULT_AUTH_FLOW },
|
|
363
|
+
signIn: { identifiers: ['email'] },
|
|
364
|
+
oauth: createDefaultOAuth(),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function createDefaultOAuth() {
|
|
368
|
+
return {
|
|
369
|
+
enabled: false,
|
|
370
|
+
callbackRoute: '/auth/callback',
|
|
371
|
+
providers: [],
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function createFallbackManifest() {
|
|
375
|
+
return {
|
|
376
|
+
metadata: { name: 'App', slug: 'app', version: '1.0.0', themeId: 'default' },
|
|
377
|
+
settings: { localization: { defaultLocale: 'en', locales: ['en'] } },
|
|
378
|
+
infra: { plugins: [] },
|
|
379
|
+
navigator: { type: 'stack', routes: [] },
|
|
380
|
+
screens: {},
|
|
381
|
+
themes: [],
|
|
382
|
+
activeThemeId: 'default',
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function splitList(value) {
|
|
386
|
+
return [
|
|
387
|
+
...new Set(value
|
|
388
|
+
.split(',')
|
|
389
|
+
.map((entry) => entry.trim())
|
|
390
|
+
.filter(Boolean)),
|
|
391
|
+
];
|
|
392
|
+
}
|
|
393
|
+
function toMessage(error) {
|
|
394
|
+
if (error instanceof ProjectAuthApiError)
|
|
395
|
+
return error.message;
|
|
396
|
+
return error instanceof Error ? error.message : 'Authentication configuration request failed.';
|
|
397
|
+
}
|
|
398
|
+
function formatAuthAdminWriteBusyReason(reason) {
|
|
399
|
+
if (reason === 'full_auth_save_busy') {
|
|
400
|
+
return 'Authentication configuration is already being saved.';
|
|
401
|
+
}
|
|
402
|
+
if (reason === 'credential_transaction_busy') {
|
|
403
|
+
return 'OAuth credential changes are still being linked. Try again after they finish.';
|
|
404
|
+
}
|
|
405
|
+
if (reason === 'credential_ref_busy') {
|
|
406
|
+
return 'OAuth credentials for this secret are already being linked.';
|
|
407
|
+
}
|
|
408
|
+
if (reason === 'credential_secret_cleanup_busy') {
|
|
409
|
+
return 'Project secret cleanup is in progress for these OAuth credentials.';
|
|
410
|
+
}
|
|
411
|
+
return 'OAuth provider credentials are already being saved.';
|
|
412
|
+
}
|
|
413
|
+
function AuthHealthCard({ health }) {
|
|
414
|
+
if (!health) {
|
|
415
|
+
return (_jsx(Card, { title: "Health", children: _jsx(Text, { color: "neutral", emphasis: "muted", variant: "bodySmall", children: "Auth health is unavailable." }) }));
|
|
416
|
+
}
|
|
417
|
+
return (_jsxs(Card, { title: "Health", children: [_jsx(Text, { color: healthStatusColor(health.status), weight: "semiBold", children: formatHealthStatus(health.status) }), _jsx(KeyValue, { label: "Callback route", value: health.callbackUrls.appCallbackRoute }), health.callbackUrls.providerRedirectUrl ? (_jsx(KeyValue, { label: "Provider redirect URL", value: health.callbackUrls.providerRedirectUrl })) : null, health.providers.map((provider) => (_jsxs(View, { style: styles.healthProvider, children: [_jsxs(Text, { weight: "semiBold", children: [provider.label, ": ", formatProviderHealthStatus(provider.status)] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Ref: ", provider.credentialsRef ?? 'none'] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Required fields: ", provider.requiredFields.join(', ') || 'none'] }), _jsxs(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: ["Configured fields: ", provider.configuredFields.join(', ') || 'none'] }), provider.missingFields.length > 0 ? (_jsxs(Text, { color: "warning", variant: "caption", children: ["Missing fields: ", provider.missingFields.join(', ')] })) : null] }, provider.providerId))), health.diagnostics.map((diagnostic) => (_jsxs(View, { style: styles.diagnostic, children: [_jsx(Text, { color: diagnosticSeverityColor(diagnostic.severity), variant: "bodySmall", children: diagnostic.code }), _jsx(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: diagnostic.message })] }, `${diagnostic.code}:${diagnostic.path ?? ''}`))), health.diagnostics.length === 0 ? (_jsx(Text, { color: "success", variant: "bodySmall", children: "No auth diagnostics." })) : null] }));
|
|
418
|
+
}
|
|
419
|
+
function formatHealthStatus(status) {
|
|
420
|
+
if (status === 'healthy')
|
|
421
|
+
return 'Healthy';
|
|
422
|
+
if (status === 'warning')
|
|
423
|
+
return 'Warning';
|
|
424
|
+
if (status === 'error')
|
|
425
|
+
return 'Error';
|
|
426
|
+
return 'Not configured';
|
|
427
|
+
}
|
|
428
|
+
function formatProviderHealthStatus(status) {
|
|
429
|
+
if (status === 'configured')
|
|
430
|
+
return 'Configured';
|
|
431
|
+
if (status === 'incomplete')
|
|
432
|
+
return 'Incomplete';
|
|
433
|
+
if (status === 'missing')
|
|
434
|
+
return 'Missing secret';
|
|
435
|
+
if (status === 'invalid')
|
|
436
|
+
return 'Invalid';
|
|
437
|
+
return 'Disabled';
|
|
438
|
+
}
|
|
439
|
+
function healthStatusColor(status) {
|
|
440
|
+
if (status === 'healthy')
|
|
441
|
+
return 'success';
|
|
442
|
+
if (status === 'warning')
|
|
443
|
+
return 'warning';
|
|
444
|
+
if (status === 'error')
|
|
445
|
+
return 'danger';
|
|
446
|
+
return 'neutral';
|
|
447
|
+
}
|
|
448
|
+
function diagnosticSeverityColor(severity) {
|
|
449
|
+
if (severity === 'error')
|
|
450
|
+
return 'danger';
|
|
451
|
+
if (severity === 'warning')
|
|
452
|
+
return 'warning';
|
|
453
|
+
return 'neutral';
|
|
454
|
+
}
|
|
455
|
+
function Card(props) {
|
|
456
|
+
const { theme } = useZoraTheme();
|
|
457
|
+
return (_jsxs(View, { style: [
|
|
458
|
+
styles.card,
|
|
459
|
+
{ backgroundColor: theme.colors.surface, borderColor: theme.colors.border },
|
|
460
|
+
], children: [_jsx(Heading, { level: 3, text: props.title }), props.children] }));
|
|
461
|
+
}
|
|
462
|
+
function Field(props) {
|
|
463
|
+
return (_jsxs(View, { style: styles.field, children: [_jsx(Text, { variant: "bodySmall", weight: "semiBold", children: props.label }), props.children] }));
|
|
464
|
+
}
|
|
465
|
+
function RouteField(props) {
|
|
466
|
+
return (_jsx(Field, { label: props.label, children: _jsx(Input, { value: props.value, autoCapitalize: "none", onChangeText: props.onChange }) }));
|
|
467
|
+
}
|
|
468
|
+
function Input(props) {
|
|
469
|
+
const { theme } = useZoraTheme();
|
|
470
|
+
return (_jsx(TextInput, { ...props, placeholderTextColor: theme.colors.textMuted, style: [
|
|
471
|
+
styles.input,
|
|
472
|
+
{
|
|
473
|
+
color: theme.colors.text,
|
|
474
|
+
backgroundColor: theme.colors.background,
|
|
475
|
+
borderColor: theme.colors.border,
|
|
476
|
+
},
|
|
477
|
+
props.style,
|
|
478
|
+
] }));
|
|
479
|
+
}
|
|
480
|
+
function SwitchSetting(props) {
|
|
481
|
+
return (_jsxs(View, { style: styles.switchRow, children: [_jsxs(View, { style: styles.grow, children: [_jsx(Text, { weight: "semiBold", children: props.title }), _jsx(Text, { color: "neutral", emphasis: "muted", variant: "caption", children: props.description })] }), _jsx(Switch, { value: props.value, onValueChange: props.onValueChange })] }));
|
|
482
|
+
}
|
|
483
|
+
function Choice(props) {
|
|
484
|
+
const { theme } = useZoraTheme();
|
|
485
|
+
return (_jsx(Pressable, { onPress: props.onPress, style: [
|
|
486
|
+
styles.choice,
|
|
487
|
+
{
|
|
488
|
+
borderColor: props.selected ? theme.colors.primary : theme.colors.border,
|
|
489
|
+
backgroundColor: props.selected ? theme.colors.surface : theme.colors.background,
|
|
490
|
+
},
|
|
491
|
+
], children: _jsx(Text, { color: props.selected ? 'primary' : 'neutral', variant: "bodySmall", weight: "semiBold", children: props.label }) }));
|
|
492
|
+
}
|
|
493
|
+
function KeyValue(props) {
|
|
494
|
+
return (_jsxs(View, { style: styles.keyValue, children: [_jsx(Text, { color: "neutral", emphasis: "muted", variant: "bodySmall", children: props.label }), _jsx(Text, { weight: "semiBold", children: props.value })] }));
|
|
495
|
+
}
|
|
496
|
+
function PrimaryButton(props) {
|
|
497
|
+
const { theme } = useZoraTheme();
|
|
498
|
+
return (_jsx(Pressable, { disabled: props.loading, onPress: props.onPress, style: [styles.primaryButton, { backgroundColor: theme.colors.primary }], children: props.loading ? (_jsx(ActivityIndicator, { color: "#ffffff" })) : (_jsx(Text, { emphasis: "inverse", weight: "semiBold", children: props.label })) }));
|
|
499
|
+
}
|
|
500
|
+
function SecondaryButton(props) {
|
|
501
|
+
const { theme } = useZoraTheme();
|
|
502
|
+
return (_jsx(Pressable, { onPress: props.onPress, style: [styles.secondaryButton, { borderColor: theme.colors.border }], children: _jsx(Text, { weight: "semiBold", children: props.label }) }));
|
|
503
|
+
}
|
|
504
|
+
function Message({ text }) {
|
|
505
|
+
return (_jsx(Text, { color: "info", variant: "bodySmall", children: text }));
|
|
506
|
+
}
|
|
507
|
+
const styles = StyleSheet.create({
|
|
508
|
+
content: {
|
|
509
|
+
width: '100%',
|
|
510
|
+
maxWidth: 920,
|
|
511
|
+
alignSelf: 'center',
|
|
512
|
+
padding: 20,
|
|
513
|
+
gap: 16,
|
|
514
|
+
},
|
|
515
|
+
card: {
|
|
516
|
+
borderWidth: 1,
|
|
517
|
+
borderRadius: 14,
|
|
518
|
+
padding: 16,
|
|
519
|
+
gap: 14,
|
|
520
|
+
},
|
|
521
|
+
grow: { flex: 1 },
|
|
522
|
+
field: { gap: 6 },
|
|
523
|
+
input: {
|
|
524
|
+
minHeight: 42,
|
|
525
|
+
borderWidth: 1,
|
|
526
|
+
borderRadius: 8,
|
|
527
|
+
paddingHorizontal: 12,
|
|
528
|
+
paddingVertical: 8,
|
|
529
|
+
},
|
|
530
|
+
switchRow: {
|
|
531
|
+
flexDirection: 'row',
|
|
532
|
+
alignItems: 'center',
|
|
533
|
+
gap: 16,
|
|
534
|
+
},
|
|
535
|
+
choiceRow: {
|
|
536
|
+
flexDirection: 'row',
|
|
537
|
+
flexWrap: 'wrap',
|
|
538
|
+
gap: 8,
|
|
539
|
+
},
|
|
540
|
+
choice: {
|
|
541
|
+
borderWidth: 1,
|
|
542
|
+
borderRadius: 999,
|
|
543
|
+
paddingHorizontal: 12,
|
|
544
|
+
paddingVertical: 8,
|
|
545
|
+
},
|
|
546
|
+
providerRow: {
|
|
547
|
+
flexDirection: 'row',
|
|
548
|
+
alignItems: 'center',
|
|
549
|
+
gap: 16,
|
|
550
|
+
paddingVertical: 8,
|
|
551
|
+
},
|
|
552
|
+
providerPanel: {
|
|
553
|
+
gap: 10,
|
|
554
|
+
paddingVertical: 8,
|
|
555
|
+
},
|
|
556
|
+
healthProvider: {
|
|
557
|
+
gap: 3,
|
|
558
|
+
paddingVertical: 8,
|
|
559
|
+
},
|
|
560
|
+
diagnostic: {
|
|
561
|
+
gap: 3,
|
|
562
|
+
paddingVertical: 6,
|
|
563
|
+
},
|
|
564
|
+
keyValue: {
|
|
565
|
+
flexDirection: 'row',
|
|
566
|
+
alignItems: 'center',
|
|
567
|
+
justifyContent: 'space-between',
|
|
568
|
+
gap: 16,
|
|
569
|
+
},
|
|
570
|
+
actions: {
|
|
571
|
+
flexDirection: 'row',
|
|
572
|
+
justifyContent: 'flex-end',
|
|
573
|
+
gap: 10,
|
|
574
|
+
},
|
|
575
|
+
footerActions: {
|
|
576
|
+
flexDirection: 'row',
|
|
577
|
+
justifyContent: 'flex-end',
|
|
578
|
+
gap: 10,
|
|
579
|
+
},
|
|
580
|
+
primaryButton: {
|
|
581
|
+
minHeight: 42,
|
|
582
|
+
borderRadius: 8,
|
|
583
|
+
paddingHorizontal: 16,
|
|
584
|
+
alignItems: 'center',
|
|
585
|
+
justifyContent: 'center',
|
|
586
|
+
},
|
|
587
|
+
secondaryButton: {
|
|
588
|
+
minHeight: 42,
|
|
589
|
+
borderWidth: 1,
|
|
590
|
+
borderRadius: 8,
|
|
591
|
+
paddingHorizontal: 16,
|
|
592
|
+
alignItems: 'center',
|
|
593
|
+
justifyContent: 'center',
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
//# sourceMappingURL=AuthAdminPage.js.map
|