@geenius/adapters 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -42
- package/package.json +23 -4
- package/packages/convex/README.md +1 -1
- package/packages/convex/dist/index.cjs +300 -0
- package/packages/convex/dist/index.cjs.map +1 -0
- package/packages/convex/dist/index.d.cts +231 -0
- package/packages/convex/dist/index.d.ts +231 -0
- package/packages/convex/dist/index.js +263 -0
- package/packages/convex/dist/index.js.map +1 -0
- package/packages/react/README.md +1 -1
- package/packages/react/dist/index.d.mts +106 -0
- package/packages/react/dist/index.d.ts +106 -0
- package/packages/react/dist/index.js +611 -0
- package/packages/react/dist/index.js.map +1 -0
- package/packages/react/dist/index.mjs +570 -0
- package/packages/react/dist/index.mjs.map +1 -0
- package/packages/react-css/README.md +1 -1
- package/packages/react-css/dist/index.cjs +515 -0
- package/packages/react-css/dist/index.cjs.map +1 -0
- package/packages/react-css/dist/index.d.cts +105 -0
- package/packages/react-css/dist/index.d.ts +105 -0
- package/packages/react-css/dist/index.js +467 -0
- package/packages/react-css/dist/index.js.map +1 -0
- package/packages/shared/README.md +1 -1
- package/packages/shared/dist/index.d.mts +625 -0
- package/packages/shared/dist/index.d.ts +625 -0
- package/packages/shared/dist/index.js +1567 -0
- package/packages/shared/dist/index.js.map +1 -0
- package/packages/shared/dist/index.mjs +1489 -0
- package/packages/shared/dist/index.mjs.map +1 -0
- package/packages/solidjs/README.md +1 -1
- package/packages/solidjs/dist/index.d.mts +97 -0
- package/packages/solidjs/dist/index.d.ts +97 -0
- package/packages/solidjs/dist/index.js +250 -0
- package/packages/solidjs/dist/index.js.map +1 -0
- package/packages/solidjs/dist/index.mjs +202 -0
- package/packages/solidjs/dist/index.mjs.map +1 -0
- package/packages/solidjs-css/README.md +1 -1
- package/packages/solidjs-css/dist/index.cjs +343 -0
- package/packages/solidjs-css/dist/index.cjs.map +1 -0
- package/packages/solidjs-css/dist/index.d.cts +67 -0
- package/packages/solidjs-css/dist/index.d.ts +67 -0
- package/packages/solidjs-css/dist/index.js +326 -0
- package/packages/solidjs-css/dist/index.js.map +1 -0
- package/.changeset/config.json +0 -11
- package/.github/CODEOWNERS +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/ci.yml +0 -23
- package/.github/workflows/release.yml +0 -29
- package/.nvmrc +0 -1
- package/.project/ACCOUNT.yaml +0 -4
- package/.project/IDEAS.yaml +0 -7
- package/.project/PROJECT.yaml +0 -11
- package/.project/ROADMAP.yaml +0 -15
- package/CODE_OF_CONDUCT.md +0 -16
- package/CONTRIBUTING.md +0 -26
- package/SECURITY.md +0 -15
- package/SUPPORT.md +0 -8
- package/packages/convex/package.json +0 -42
- package/packages/convex/src/adapter.ts +0 -39
- package/packages/convex/src/index.ts +0 -19
- package/packages/convex/src/mutations.ts +0 -142
- package/packages/convex/src/queries.ts +0 -106
- package/packages/convex/src/schema.ts +0 -54
- package/packages/convex/src/types.ts +0 -20
- package/packages/convex/tsconfig.json +0 -11
- package/packages/convex/tsup.config.ts +0 -10
- package/packages/react/package.json +0 -45
- package/packages/react/src/components/AdapterCard.tsx +0 -49
- package/packages/react/src/components/AdapterConfigForm.tsx +0 -118
- package/packages/react/src/components/AdapterList.tsx +0 -84
- package/packages/react/src/components/AdapterStatusBadge.tsx +0 -30
- package/packages/react/src/components/index.ts +0 -4
- package/packages/react/src/hooks/index.ts +0 -75
- package/packages/react/src/index.tsx +0 -44
- package/packages/react/src/pages/AdapterDetailPage.tsx +0 -133
- package/packages/react/src/pages/AdaptersPage.tsx +0 -111
- package/packages/react/src/pages/index.ts +0 -2
- package/packages/react/src/provider/AdapterProvider.tsx +0 -115
- package/packages/react/src/provider/index.ts +0 -2
- package/packages/react/tsconfig.json +0 -18
- package/packages/react/tsup.config.ts +0 -10
- package/packages/react-css/package.json +0 -44
- package/packages/react-css/src/adapters.css +0 -1576
- package/packages/react-css/src/components/AdapterCard.tsx +0 -34
- package/packages/react-css/src/components/AdapterConfigForm.tsx +0 -63
- package/packages/react-css/src/components/AdapterList.tsx +0 -40
- package/packages/react-css/src/components/AdapterStatusBadge.tsx +0 -21
- package/packages/react-css/src/components/index.ts +0 -4
- package/packages/react-css/src/hooks/index.ts +0 -75
- package/packages/react-css/src/index.tsx +0 -25
- package/packages/react-css/src/pages/AdapterDetailPage.tsx +0 -133
- package/packages/react-css/src/pages/AdaptersPage.tsx +0 -111
- package/packages/react-css/src/pages/index.ts +0 -2
- package/packages/react-css/src/provider/AdapterProvider.tsx +0 -115
- package/packages/react-css/src/provider/index.ts +0 -2
- package/packages/react-css/src/styles.css +0 -494
- package/packages/react-css/tsconfig.json +0 -19
- package/packages/react-css/tsup.config.ts +0 -2
- package/packages/shared/package.json +0 -39
- package/packages/shared/src/__tests__/adapters.test.ts +0 -545
- package/packages/shared/src/admin/index.ts +0 -2
- package/packages/shared/src/admin/interface.ts +0 -34
- package/packages/shared/src/admin/localStorage.ts +0 -109
- package/packages/shared/src/ai/anthropic.ts +0 -123
- package/packages/shared/src/ai/cloudflare-gateway.ts +0 -130
- package/packages/shared/src/ai/gemini.ts +0 -181
- package/packages/shared/src/ai/index.ts +0 -14
- package/packages/shared/src/ai/interface.ts +0 -11
- package/packages/shared/src/ai/localStorage.ts +0 -78
- package/packages/shared/src/ai/ollama.ts +0 -143
- package/packages/shared/src/ai/openai.ts +0 -120
- package/packages/shared/src/ai/vercel-ai.ts +0 -101
- package/packages/shared/src/auth/better-auth.ts +0 -118
- package/packages/shared/src/auth/clerk.ts +0 -151
- package/packages/shared/src/auth/convex-auth.ts +0 -125
- package/packages/shared/src/auth/index.ts +0 -10
- package/packages/shared/src/auth/interface.ts +0 -17
- package/packages/shared/src/auth/localStorage.ts +0 -125
- package/packages/shared/src/auth/supabase-auth.ts +0 -136
- package/packages/shared/src/config.ts +0 -57
- package/packages/shared/src/constants.ts +0 -122
- package/packages/shared/src/db/convex.ts +0 -146
- package/packages/shared/src/db/index.ts +0 -10
- package/packages/shared/src/db/interface.ts +0 -13
- package/packages/shared/src/db/localStorage.ts +0 -91
- package/packages/shared/src/db/mongodb.ts +0 -125
- package/packages/shared/src/db/neon.ts +0 -171
- package/packages/shared/src/db/supabase.ts +0 -158
- package/packages/shared/src/index.ts +0 -117
- package/packages/shared/src/payments/index.ts +0 -4
- package/packages/shared/src/payments/interface.ts +0 -11
- package/packages/shared/src/payments/localStorage.ts +0 -81
- package/packages/shared/src/payments/stripe.ts +0 -177
- package/packages/shared/src/storage/convex.ts +0 -113
- package/packages/shared/src/storage/index.ts +0 -14
- package/packages/shared/src/storage/interface.ts +0 -11
- package/packages/shared/src/storage/localStorage.ts +0 -95
- package/packages/shared/src/storage/minio.ts +0 -47
- package/packages/shared/src/storage/r2.ts +0 -123
- package/packages/shared/src/storage/s3.ts +0 -128
- package/packages/shared/src/storage/supabase-storage.ts +0 -116
- package/packages/shared/src/storage/uploadthing.ts +0 -126
- package/packages/shared/src/styles/adapters.css +0 -494
- package/packages/shared/src/tier-gate.ts +0 -119
- package/packages/shared/src/types.ts +0 -162
- package/packages/shared/tsconfig.json +0 -18
- package/packages/shared/tsup.config.ts +0 -9
- package/packages/shared/vitest.config.ts +0 -14
- package/packages/solidjs/package.json +0 -44
- package/packages/solidjs/src/components/AdapterCard.tsx +0 -24
- package/packages/solidjs/src/components/AdapterConfigForm.tsx +0 -54
- package/packages/solidjs/src/components/AdapterList.tsx +0 -28
- package/packages/solidjs/src/components/AdapterStatusBadge.tsx +0 -20
- package/packages/solidjs/src/components/index.ts +0 -4
- package/packages/solidjs/src/index.tsx +0 -17
- package/packages/solidjs/src/pages/AdapterDetailPage.tsx +0 -38
- package/packages/solidjs/src/pages/AdaptersPage.tsx +0 -39
- package/packages/solidjs/src/pages/index.ts +0 -2
- package/packages/solidjs/src/primitives/index.ts +0 -78
- package/packages/solidjs/src/provider/AdapterProvider.tsx +0 -62
- package/packages/solidjs/src/provider/index.ts +0 -2
- package/packages/solidjs/tsconfig.json +0 -20
- package/packages/solidjs/tsup.config.ts +0 -10
- package/packages/solidjs-css/package.json +0 -43
- package/packages/solidjs-css/src/adapters.css +0 -1576
- package/packages/solidjs-css/src/components/AdapterCard.tsx +0 -43
- package/packages/solidjs-css/src/components/AdapterConfigForm.tsx +0 -119
- package/packages/solidjs-css/src/components/AdapterList.tsx +0 -68
- package/packages/solidjs-css/src/components/AdapterStatusBadge.tsx +0 -24
- package/packages/solidjs-css/src/components/index.ts +0 -8
- package/packages/solidjs-css/src/index.tsx +0 -30
- package/packages/solidjs-css/src/pages/AdapterDetailPage.tsx +0 -107
- package/packages/solidjs-css/src/pages/AdaptersPage.tsx +0 -94
- package/packages/solidjs-css/src/pages/index.ts +0 -4
- package/packages/solidjs-css/src/primitives/index.ts +0 -1
- package/packages/solidjs-css/src/provider/AdapterProvider.tsx +0 -61
- package/packages/solidjs-css/src/provider/index.ts +0 -2
- package/packages/solidjs-css/tsconfig.json +0 -20
- package/packages/solidjs-css/tsup.config.ts +0 -2
- package/pnpm-workspace.yaml +0 -2
- package/tsconfig.json +0 -17
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// @geenius-adapters/react — AdapterConfigForm
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import type { AdapterDomain, DomainAdapterConfig } from '@geenius-adapters/shared'
|
|
5
|
-
import { DOMAIN_LABELS, getProvidersForDomain } from '@geenius-adapters/shared'
|
|
6
|
-
|
|
7
|
-
export interface AdapterConfigFormProps {
|
|
8
|
-
domain: AdapterDomain
|
|
9
|
-
initialConfig?: DomainAdapterConfig
|
|
10
|
-
onSave: (config: DomainAdapterConfig) => Promise<void> | void
|
|
11
|
-
onCancel?: () => void
|
|
12
|
-
className?: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function AdapterConfigForm({ domain, initialConfig, onSave, onCancel, className = '' }: AdapterConfigFormProps) {
|
|
16
|
-
const providers = getProvidersForDomain(domain)
|
|
17
|
-
const [provider, setProvider] = useState(initialConfig?.provider ?? providers[0]?.id ?? '')
|
|
18
|
-
const [apiKey, setApiKey] = useState(initialConfig?.apiKey ?? '')
|
|
19
|
-
const [baseUrl, setBaseUrl] = useState(initialConfig?.baseUrl ?? '')
|
|
20
|
-
const [isSaving, setIsSaving] = useState(false)
|
|
21
|
-
const [error, setError] = useState<string | null>(null)
|
|
22
|
-
|
|
23
|
-
const selectedMeta = providers.find((p) => p.id === provider)
|
|
24
|
-
const needsApiKey = !['localStorage', 'noop'].includes(provider)
|
|
25
|
-
const needsBaseUrl = ['ollama', 'minio', 'neon', 'supabase'].includes(provider)
|
|
26
|
-
|
|
27
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
28
|
-
e.preventDefault()
|
|
29
|
-
setError(null)
|
|
30
|
-
if (!provider) { setError('Select a provider'); return }
|
|
31
|
-
if (needsApiKey && !apiKey.trim()) { setError('API key is required'); return }
|
|
32
|
-
setIsSaving(true)
|
|
33
|
-
try {
|
|
34
|
-
await onSave({ provider, apiKey: apiKey || undefined, baseUrl: baseUrl || undefined })
|
|
35
|
-
} catch (e) {
|
|
36
|
-
setError(e instanceof Error ? e.message : 'Failed to save')
|
|
37
|
-
} finally {
|
|
38
|
-
setIsSaving(false)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<form onSubmit={handleSubmit} className={`space-y-5 rounded-2xl border border-white/10 bg-white/[0.03] p-6 backdrop-blur-sm ${className}`} noValidate>
|
|
44
|
-
<div>
|
|
45
|
-
<h3 className="text-lg font-bold text-white/90">Configure {DOMAIN_LABELS[domain]}</h3>
|
|
46
|
-
<p className="mt-1 text-xs text-white/40">Select and configure your preferred provider.</p>
|
|
47
|
-
</div>
|
|
48
|
-
|
|
49
|
-
{error && <div className="rounded-lg border border-red-500/20 bg-red-500/10 px-4 py-3 text-sm text-red-400" role="alert">{error}</div>}
|
|
50
|
-
|
|
51
|
-
<div>
|
|
52
|
-
<label htmlFor={`adapter-${domain}-provider`} className="mb-1.5 block text-xs font-semibold uppercase tracking-wider text-white/40">Provider</label>
|
|
53
|
-
<div className="grid gap-2 sm:grid-cols-2">
|
|
54
|
-
{providers.map((p) => (
|
|
55
|
-
<button
|
|
56
|
-
key={p.id}
|
|
57
|
-
type="button"
|
|
58
|
-
onClick={() => setProvider(p.id)}
|
|
59
|
-
className={`flex flex-col rounded-xl border p-3 text-left transition-all ${
|
|
60
|
-
provider === p.id
|
|
61
|
-
? 'border-indigo-500/30 bg-indigo-500/10'
|
|
62
|
-
: 'border-white/10 bg-white/[0.02] hover:bg-white/[0.04]'
|
|
63
|
-
}`}
|
|
64
|
-
>
|
|
65
|
-
<span className="text-xs font-semibold text-white/80">{p.name}</span>
|
|
66
|
-
<span className="mt-0.5 text-[0.625rem] text-white/40">{p.description}</span>
|
|
67
|
-
<span className={`mt-1 self-start rounded px-1.5 py-0.5 text-[0.5625rem] font-bold uppercase ${
|
|
68
|
-
p.tier === 'pronto' ? 'bg-emerald-500/10 text-emerald-400' : p.tier === 'mvp' ? 'bg-blue-500/10 text-blue-400' : 'bg-purple-500/10 text-purple-400'
|
|
69
|
-
}`}>{p.tier}</span>
|
|
70
|
-
</button>
|
|
71
|
-
))}
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
{needsApiKey && (
|
|
76
|
-
<div>
|
|
77
|
-
<label htmlFor={`adapter-${domain}-apiKey`} className="mb-1.5 block text-xs font-semibold uppercase tracking-wider text-white/40">API Key</label>
|
|
78
|
-
<input
|
|
79
|
-
id={`adapter-${domain}-apiKey`}
|
|
80
|
-
type="password"
|
|
81
|
-
value={apiKey}
|
|
82
|
-
onChange={(e) => setApiKey(e.target.value)}
|
|
83
|
-
placeholder={`Enter ${selectedMeta?.name ?? provider} API key`}
|
|
84
|
-
autoComplete="off"
|
|
85
|
-
className="w-full rounded-xl border border-white/10 bg-white/[0.02] px-4 py-3 text-sm text-white/90 placeholder:text-white/20 focus:border-indigo-500/50 focus:outline-none focus:ring-2 focus:ring-indigo-500/20"
|
|
86
|
-
/>
|
|
87
|
-
</div>
|
|
88
|
-
)}
|
|
89
|
-
|
|
90
|
-
{needsBaseUrl && (
|
|
91
|
-
<div>
|
|
92
|
-
<label htmlFor={`adapter-${domain}-url`} className="mb-1.5 block text-xs font-semibold uppercase tracking-wider text-white/40">Base URL</label>
|
|
93
|
-
<input
|
|
94
|
-
id={`adapter-${domain}-url`}
|
|
95
|
-
type="url"
|
|
96
|
-
value={baseUrl}
|
|
97
|
-
onChange={(e) => setBaseUrl(e.target.value)}
|
|
98
|
-
placeholder={`https://...`}
|
|
99
|
-
className="w-full rounded-xl border border-white/10 bg-white/[0.02] px-4 py-3 text-sm text-white/90 placeholder:text-white/20 focus:border-indigo-500/50 focus:outline-none focus:ring-2 focus:ring-indigo-500/20"
|
|
100
|
-
/>
|
|
101
|
-
</div>
|
|
102
|
-
)}
|
|
103
|
-
|
|
104
|
-
<div className="flex items-center justify-end gap-3 pt-2">
|
|
105
|
-
{onCancel && (
|
|
106
|
-
<button type="button" onClick={onCancel} className="rounded-xl px-5 py-2.5 text-sm font-semibold text-white/50 hover:text-white/70">Cancel</button>
|
|
107
|
-
)}
|
|
108
|
-
<button
|
|
109
|
-
type="submit"
|
|
110
|
-
disabled={isSaving || !provider}
|
|
111
|
-
className="flex items-center gap-2 rounded-xl bg-indigo-500 px-6 py-2.5 text-sm font-semibold text-white hover:bg-indigo-400 disabled:opacity-50"
|
|
112
|
-
>
|
|
113
|
-
{isSaving ? 'Saving…' : 'Save Configuration'}
|
|
114
|
-
</button>
|
|
115
|
-
</div>
|
|
116
|
-
</form>
|
|
117
|
-
)
|
|
118
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// @geenius-adapters/react — AdapterList
|
|
2
|
-
|
|
3
|
-
import { useState, useMemo } from 'react'
|
|
4
|
-
import { useAdapters } from '../hooks'
|
|
5
|
-
import { AdapterCard } from './AdapterCard'
|
|
6
|
-
import { ADAPTER_DOMAINS, DOMAIN_LABELS } from '@geenius-adapters/shared'
|
|
7
|
-
import type { AdapterDomain, AdapterStatusType } from '@geenius-adapters/shared'
|
|
8
|
-
|
|
9
|
-
export interface AdapterListProps {
|
|
10
|
-
onSelect?: (domain: AdapterDomain) => void
|
|
11
|
-
filterStatus?: AdapterStatusType
|
|
12
|
-
className?: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function AdapterList({ onSelect, filterStatus, className = '' }: AdapterListProps) {
|
|
16
|
-
const { statuses, isLoading } = useAdapters()
|
|
17
|
-
const [search, setSearch] = useState('')
|
|
18
|
-
|
|
19
|
-
const filteredDomains = useMemo(() => {
|
|
20
|
-
let domains = [...ADAPTER_DOMAINS]
|
|
21
|
-
if (search) {
|
|
22
|
-
const q = search.toLowerCase()
|
|
23
|
-
domains = domains.filter((d) => {
|
|
24
|
-
const label = DOMAIN_LABELS[d].toLowerCase()
|
|
25
|
-
const provider = statuses[d]?.provider?.toLowerCase() ?? ''
|
|
26
|
-
return label.includes(q) || provider.includes(q) || d.includes(q)
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
if (filterStatus) {
|
|
30
|
-
domains = domains.filter((d) => statuses[d]?.status === filterStatus)
|
|
31
|
-
}
|
|
32
|
-
return domains
|
|
33
|
-
}, [search, filterStatus, statuses])
|
|
34
|
-
|
|
35
|
-
if (isLoading) {
|
|
36
|
-
return (
|
|
37
|
-
<div className={`space-y-3 ${className}`} role="status" aria-label="Loading adapters">
|
|
38
|
-
{[1, 2, 3, 4].map((i) => (
|
|
39
|
-
<div key={i} className="animate-pulse rounded-2xl border border-white/5 bg-white/[0.02] p-5">
|
|
40
|
-
<div className="flex items-start gap-4">
|
|
41
|
-
<div className="h-12 w-12 rounded-xl bg-white/10" />
|
|
42
|
-
<div className="flex-1 space-y-2">
|
|
43
|
-
<div className="h-3 w-24 rounded bg-white/10" />
|
|
44
|
-
<div className="h-2 w-48 rounded bg-white/5" />
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
))}
|
|
49
|
-
</div>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div className={className}>
|
|
55
|
-
<div className="mb-4">
|
|
56
|
-
<input
|
|
57
|
-
type="text"
|
|
58
|
-
value={search}
|
|
59
|
-
onChange={(e) => setSearch(e.target.value)}
|
|
60
|
-
placeholder="Search adapters..."
|
|
61
|
-
aria-label="Search adapters"
|
|
62
|
-
className="w-full rounded-xl border border-white/10 bg-white/[0.02] px-4 py-2.5 text-sm text-white/90 placeholder:text-white/20 focus:border-indigo-500/50 focus:outline-none focus:ring-2 focus:ring-indigo-500/20"
|
|
63
|
-
/>
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
{filteredDomains.length === 0 ? (
|
|
67
|
-
<div className="rounded-xl border border-white/5 bg-white/[0.01] p-8 text-center">
|
|
68
|
-
<p className="text-sm text-white/40">{search ? 'No adapters match your search' : 'No adapters configured'}</p>
|
|
69
|
-
</div>
|
|
70
|
-
) : (
|
|
71
|
-
<div className="grid gap-3 sm:grid-cols-2" role="list" aria-label="Adapter list">
|
|
72
|
-
{filteredDomains.map((domain) => (
|
|
73
|
-
<AdapterCard
|
|
74
|
-
key={domain}
|
|
75
|
-
domain={domain}
|
|
76
|
-
status={statuses[domain]}
|
|
77
|
-
onClick={() => onSelect?.(domain)}
|
|
78
|
-
/>
|
|
79
|
-
))}
|
|
80
|
-
</div>
|
|
81
|
-
)}
|
|
82
|
-
</div>
|
|
83
|
-
)
|
|
84
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// @geenius-adapters/react — AdapterStatusBadge
|
|
2
|
-
|
|
3
|
-
import type { AdapterStatusType } from '@geenius-adapters/shared'
|
|
4
|
-
|
|
5
|
-
const STATUS_STYLES: Record<AdapterStatusType, { bg: string; text: string; dot: string; label: string }> = {
|
|
6
|
-
connected: { bg: 'bg-emerald-500/10', text: 'text-emerald-400', dot: 'bg-emerald-400', label: 'Connected' },
|
|
7
|
-
disconnected: { bg: 'bg-white/5', text: 'text-white/40', dot: 'bg-white/30', label: 'Disconnected' },
|
|
8
|
-
error: { bg: 'bg-red-500/10', text: 'text-red-400', dot: 'bg-red-400', label: 'Error' },
|
|
9
|
-
initializing: { bg: 'bg-amber-500/10', text: 'text-amber-400', dot: 'bg-amber-400', label: 'Initializing' },
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface AdapterStatusBadgeProps {
|
|
13
|
-
status: AdapterStatusType
|
|
14
|
-
showLabel?: boolean
|
|
15
|
-
size?: 'sm' | 'md'
|
|
16
|
-
className?: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function AdapterStatusBadge({ status, showLabel = true, size = 'sm', className = '' }: AdapterStatusBadgeProps) {
|
|
20
|
-
const s = STATUS_STYLES[status]
|
|
21
|
-
const dotSize = size === 'sm' ? 'h-1.5 w-1.5' : 'h-2 w-2'
|
|
22
|
-
const textSize = size === 'sm' ? 'text-[0.625rem]' : 'text-xs'
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<span className={`inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 ${s.bg} ${className}`}>
|
|
26
|
-
<span className={`${dotSize} rounded-full ${s.dot} ${status === 'initializing' ? 'animate-pulse' : ''}`} />
|
|
27
|
-
{showLabel && <span className={`${textSize} font-semibold uppercase tracking-wider ${s.text}`}>{s.label}</span>}
|
|
28
|
-
</span>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { AdapterStatusBadge, type AdapterStatusBadgeProps } from './AdapterStatusBadge'
|
|
2
|
-
export { AdapterCard, type AdapterCardProps } from './AdapterCard'
|
|
3
|
-
export { AdapterList, type AdapterListProps } from './AdapterList'
|
|
4
|
-
export { AdapterConfigForm, type AdapterConfigFormProps } from './AdapterConfigForm'
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
// @geenius-adapters/react — Hooks
|
|
2
|
-
|
|
3
|
-
import { useMemo } from 'react'
|
|
4
|
-
import { useAdapterContext } from '../provider/AdapterProvider'
|
|
5
|
-
import type {
|
|
6
|
-
AuthAdapter, DbAdapter, PaymentsAdapter, AiAdapter, FileStorageAdapter, AdminAdapter,
|
|
7
|
-
AdapterDomain, AdapterStatusInfo,
|
|
8
|
-
} from '@geenius-adapters/shared'
|
|
9
|
-
|
|
10
|
-
// ─── Domain-specific hooks ───────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
/** Access the database adapter */
|
|
13
|
-
export function useDb(): DbAdapter {
|
|
14
|
-
return useAdapterContext().getAdapter('db')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Access the auth adapter */
|
|
18
|
-
export function useAuth(): AuthAdapter {
|
|
19
|
-
return useAdapterContext().getAdapter('auth')
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Access the payments adapter */
|
|
23
|
-
export function usePayments(): PaymentsAdapter {
|
|
24
|
-
return useAdapterContext().getAdapter('payments')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Access the AI adapter */
|
|
28
|
-
export function useAi(): AiAdapter {
|
|
29
|
-
return useAdapterContext().getAdapter('ai')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Access the file storage adapter */
|
|
33
|
-
export function useStorage(): FileStorageAdapter {
|
|
34
|
-
return useAdapterContext().getAdapter('storage')
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Access the admin adapter */
|
|
38
|
-
export function useAdmin(): AdminAdapter {
|
|
39
|
-
return useAdapterContext().getAdapter('admin')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ─── Status hooks ────────────────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
/** Get all adapter statuses */
|
|
45
|
-
export function useAdapterStatuses(): Record<AdapterDomain, AdapterStatusInfo> {
|
|
46
|
-
return useAdapterContext().statuses
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Get status for a specific adapter domain */
|
|
50
|
-
export function useAdapterStatus(domain: AdapterDomain): AdapterStatusInfo {
|
|
51
|
-
return useAdapterContext().statuses[domain]
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Check if adapter is ready */
|
|
55
|
-
export function useIsAdapterReady(domain: AdapterDomain): boolean {
|
|
56
|
-
return useAdapterContext().isReady(domain)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ─── Full adapters hook ──────────────────────────────────────────────────────
|
|
60
|
-
|
|
61
|
-
/** Access all adapters at once */
|
|
62
|
-
export function useAdapters() {
|
|
63
|
-
const ctx = useAdapterContext()
|
|
64
|
-
return useMemo(() => ({
|
|
65
|
-
db: ctx.adapters.db,
|
|
66
|
-
auth: ctx.adapters.auth,
|
|
67
|
-
payments: ctx.adapters.payments,
|
|
68
|
-
ai: ctx.adapters.ai,
|
|
69
|
-
storage: ctx.adapters.storage,
|
|
70
|
-
admin: ctx.adapters.admin,
|
|
71
|
-
statuses: ctx.statuses,
|
|
72
|
-
isLoading: ctx.isLoading,
|
|
73
|
-
isReady: ctx.isReady,
|
|
74
|
-
}), [ctx])
|
|
75
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// @geenius-adapters/react — Barrel export
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @geenius-adapters/react
|
|
5
|
-
*
|
|
6
|
-
* React hooks, components, and pages for the Geenius adapter ecosystem.
|
|
7
|
-
* Wraps all 6 adapter domains (DB, Auth, AI, Storage, Payments, Admin)
|
|
8
|
-
* behind a unified provider/hook API.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Re-export shared types for convenience
|
|
12
|
-
export type {
|
|
13
|
-
AuthUser, AuthSession, OAuthProvider, ListOptions, QueryFilter,
|
|
14
|
-
Plan, Subscription, CheckoutParams, CheckoutResult,
|
|
15
|
-
ChatMessage, AiOptions, ChatResponse, StoredFile,
|
|
16
|
-
AdapterDomain, DomainAdapterConfig, AdapterConfig,
|
|
17
|
-
AdapterStatusType, AdapterStatusInfo, AdapterRegistryEntry,
|
|
18
|
-
AuthAdapter, DbAdapter, PaymentsAdapter, AiAdapter, FileStorageAdapter,
|
|
19
|
-
AdminAdapter, AdminMetrics, ManagedUser,
|
|
20
|
-
} from '@geenius-adapters/shared'
|
|
21
|
-
|
|
22
|
-
export {
|
|
23
|
-
ADAPTER_DOMAINS, DOMAIN_LABELS, DOMAIN_ICONS, DOMAIN_DESCRIPTIONS,
|
|
24
|
-
getProvidersForDomain, getProviderMeta,
|
|
25
|
-
configureAdapters, getAdapterConfig, isAdaptersConfigured,
|
|
26
|
-
} from '@geenius-adapters/shared'
|
|
27
|
-
|
|
28
|
-
// Provider
|
|
29
|
-
export { AdapterProvider, useAdapterContext } from './provider'
|
|
30
|
-
export type { AdapterProviderProps, AdapterSet, AdapterContextValue } from './provider'
|
|
31
|
-
|
|
32
|
-
// Hooks
|
|
33
|
-
export {
|
|
34
|
-
useDb, useAuth, usePayments, useAi, useStorage, useAdmin,
|
|
35
|
-
useAdapters, useAdapterStatuses, useAdapterStatus, useIsAdapterReady,
|
|
36
|
-
} from './hooks'
|
|
37
|
-
|
|
38
|
-
// Components
|
|
39
|
-
export { AdapterStatusBadge, AdapterCard, AdapterList, AdapterConfigForm } from './components'
|
|
40
|
-
export type { AdapterStatusBadgeProps, AdapterCardProps, AdapterListProps, AdapterConfigFormProps } from './components'
|
|
41
|
-
|
|
42
|
-
// Pages
|
|
43
|
-
export { AdaptersPage, AdapterDetailPage } from './pages'
|
|
44
|
-
export type { AdaptersPageProps, AdapterDetailPageProps } from './pages'
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
// @geenius-adapters/react — AdapterDetailPage
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import { useAdapters, useAdapterStatus } from '../hooks'
|
|
5
|
-
import { AdapterStatusBadge } from '../components/AdapterStatusBadge'
|
|
6
|
-
import { AdapterConfigForm } from '../components/AdapterConfigForm'
|
|
7
|
-
import type { AdapterDomain } from '@geenius-adapters/shared'
|
|
8
|
-
import { DOMAIN_LABELS, DOMAIN_ICONS, DOMAIN_DESCRIPTIONS, getProvidersForDomain } from '@geenius-adapters/shared'
|
|
9
|
-
|
|
10
|
-
export interface AdapterDetailPageProps {
|
|
11
|
-
domain: AdapterDomain
|
|
12
|
-
onBack?: () => void
|
|
13
|
-
className?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function AdapterDetailPage({ domain, onBack, className = '' }: AdapterDetailPageProps) {
|
|
17
|
-
const { isLoading } = useAdapters()
|
|
18
|
-
const status = useAdapterStatus(domain)
|
|
19
|
-
const [isEditing, setIsEditing] = useState(false)
|
|
20
|
-
const providers = getProvidersForDomain(domain)
|
|
21
|
-
|
|
22
|
-
const label = DOMAIN_LABELS[domain]
|
|
23
|
-
const icon = DOMAIN_ICONS[domain]
|
|
24
|
-
const desc = DOMAIN_DESCRIPTIONS[domain]
|
|
25
|
-
|
|
26
|
-
if (isLoading) {
|
|
27
|
-
return (
|
|
28
|
-
<div className={`max-w-3xl mx-auto space-y-6 ${className}`}>
|
|
29
|
-
<div className="animate-pulse space-y-4">
|
|
30
|
-
<div className="h-6 w-32 rounded bg-white/10" />
|
|
31
|
-
<div className="h-40 rounded-2xl bg-white/[0.03]" />
|
|
32
|
-
<div className="h-60 rounded-2xl bg-white/[0.03]" />
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<div className={`max-w-3xl mx-auto space-y-8 ${className}`}>
|
|
40
|
-
{/* Back navigation */}
|
|
41
|
-
{onBack && (
|
|
42
|
-
<button onClick={onBack} className="flex items-center gap-1.5 text-sm text-white/30 hover:text-white/60 transition-colors">
|
|
43
|
-
<span>←</span> Back to Adapters
|
|
44
|
-
</button>
|
|
45
|
-
)}
|
|
46
|
-
|
|
47
|
-
{/* Header */}
|
|
48
|
-
<div className="flex items-start gap-5">
|
|
49
|
-
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-white/5 text-3xl">{icon}</div>
|
|
50
|
-
<div className="flex-1">
|
|
51
|
-
<div className="flex items-center gap-3">
|
|
52
|
-
<h1 className="text-2xl font-extrabold text-white/95 tracking-tight">{label}</h1>
|
|
53
|
-
<AdapterStatusBadge status={status.status} size="md" />
|
|
54
|
-
</div>
|
|
55
|
-
<p className="mt-1 text-sm text-white/40">{desc}</p>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
|
|
59
|
-
{/* Status Card */}
|
|
60
|
-
<div className="rounded-2xl border border-white/10 bg-white/[0.03] p-6 backdrop-blur-sm">
|
|
61
|
-
<h2 className="text-xs font-semibold uppercase tracking-wider text-white/30 mb-4">Current Status</h2>
|
|
62
|
-
<div className="grid gap-4 sm:grid-cols-3">
|
|
63
|
-
<div>
|
|
64
|
-
<p className="text-[0.625rem] text-white/30 uppercase">Provider</p>
|
|
65
|
-
<p className="mt-0.5 text-sm font-semibold text-white/80">{status.provider !== 'none' ? status.provider : '—'}</p>
|
|
66
|
-
</div>
|
|
67
|
-
<div>
|
|
68
|
-
<p className="text-[0.625rem] text-white/30 uppercase">Status</p>
|
|
69
|
-
<p className="mt-0.5"><AdapterStatusBadge status={status.status} /></p>
|
|
70
|
-
</div>
|
|
71
|
-
<div>
|
|
72
|
-
<p className="text-[0.625rem] text-white/30 uppercase">Last Checked</p>
|
|
73
|
-
<p className="mt-0.5 text-sm text-white/60">{status.lastCheckedAt ? new Date(status.lastCheckedAt).toLocaleTimeString() : '—'}</p>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
{status.error && (
|
|
77
|
-
<div className="mt-4 rounded-lg border border-red-500/20 bg-red-500/10 px-4 py-3 text-sm text-red-400">
|
|
78
|
-
<span className="font-semibold">Error:</span> {status.error}
|
|
79
|
-
</div>
|
|
80
|
-
)}
|
|
81
|
-
{status.latency && (
|
|
82
|
-
<div className="mt-4">
|
|
83
|
-
<p className="text-[0.625rem] text-white/30 uppercase mb-1">Latency</p>
|
|
84
|
-
<div className="h-2 w-full rounded-full bg-white/5 overflow-hidden">
|
|
85
|
-
<div className={`h-full rounded-full transition-all ${status.latency < 100 ? 'bg-emerald-400' : status.latency < 500 ? 'bg-amber-400' : 'bg-red-400'}`}
|
|
86
|
-
style={{ width: `${Math.min(100, (status.latency / 1000) * 100)}%` }} />
|
|
87
|
-
</div>
|
|
88
|
-
<p className="mt-1 text-xs text-white/40">{status.latency}ms</p>
|
|
89
|
-
</div>
|
|
90
|
-
)}
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
{/* Available Providers */}
|
|
94
|
-
<div className="rounded-2xl border border-white/10 bg-white/[0.03] p-6 backdrop-blur-sm">
|
|
95
|
-
<div className="flex items-center justify-between mb-4">
|
|
96
|
-
<h2 className="text-xs font-semibold uppercase tracking-wider text-white/30">Available Providers</h2>
|
|
97
|
-
<span className="text-xs text-white/20">{providers.length} providers</span>
|
|
98
|
-
</div>
|
|
99
|
-
<div className="divide-y divide-white/5">
|
|
100
|
-
{providers.map(p => (
|
|
101
|
-
<div key={p.id} className="flex items-center justify-between py-3 first:pt-0 last:pb-0">
|
|
102
|
-
<div>
|
|
103
|
-
<p className="text-sm font-semibold text-white/80">{p.name}</p>
|
|
104
|
-
<p className="text-xs text-white/30">{p.description}</p>
|
|
105
|
-
</div>
|
|
106
|
-
<span className={`self-start rounded px-2 py-0.5 text-[0.625rem] font-bold uppercase ${
|
|
107
|
-
p.tier === 'pronto' ? 'bg-emerald-500/10 text-emerald-400' : p.tier === 'mvp' ? 'bg-blue-500/10 text-blue-400' : 'bg-purple-500/10 text-purple-400'
|
|
108
|
-
}`}>{p.tier}</span>
|
|
109
|
-
</div>
|
|
110
|
-
))}
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
{/* Configuration */}
|
|
115
|
-
<div>
|
|
116
|
-
{isEditing ? (
|
|
117
|
-
<AdapterConfigForm
|
|
118
|
-
domain={domain}
|
|
119
|
-
onSave={async () => setIsEditing(false)}
|
|
120
|
-
onCancel={() => setIsEditing(false)}
|
|
121
|
-
/>
|
|
122
|
-
) : (
|
|
123
|
-
<button
|
|
124
|
-
onClick={() => setIsEditing(true)}
|
|
125
|
-
className="w-full rounded-2xl border border-dashed border-white/10 bg-white/[0.01] p-6 text-center text-sm text-white/30 hover:border-white/20 hover:text-white/50 transition-all"
|
|
126
|
-
>
|
|
127
|
-
⚙️ Configure {label} Adapter
|
|
128
|
-
</button>
|
|
129
|
-
)}
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
)
|
|
133
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
// @geenius-adapters/react — AdaptersPage
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import { useAdapters } from '../hooks'
|
|
5
|
-
import { AdapterList } from '../components/AdapterList'
|
|
6
|
-
import { AdapterConfigForm } from '../components/AdapterConfigForm'
|
|
7
|
-
import { AdapterStatusBadge } from '../components/AdapterStatusBadge'
|
|
8
|
-
import { ADAPTER_DOMAINS, ADAPTER_STATUSES, DOMAIN_LABELS } from '@geenius-adapters/shared'
|
|
9
|
-
import type { AdapterDomain, AdapterStatusType } from '@geenius-adapters/shared'
|
|
10
|
-
|
|
11
|
-
export interface AdaptersPageProps {
|
|
12
|
-
onNavigateDetail?: (domain: AdapterDomain) => void
|
|
13
|
-
className?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function AdaptersPage({ onNavigateDetail, className = '' }: AdaptersPageProps) {
|
|
17
|
-
const { statuses, isLoading } = useAdapters()
|
|
18
|
-
const [selectedDomain, setSelectedDomain] = useState<AdapterDomain | null>(null)
|
|
19
|
-
const [filterStatus, setFilterStatus] = useState<AdapterStatusType | undefined>()
|
|
20
|
-
|
|
21
|
-
// Status summary counts
|
|
22
|
-
const counts = {
|
|
23
|
-
connected: ADAPTER_DOMAINS.filter(d => statuses[d]?.status === 'connected').length,
|
|
24
|
-
error: ADAPTER_DOMAINS.filter(d => statuses[d]?.status === 'error').length,
|
|
25
|
-
total: ADAPTER_DOMAINS.length,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (isLoading) {
|
|
29
|
-
return (
|
|
30
|
-
<div className={`max-w-4xl mx-auto space-y-6 ${className}`}>
|
|
31
|
-
<div className="animate-pulse space-y-4">
|
|
32
|
-
<div className="h-8 w-48 rounded bg-white/10" />
|
|
33
|
-
<div className="h-4 w-72 rounded bg-white/5" />
|
|
34
|
-
<div className="grid gap-3 sm:grid-cols-3">{[1,2,3].map(i => <div key={i} className="h-20 rounded-2xl bg-white/[0.03]" />)}</div>
|
|
35
|
-
<div className="grid gap-3 sm:grid-cols-2">{[1,2,3,4].map(i => <div key={i} className="h-28 rounded-2xl bg-white/[0.03]" />)}</div>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div className={`max-w-4xl mx-auto space-y-8 ${className}`}>
|
|
43
|
-
{/* Header */}
|
|
44
|
-
<div>
|
|
45
|
-
<h1 className="text-2xl font-extrabold text-white/95 tracking-tight">Adapters</h1>
|
|
46
|
-
<p className="mt-1 text-sm text-white/40">Manage your infrastructure adapters — database, auth, AI, storage, payments, and admin.</p>
|
|
47
|
-
</div>
|
|
48
|
-
|
|
49
|
-
{/* Status Summary */}
|
|
50
|
-
<div className="grid gap-3 sm:grid-cols-3">
|
|
51
|
-
<div className="rounded-2xl border border-white/10 bg-white/[0.03] p-4 backdrop-blur-sm">
|
|
52
|
-
<p className="text-xs font-semibold uppercase tracking-wider text-white/30">Total</p>
|
|
53
|
-
<p className="mt-1 text-2xl font-extrabold text-white/90">{counts.total}</p>
|
|
54
|
-
<p className="text-[0.625rem] text-white/30">adapter domains</p>
|
|
55
|
-
</div>
|
|
56
|
-
<div className="rounded-2xl border border-emerald-500/10 bg-emerald-500/[0.03] p-4 backdrop-blur-sm">
|
|
57
|
-
<p className="text-xs font-semibold uppercase tracking-wider text-emerald-400/60">Connected</p>
|
|
58
|
-
<p className="mt-1 text-2xl font-extrabold text-emerald-400">{counts.connected}</p>
|
|
59
|
-
<p className="text-[0.625rem] text-emerald-400/40">active adapters</p>
|
|
60
|
-
</div>
|
|
61
|
-
{counts.error > 0 && (
|
|
62
|
-
<div className="rounded-2xl border border-red-500/10 bg-red-500/[0.03] p-4 backdrop-blur-sm">
|
|
63
|
-
<p className="text-xs font-semibold uppercase tracking-wider text-red-400/60">Errors</p>
|
|
64
|
-
<p className="mt-1 text-2xl font-extrabold text-red-400">{counts.error}</p>
|
|
65
|
-
<p className="text-[0.625rem] text-red-400/40">need attention</p>
|
|
66
|
-
</div>
|
|
67
|
-
)}
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
{/* Filters */}
|
|
71
|
-
<div className="flex items-center gap-2">
|
|
72
|
-
<button
|
|
73
|
-
onClick={() => setFilterStatus(undefined)}
|
|
74
|
-
className={`rounded-lg px-3 py-1.5 text-xs font-semibold transition-all ${!filterStatus ? 'bg-white/10 text-white/80' : 'text-white/30 hover:text-white/50'}`}
|
|
75
|
-
>All</button>
|
|
76
|
-
{ADAPTER_STATUSES.map(s => (
|
|
77
|
-
<button
|
|
78
|
-
key={s}
|
|
79
|
-
onClick={() => setFilterStatus(s)}
|
|
80
|
-
className={`flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold transition-all ${filterStatus === s ? 'bg-white/10 text-white/80' : 'text-white/30 hover:text-white/50'}`}
|
|
81
|
-
>
|
|
82
|
-
<AdapterStatusBadge status={s} showLabel={false} size="sm" />
|
|
83
|
-
{s}
|
|
84
|
-
</button>
|
|
85
|
-
))}
|
|
86
|
-
</div>
|
|
87
|
-
|
|
88
|
-
{/* Adapter Grid */}
|
|
89
|
-
<AdapterList
|
|
90
|
-
onSelect={(domain) => {
|
|
91
|
-
if (onNavigateDetail) { onNavigateDetail(domain) }
|
|
92
|
-
else { setSelectedDomain(domain) }
|
|
93
|
-
}}
|
|
94
|
-
filterStatus={filterStatus}
|
|
95
|
-
/>
|
|
96
|
-
|
|
97
|
-
{/* Inline Config Modal */}
|
|
98
|
-
{selectedDomain && (
|
|
99
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm" onClick={() => setSelectedDomain(null)}>
|
|
100
|
-
<div className="max-w-lg w-full mx-4" onClick={e => e.stopPropagation()}>
|
|
101
|
-
<AdapterConfigForm
|
|
102
|
-
domain={selectedDomain}
|
|
103
|
-
onSave={async () => { setSelectedDomain(null) }}
|
|
104
|
-
onCancel={() => setSelectedDomain(null)}
|
|
105
|
-
/>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
)}
|
|
109
|
-
</div>
|
|
110
|
-
)
|
|
111
|
-
}
|