From 2aac4bf2cf666e2b92c67b4801748623b69862fd Mon Sep 17 00:00:00 2001 From: JamiltonQuintero Date: Fri, 13 Sep 2024 17:16:49 -0500 Subject: [PATCH] Se agrega logica para cobros --- .gitignore | 1 + package.json | 1 + src/app/(routes)/interview/page.tsx | 712 +++--------------- src/app/(routes)/suscriptions/page.tsx | 170 +++++ src/app/api/webhooks/lemonsqueezy/route.ts | 104 +++ .../custom/interview/video-call/dialog.tsx | 198 +++++ .../video-call/interviewerListProps .tsx | 50 ++ .../custom/interview/video-call/videoCall.tsx | 193 +++++ src/components/ui/switch.tsx | 29 + .../application/service/userManagerService.ts | 10 + .../interview_manager/domain/model/user.ts | 5 + .../domain/port/userRepositoryPort.ts | 5 + .../adapter/userManagerRepositoryAdapter.ts | 94 +++ 13 files changed, 945 insertions(+), 627 deletions(-) create mode 100644 src/app/(routes)/suscriptions/page.tsx create mode 100644 src/app/api/webhooks/lemonsqueezy/route.ts create mode 100644 src/components/custom/interview/video-call/dialog.tsx create mode 100644 src/components/custom/interview/video-call/interviewerListProps .tsx create mode 100644 src/components/custom/interview/video-call/videoCall.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/modules/interview_manager/application/service/userManagerService.ts create mode 100644 src/modules/interview_manager/domain/model/user.ts create mode 100644 src/modules/interview_manager/domain/port/userRepositoryPort.ts create mode 100644 src/modules/interview_manager/infrastructure/adapter/userManagerRepositoryAdapter.ts diff --git a/.gitignore b/.gitignore index b7dc12f..c1ef091 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ next-env.d.ts package-lock.json /*.yaml /*.env +/.env.development diff --git a/package.json b/package.json index 013b3a1..dbf3d68 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "class-variance-authority": "^0.7.0", diff --git a/src/app/(routes)/interview/page.tsx b/src/app/(routes)/interview/page.tsx index 663e40d..34a303a 100644 --- a/src/app/(routes)/interview/page.tsx +++ b/src/app/(routes)/interview/page.tsx @@ -1,640 +1,98 @@ 'use client' -import { useState, useRef, useEffect } from 'react' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { Textarea } from '@/components/ui/textarea' -import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card' -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' -import { Badge } from '@/components/ui/badge' -import { Separator } from '@/components/ui/separator' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { ScrollArea } from '@/components/ui/scroll-area' -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' -import { - Loader2, - CheckCircle, - AlertCircle, - ArrowRight, - Upload, - Send, - Link as LinkIcon, - Mic, - Video, - ArrowLeft -} from 'lucide-react' - -interface WorkExperience { - company: string - position: string - duration: string - highlights: string[] -} - -interface Language { - name: string - proficiency: string -} - -interface DeveloperAnalysis { - name: string - technicalSkills: string[] - mainTechnologies: string[] - workExperiences: WorkExperience[] - languages: Language[] - education: string - certifications: string[] - softSkills: string[] -} - -interface JobOffer { - title: string - profile: string - seniority: string - description: string - companyName: string - country: string -} - -interface Message { - role: 'user' | 'ai' - content: string -} - -const mockDeveloperAIAnalysis = (): DeveloperAnalysis => { - return { - name: 'Alex Johnson', - technicalSkills: ['JavaScript', 'React', 'Node.js', 'Python', 'SQL', 'Git'], - mainTechnologies: ['React', 'Node.js', 'Express', 'MongoDB'], - workExperiences: [ - { - company: 'Tech Innovators Inc.', - position: 'Senior Frontend Developer', - duration: '2019 - Present', - highlights: [ - "Led a team of 5 developers in redesigning the company's main product UI", - 'Implemented CI/CD pipeline, reducing deployment time by 40%', - 'Mentored junior developers and conducted code reviews' - ] - }, - { - company: 'DataSoft Solutions', - position: 'Full Stack Developer', - duration: '2016 - 2019', - highlights: [ - 'Developed and maintained multiple client-facing web applications', - 'Optimized database queries, improving application performance by 30%', - 'Collaborated with UX team to implement responsive designs' - ] - } - ], - languages: [ - { name: 'English', proficiency: 'Native' }, - { name: 'Spanish', proficiency: 'Intermediate' } - ], - education: 'B.S. in Computer Science, Tech University (2016)', - certifications: ['AWS Certified Developer', 'MongoDB Certified Developer'], - softSkills: ['Team leadership', 'Problem-solving', 'Communication', 'Agile methodologies'] - } -} - -const mockGetAIResponse = async (message: string): Promise => { - await new Promise(resolve => setTimeout(resolve, 1000)) - return `This is a mock AI response to: "${message}". In a real implementation, this would be an actual response from an AI model based on the CV analysis and job offer details.` -} - -export default function AIInterviewProcess() { - const [file, setFile] = useState(null) - const [analysis, setAnalysis] = useState(null) - const [isLoading, setIsLoading] = useState(false) - const [error, setError] = useState(null) - const [step, setStep] = useState(1) - const [jobOffer, setJobOffer] = useState({ - title: '', - profile: '', - seniority: '', - description: '', - companyName: '', - country: '' - }) - const [jobOfferLink, setJobOfferLink] = useState('') - const [messages, setMessages] = useState([]) - const [input, setInput] = useState('') - const messagesEndRef = useRef(null) - const [isListening, setIsListening] = useState(false) - const [transcript, setTranscript] = useState('') - const [animationKey, setAnimationKey] = useState(0) - - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) - } - - useEffect(scrollToBottom, [messages]) - - const handleFileChange = (event: React.ChangeEvent) => { - if (event.target.files && event.target.files[0]) { - setFile(event.target.files[0]) - setAnalysis(null) - setError(null) - } - } - - const handleAnalyze = async () => { - if (!file) { - setError('Please upload a CV file first.') - return - } - - setIsLoading(true) - setError(null) - +import { useState } from 'react' +import { useRouter } from 'next/navigation' +//import { loadStripe } from '@stripe/stripe-js' +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Check } from "lucide-react" + +const plans = [ + { + name: "Básico", + price: "9.99", + description: "Perfecto para empezar", + features: ["Acceso básico", "Soporte por email", "1 usuario"], + badge: "Popular", + priceId: "price_1234567890" // Reemplaza con tu Stripe Price ID real + }, + { + name: "Pro", + price: "19.99", + description: "Ideal para equipos pequeños", + features: ["Acceso completo", "Soporte prioritario", "5 usuarios"], + priceId: "price_0987654321" // Reemplaza con tu Stripe Price ID real + }, + { + name: "Empresa", + price: "49.99", + description: "Para grandes organizaciones", + features: ["Acceso completo", "Soporte 24/7", "Usuarios ilimitados"], + priceId: "price_1122334455" // Reemplaza con tu Stripe Price ID real + } +] + +export default function PricingPage() { + const [loading, setLoading] = useState(false) + const router = useRouter() + + const handleSubscription = async (priceId: string) => { + setLoading(true) try { - const result = mockDeveloperAIAnalysis() - setAnalysis(result) - setStep(2) - setAnimationKey(prev => prev + 1) - } catch (err) { - setError('An error occurred during analysis. Please try again.') - } finally { - setIsLoading(false) - } - } - - const handleContinue = () => { - setStep(step + 1) - setAnimationKey(prev => prev + 1) - } - - const handleJobOfferChange = (e: React.ChangeEvent) => { - const { name, value } = e.target - setJobOffer(prev => ({ ...prev, [name]: value })) - } - - const handleSeniorityChange = (value: string) => { - setJobOffer(prev => ({ ...prev, seniority: value })) - } + const response = await fetch('/api/create-checkout-session', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ priceId }), + }) + const { sessionId } = await response.json() - const handleJobOfferSubmit = () => { - if ( - jobOffer.title && - jobOffer.profile && - jobOffer.seniority && - jobOffer.description && - jobOffer.companyName && - jobOffer.country - ) { - console.log('Job offer submitted:', jobOffer) - setStep(4) - setAnimationKey(prev => prev + 1) - initializeChat() - } else { - setError('Please fill in all fields for the job offer.') - } - } - - const handleJobOfferLinkChange = (e: React.ChangeEvent) => { - setJobOfferLink(e.target.value) - } - - const handleJobOfferLinkSubmit = async () => { - if (!jobOfferLink) { - setError('Please enter a job offer link.') - return - } - - setIsLoading(true) - setError(null) - - try { - // Simulating fetching job offer from link - const result: JobOffer = { - title: 'Senior Frontend Developer', - profile: 'Experienced React developer', - seniority: 'senior', - description: 'We are looking for a senior frontend developer with strong React skills...', - companyName: 'Tech Innovators Inc.', - country: 'United States' - } - setJobOffer(result) - setStep(4) - setAnimationKey(prev => prev + 1) - initializeChat() - } catch (err) { - setError('An error occurred while fetching the job offer. Please try again.') - } finally { - setIsLoading(false) - } - } - - const initializeChat = () => { - setMessages([ - { - role: 'ai', - content: `Hello! I'm your AI interviewer. Based on your CV and the job offer for ${jobOffer.title} at ${jobOffer.companyName}, let's start the interview. What made you interested in this position?` - } - ]) - } - - const handleSend = async () => { - if (input.trim() === '') return - - const userMessage: Message = { role: 'user', content: input } - setMessages(prev => [...prev, userMessage]) - setInput('') - setIsLoading(true) - - try { - const aiResponse = await mockGetAIResponse(input) - const aiMessage: Message = { role: 'ai', content: aiResponse } - setMessages(prev => [...prev, aiMessage]) } catch (error) { - console.error('Error getting AI response:', error) - const errorMessage: Message = { - role: 'ai', - content: "I'm sorry, I encountered an error. Can you please try again?" - } - setMessages(prev => [...prev, errorMessage]) + console.error('Error:', error) } finally { - setIsLoading(false) + setLoading(false) } } - const handleKeyPress = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault() - handleSend() - } - } - - const toggleListening = () => { - if (!isListening) { - setStep(5) - setAnimationKey(prev => prev + 1) - setIsListening(true) - // Simulating speech recognition - setTimeout(() => { - setTranscript( - 'This is a simulated voice transcript. In a real implementation, this would be the result of speech recognition.' - ) - setIsListening(false) - }, 5000) - } else { - setIsListening(false) - } - } - - const handleVideoCall = () => { - setStep(6) - setAnimationKey(prev => prev + 1) - } - - const handleBackToChat = () => { - setStep(4) - setAnimationKey(prev => prev + 1) - setTranscript('') - } - return ( -
- - - - AI Interview Process - - -
- {step === 1 && ( -
-

Upload Your CV

-
- - -
-
- )} - - {error && ( - - - Error - {error} - - )} - - {step === 2 && analysis && ( -
- - - Analysis Complete - Here's the detailed analysis of your CV: - - -
-

{analysis.name}

-

{analysis.education}

-
- -
-

Technical Skills

-
- {analysis.technicalSkills.map((skill, index) => ( - - {skill} - - ))} -
-
- -
-

Main Technologies

-
- {analysis.mainTechnologies.map((tech, index) => ( - - {tech} - - ))} -
-
- -
-

Work Experience

- {analysis.workExperiences.map((exp, index) => ( -
-
{exp.position}
-

- {exp.company} | {exp.duration} -

-
    - {exp.highlights.map((highlight, hIndex) => ( -
  • - {highlight} -
  • - ))} -
-
- ))} -
- -
-

Languages

- {analysis.languages.map((lang, index) => ( -

- {lang.name}: {lang.proficiency} -

- ))} -
- - - -
-

Additional Information

-
-
-
Certifications
-
    - {analysis.certifications.map((cert, index) => ( -
  • - {cert} -
  • - ))} -
-
-
-
Soft Skills
-
    - {analysis.softSkills.map((skill, index) => ( -
  • - {skill} -
  • - ))} -
-
-
-
-
- )} - - {step === 3 && ( - - - Manual Entry - Job Offer Link - - -

Enter Job Offer Details

-
- -
-
- -
-
- -
-
-