Funnels V2: event steps, edit UI, filters, trends, breakdowns #56
58
app/sites/[id]/funnels/[funnelId]/edit/page.tsx
Normal file
58
app/sites/[id]/funnels/[funnelId]/edit/page.tsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useParams, useRouter } from 'next/navigation'
|
||||||
|
import { useSWRConfig } from 'swr'
|
||||||
|
import { getFunnel, updateFunnel, type Funnel, type CreateFunnelRequest } from '@/lib/api/funnels'
|
||||||
|
import { toast } from '@ciphera-net/ui'
|
||||||
|
import FunnelForm from '@/components/funnels/FunnelForm'
|
||||||
|
import { FunnelDetailSkeleton } from '@/components/skeletons'
|
||||||
|
|
||||||
|
export default function EditFunnelPage() {
|
||||||
|
const params = useParams()
|
||||||
|
const router = useRouter()
|
||||||
|
const { mutate } = useSWRConfig()
|
||||||
|
const siteId = params.id as string
|
||||||
|
const funnelId = params.funnelId as string
|
||||||
|
const [funnel, setFunnel] = useState<Funnel | null>(null)
|
||||||
|
const [saving, setSaving] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getFunnel(siteId, funnelId).then(setFunnel).catch(() => {
|
||||||
|
toast.error('Failed to load funnel')
|
||||||
|
router.push(`/sites/${siteId}/funnels`)
|
||||||
|
})
|
||||||
|
}, [siteId, funnelId, router])
|
||||||
|
|
||||||
|
const handleSubmit = async (data: CreateFunnelRequest) => {
|
||||||
|
try {
|
||||||
|
setSaving(true)
|
||||||
|
await updateFunnel(siteId, funnelId, data)
|
||||||
|
await mutate(['funnels', siteId])
|
||||||
|
toast.success('Funnel updated')
|
||||||
|
router.push(`/sites/${siteId}/funnels/${funnelId}`)
|
||||||
|
} catch {
|
||||||
|
toast.error('Failed to update funnel. Please try again.')
|
||||||
|
} finally {
|
||||||
|
setSaving(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!funnel) return <FunnelDetailSkeleton />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FunnelForm
|
||||||
|
siteId={siteId}
|
||||||
|
initialData={{
|
||||||
|
name: funnel.name,
|
||||||
|
description: funnel.description,
|
||||||
|
steps: funnel.steps.map(({ order, ...rest }) => rest),
|
||||||
|
conversion_window_value: funnel.conversion_window_value,
|
||||||
|
conversion_window_unit: funnel.conversion_window_unit,
|
||||||
|
}}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitLabel={saving ? 'Saving...' : 'Save Changes'}
|
||||||
|
cancelHref={`/sites/${siteId}/funnels/${funnelId}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import FilterBar from '@/components/dashboard/FilterBar'
|
|||||||
import AddFilterDropdown from '@/components/dashboard/AddFilterDropdown'
|
import AddFilterDropdown from '@/components/dashboard/AddFilterDropdown'
|
||||||
import { type DimensionFilter, serializeFilters } from '@/lib/filters'
|
import { type DimensionFilter, serializeFilters } from '@/lib/filters'
|
||||||
import { toast, Select, DatePicker, ChevronLeftIcon, ArrowRightIcon, TrashIcon, Button } from '@ciphera-net/ui'
|
import { toast, Select, DatePicker, ChevronLeftIcon, ArrowRightIcon, TrashIcon, Button } from '@ciphera-net/ui'
|
||||||
|
import { PencilSimple } from '@phosphor-icons/react'
|
||||||
import { FunnelDetailSkeleton, useMinimumLoading, useSkeletonFade } from '@/components/skeletons'
|
import { FunnelDetailSkeleton, useMinimumLoading, useSkeletonFade } from '@/components/skeletons'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { FunnelChart } from '@/components/ui/funnel-chart'
|
import { FunnelChart } from '@/components/ui/funnel-chart'
|
||||||
@@ -162,6 +163,13 @@ export default function FunnelReportPage() {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={`/sites/${siteId}/funnels/${funnelId}/edit`}
|
||||||
|
className="p-2 text-neutral-400 hover:text-brand-orange hover:bg-orange-50 dark:hover:bg-orange-900/20 rounded-xl transition-colors"
|
||||||
|
aria-label="Edit funnel"
|
||||||
|
>
|
||||||
|
<PencilSimple className="w-5 h-5" />
|
||||||
|
</Link>
|
||||||
<button
|
<button
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
className="p-2 text-neutral-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-xl transition-colors"
|
className="p-2 text-neutral-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-xl transition-colors"
|
||||||
|
|||||||
Reference in New Issue
Block a user