feat: add integration setup options and script copying functionality to welcome page
This commit is contained in:
@@ -21,6 +21,8 @@ import { createSite, type Site } from '@/lib/api/sites'
|
||||
import { setSessionAction } from '@/app/actions/auth'
|
||||
import { useAuth } from '@/lib/auth/context'
|
||||
import { getAuthErrorMessage } from '@/lib/utils/authErrors'
|
||||
import { API_URL, APP_URL } from '@/lib/api/client'
|
||||
import { integrations, getIntegration } from '@/lib/integrations'
|
||||
import {
|
||||
trackWelcomeStepView,
|
||||
trackWelcomeWorkspaceSelected,
|
||||
@@ -35,6 +37,7 @@ import { LoadingOverlay, Button, Input } from '@ciphera-net/ui'
|
||||
import { toast } from '@ciphera-net/ui'
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
CheckIcon,
|
||||
ArrowRightIcon,
|
||||
ArrowLeftIcon,
|
||||
BarChartIcon,
|
||||
@@ -104,6 +107,9 @@ function WelcomeContent() {
|
||||
const [orgsLoading, setOrgsLoading] = useState(false)
|
||||
const [switchingOrgId, setSwitchingOrgId] = useState<string | null>(null)
|
||||
|
||||
const [selectedIntegrationSlug, setSelectedIntegrationSlug] = useState<string | null>(null)
|
||||
const [scriptCopied, setScriptCopied] = useState(false)
|
||||
|
||||
const setStep = useCallback(
|
||||
(next: number) => {
|
||||
const s = Math.min(Math.max(1, next), TOTAL_STEPS)
|
||||
@@ -264,6 +270,17 @@ function WelcomeContent() {
|
||||
}
|
||||
const goToSite = () => createdSite && router.push(`/sites/${createdSite.id}`)
|
||||
|
||||
const copyScript = useCallback(() => {
|
||||
if (!createdSite) return
|
||||
const script = `<script defer data-domain="${createdSite.domain}" data-api="${API_URL}" src="${APP_URL}/script.js"></script>`
|
||||
navigator.clipboard.writeText(script)
|
||||
setScriptCopied(true)
|
||||
toast.success('Script copied to clipboard')
|
||||
setTimeout(() => setScriptCopied(false), 2000)
|
||||
}, [createdSite])
|
||||
|
||||
const popularIntegrations = integrations.filter((i) => i.category === 'framework').slice(0, 10)
|
||||
|
||||
const showPendingCheckoutInStep3 =
|
||||
hadPendingCheckout === true && !dismissedPendingCheckout
|
||||
|
||||
@@ -688,16 +705,85 @@ function WelcomeContent() {
|
||||
? `"${createdSite.name}" is ready. Add the script to your site to start collecting data.`
|
||||
: 'Head to your dashboard to add sites and view analytics.'}
|
||||
</p>
|
||||
<div className="mt-8 flex flex-col sm:flex-row gap-3 justify-center">
|
||||
<Button variant="primary" onClick={goToDashboard} className="min-w-[160px]">
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{createdSite && (
|
||||
<Button variant="secondary" onClick={goToSite} className="min-w-[160px]">
|
||||
View {createdSite.name}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{createdSite && (
|
||||
<div className="mt-6 pt-6 border-t border-neutral-200 dark:border-neutral-700">
|
||||
<h3 className="text-sm font-semibold text-neutral-900 dark:text-white mb-1">
|
||||
Add the script to your site
|
||||
</h3>
|
||||
<p className="text-xs text-neutral-500 dark:text-neutral-400 mb-3">
|
||||
Choose your framework for setup instructions.
|
||||
</p>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2 mb-4">
|
||||
{popularIntegrations.map((int) => (
|
||||
<button
|
||||
key={int.id}
|
||||
type="button"
|
||||
onClick={() => setSelectedIntegrationSlug(selectedIntegrationSlug === int.id ? null : int.id)}
|
||||
className={`flex items-center gap-2 rounded-lg border px-3 py-2.5 text-left text-sm transition-colors ${
|
||||
selectedIntegrationSlug === int.id
|
||||
? 'border-brand-orange bg-brand-orange/10 text-brand-orange'
|
||||
: 'border-neutral-200 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-800/50 hover:bg-neutral-100 dark:hover:bg-neutral-800 text-neutral-700 dark:text-neutral-300'
|
||||
}`}
|
||||
>
|
||||
<span className="[&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0 flex items-center justify-center">
|
||||
{int.icon}
|
||||
</span>
|
||||
<span className="truncate font-medium">{int.name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-neutral-500 dark:text-neutral-400 mb-2">
|
||||
<Link href="/integrations" className="text-brand-orange hover:underline">
|
||||
View all integrations →
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<div className="rounded-xl bg-neutral-100 dark:bg-neutral-800 p-4 relative group">
|
||||
<code className="text-xs text-neutral-900 dark:text-white break-all font-mono block pr-10">
|
||||
{`<script defer data-domain="${createdSite.domain}" data-api="${API_URL}" src="${APP_URL}/script.js"></script>`}
|
||||
</code>
|
||||
<button
|
||||
type="button"
|
||||
onClick={copyScript}
|
||||
className="absolute top-2 right-2 p-2 bg-white dark:bg-neutral-700 rounded-lg shadow-sm hover:bg-neutral-50 dark:hover:bg-neutral-600 transition-colors"
|
||||
title="Copy script"
|
||||
>
|
||||
{scriptCopied ? (
|
||||
<CheckIcon className="w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<svg className="w-4 h-4 text-neutral-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
{selectedIntegrationSlug && getIntegration(selectedIntegrationSlug) && (
|
||||
<p className="mt-3 text-xs">
|
||||
<Link
|
||||
href={`/integrations/${selectedIntegrationSlug}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-brand-orange hover:underline"
|
||||
>
|
||||
See full {getIntegration(selectedIntegrationSlug)!.name} guide →
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-8 flex flex-col sm:flex-row gap-3 justify-center">
|
||||
<Button variant="primary" onClick={goToDashboard} className="min-w-[160px]">
|
||||
Go to dashboard
|
||||
</Button>
|
||||
{createdSite && (
|
||||
<Button variant="secondary" onClick={goToSite} className="min-w-[160px]">
|
||||
View {createdSite.name}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user