dograh/ui/src/app/workflow/create/page.tsx

225 lines
10 KiB
TypeScript
Raw Normal View History

'use client';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { createWorkflowFromTemplateApiV1WorkflowCreateTemplatePost } from '@/client/sdk.gen';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
import { useAuth } from '@/lib/auth';
import logger from '@/lib/logger';
export default function CreateWorkflowPage() {
const router = useRouter();
const { user, getAccessToken } = useAuth();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [workflowId, setWorkflowId] = useState<string | null>(null);
const [callType, setCallType] = useState<'inbound' | 'outbound'>('inbound');
const [useCase, setUseCase] = useState('');
const [activityDescription, setActivityDescription] = useState('');
const handleCreateWorkflow = async () => {
if (!useCase || !activityDescription) {
setError('Please fill in all fields');
return;
}
if (!user) {
setError('You must be logged in to create a workflow');
return;
}
setIsLoading(true);
setError(null);
try {
const accessToken = await getAccessToken();
// Call the API to create workflow from template
const response = await createWorkflowFromTemplateApiV1WorkflowCreateTemplatePost({
body: {
call_type: callType,
use_case: useCase,
activity_description: activityDescription,
},
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
if (response.data?.id) {
setWorkflowId(String(response.data.id));
setShowSuccessModal(true);
}
} catch (err) {
setError('Failed to create workflow. Please try again.');
logger.error(`Error creating workflow: ${err}`);
} finally {
setIsLoading(false);
}
};
const handleModalContinue = () => {
if (!workflowId) return;
router.push(`/workflow/${workflowId}?onboarding=web_call`);
};
return (
feat: UI refresh and user onboarding (#430) * docs: design spec for lead-gen surfaces (Credits & Billing, Hire-an-Expert, Top-up, Enterprise) Add brainstorming spec for: sidebar OBSERVE→MANAGE rename + Credits & Billing link + Hire-an-Expert footer button; new /billing page with extracted Dograh Model Credits card + CTAs; Top-up / Hire-an-Expert / Enterprise intake modals with inline math captcha; and a workflow-builder Hire-an-Expert nudge. Frontend only; submissions fire PostHog events via a submitLead() seam for a future MongoDB endpoint. Also gitignore .superpowers/ brainstorm mockups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: implementation plan for user-onboarding lead-gen surfaces 14 bite-sized tasks: PostHog events, shared helpers (field options, work-email blocklist, submitLead seam, math captcha), three intake modals (enterprise/hire/top-up), LeadFormsProvider context, AppLayout mount, sidebar MANAGE rename + Credits & Billing link + footer Hire button, extracted DograhCreditsCard, /billing page, credits removal from Agent Runs, builder nudge, and a full verification/dogfood pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): register PostHog events for lead-gen surfaces Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): shared field options, work-email validation, and submit seam Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): inline math captcha field Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): enterprise intake modal Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): hire-an-expert modal with enterprise link Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): top-up modal with >20k volume-pricing gate Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): shared lead-forms context provider Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): mount LeadFormsProvider in app layout Wrap the sidebar branch of AppLayout with LeadFormsProvider so the shared lead modals are available to the sidebar, billing card, and builder nudge. Includes eslint import-order auto-fixes in TopUpModal and LeadFormsContext. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): rename OBSERVE to MANAGE, add Credits & Billing link and Hire-an-Expert footer button Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): extract DograhCreditsCard with top-up + hire CTAs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): add Credits & Billing page Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(lead-gen): move Dograh Model Credits card out of Agent Runs to /billing Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(lead-gen): delayed Hire-an-Expert nudge on the workflow builder Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(ui): add lint:lead-flow guard script Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ui): restructure lead forms, self-serve Buy Credits, dialog blur Revised lead-capture surfaces and credits bar: - Dialog overlay gains backdrop blur (bg-black/60 backdrop-blur-sm). - Shared primitives: LeadModalShell (icon/eyebrow header, scrollable body, sticky footer, trust-line slot), PhoneField (react-international-phone, dark, E.164 out), FormTrustLine ("Average response: under 10 minutes..."). - HireExpertModal: Name, Company, Job title, agent goal, Phone (required), monthly volume. EnterpriseModal: + work email (required logged-out), conditional deployment (yes/no/maybe, source-gated), agent goal. OnboardingModal: drop useCase. Phone mandatory except onboarding. - Volume buckets match the backend qualifier (0-5k/5k-100k/100k+/not-sure). - Delete TopUpModal; DograhCreditsCard now self-serve Buy Credits (amount chips $5/$10/$25/$50/$100 + custom min $5 → startTopUp seam) + Hire an Expert + dashed custom-pricing link opening Enterprise (billing_custom_pricing). - PostHog events: drop topup_*, add buy_credits_clicked, buy_credits_amount_selected, custom_pricing_clicked. LeadFormsContext drops topup; LeadKind/LeadSource updated. - Introduce a single --cta warm accent token (CTAs + focus rings only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ui): split-screen auth + enterprise CTA + dark theme default - AuthShell: dark two-column auth layout (brand/value panel with CSS-only waveform motif + proof points + Bland-style enterprise CTA block on the left, zinc-900 form card on the right; single-column on mobile). - AuthEnterpriseCTA: "Talk to our team" → dograh.com/contact?intent=enterprise. - stack-theme: dark StackTheme token overrides synced to globals.css. - page.tsx: wrap StackHandler (non-fullPage) in AuthShell + StackTheme; local-auth fallback preserved inside the shell. BackButton slimmed for the card. - Dark locked as default: <html className="dark">, next-themes ThemeProvider (defaultTheme="dark", enableSystem=false); inline no-FOUC script defaults dark. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ui rezig, onboarding, billing, hire us & on prem cues * ui changes * chore: update comment * chore: untrack docs/superpowers and gitignore it * feat: refactor user configuration table * feat(ui): 'check your email' confirmation on lead forms Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * added email and country in form submissions * chore: update leads api * fix: wrap dograh model config in card --------- Co-authored-by: Pritesh <pritesh@dograh.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 19:49:33 +05:30
<div className="min-h-screen">
<div className="container mx-auto px-4 py-8 max-w-2xl">
<div className="mb-6">
<h1 className="text-3xl font-bold mb-2">Create Voice Agent</h1>
<p className="text-muted-foreground">
Tell us about your use case and we&apos;ll create a customized voice agent for you
</p>
</div>
<Card>
<CardHeader>
<CardTitle>Agent Details</CardTitle>
<CardDescription>
Configure your voice agent settings
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label htmlFor="call-type">Call Type</Label>
<Select value={callType} onValueChange={(value) => setCallType(value as 'inbound' | 'outbound')}>
<SelectTrigger id="call-type">
<SelectValue placeholder="Select type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="inbound">
Inbound (Users call AI)
</SelectItem>
<SelectItem value="outbound">
Outbound (AI calls users)
</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
Choose whether users will call your AI or your AI will call users
</p>
</div>
<div className="space-y-2">
<Label htmlFor="use-case">Use Case</Label>
<Input
id="use-case"
placeholder="e.g., Lead Qualification, HR Screening, Customer Support"
value={useCase}
onChange={(e) => setUseCase(e.target.value)}
/>
<p className="text-sm text-muted-foreground">
Describe the primary purpose of your voice agent
</p>
</div>
<div className="space-y-2">
<Label htmlFor="activity-description">Activity Description</Label>
<Textarea
id="activity-description"
placeholder="Describe briefly what your voice agent will do (e.g., Qualify leads for real estate, Screen candidates for roles, Handle customer support). This will be a prompt to an LLM."
value={activityDescription}
onChange={(e) => setActivityDescription(e.target.value)}
className="min-h-[100px]"
/>
<p className="text-sm text-muted-foreground">
This description will be used to generate the AI prompt for your voice agent
</p>
</div>
{error && (
<p className="text-sm text-red-500">{error}</p>
)}
<div className="pt-4">
<Button
onClick={handleCreateWorkflow}
disabled={isLoading || !useCase || !activityDescription}
className="w-full"
>
{isLoading ? 'Creating...' : 'Create Agent'}
</Button>
</div>
</CardContent>
</Card>
</div>
{/* Loading Overlay */}
{isLoading && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
<Card className="w-full max-w-md p-8">
<div className="flex flex-col items-center space-y-6">
{/* Animated spinner */}
<div className="relative">
<div className="w-16 h-16 border-4 border-muted rounded-full"></div>
<div className="absolute top-0 left-0 w-16 h-16 border-4 border-transparent border-t-primary rounded-full animate-spin"></div>
</div>
<div className="text-center space-y-2">
<h3 className="text-lg font-semibold">
Creating Your Workflow
</h3>
<p className="text-sm text-muted-foreground max-w-xs">
We&apos;re setting up your voice agent with your specifications. This will just take a moment...
</p>
</div>
</div>
</Card>
</div>
)}
{/* Success Modal */}
<Dialog open={showSuccessModal} onOpenChange={setShowSuccessModal}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Workflow Created Successfully!
</DialogTitle>
<DialogDescription asChild>
<div className="mt-4 space-y-3">
<p>
A voice agent workflow has been generated for your use case, with some artificial data and sample actions.
</p>
<p>
The voice bot is pre-set to communicate in English with an American accent.
</p>
<p>
Next steps would be to test the voice bot in the editor, and then modify it to suit your use case.
</p>
</div>
</DialogDescription>
</DialogHeader>
<DialogFooter className="mt-6">
<Button
onClick={handleModalContinue}
className="w-full"
>
Open and Test Agent
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}