export base44 - Nov 18

This commit is contained in:
bwnyasse
2025-11-18 21:32:16 -05:00
parent f7c2027065
commit d26bcaeed2
67 changed files with 13716 additions and 8102 deletions

View File

@@ -1,31 +1,38 @@
import React, { useState } from "react";
import React from "react";
import { base44 } from "@/api/base44Client";
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";
import { useNavigate } from "react-router-dom";
import { createPageUrl } from "@/utils";
import EventFormWizard from "@/components/events/EventFormWizard";
import AIOrderAssistant from "@/components/events/AIOrderAssistant";
import { useToast } from "@/components/ui/use-toast";
import { Button } from "@/components/ui/button";
import { Sparkles, FileText, X } from "lucide-react";
import { motion, AnimatePresence } from "framer-motion";
import { X, AlertTriangle } from "lucide-react";
import { detectAllConflicts, ConflictAlert } from "@/components/scheduling/ConflictDetection";
import { Card, CardContent } from "@/components/ui/card";
export default function CreateEvent() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { toast } = useToast();
const [useAI, setUseAI] = useState(false);
const [aiExtractedData, setAiExtractedData] = useState(null);
const [pendingEvent, setPendingEvent] = React.useState(null);
const [showConflictWarning, setShowConflictWarning] = React.useState(false);
const { data: currentUser } = useQuery({
queryKey: ['current-user-create-event'],
queryFn: () => base44.auth.me(),
});
const { data: allEvents = [] } = useQuery({
queryKey: ['events-for-conflict-check'],
queryFn: () => base44.entities.Event.list(),
initialData: [],
});
const createEventMutation = useMutation({
mutationFn: (eventData) => base44.entities.Event.create(eventData),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['events'] });
queryClient.invalidateQueries({ queryKey: ['client-events'] });
toast({
title: "✅ Event Created",
description: "Your event has been created successfully.",
@@ -42,107 +49,98 @@ export default function CreateEvent() {
});
const handleSubmit = (eventData) => {
createEventMutation.mutate(eventData);
// Detect conflicts before creating
const conflicts = detectAllConflicts(eventData, allEvents);
if (conflicts.length > 0) {
setPendingEvent({ ...eventData, detected_conflicts: conflicts });
setShowConflictWarning(true);
} else {
createEventMutation.mutate(eventData);
}
};
const handleAIDataExtracted = (extractedData) => {
setAiExtractedData(extractedData);
setUseAI(false);
const handleConfirmWithConflicts = () => {
if (pendingEvent) {
createEventMutation.mutate(pendingEvent);
setShowConflictWarning(false);
setPendingEvent(null);
}
};
const handleCancelConflicts = () => {
setShowConflictWarning(false);
setPendingEvent(null);
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100">
<div className="max-w-7xl mx-auto p-4 md:p-8">
{/* Header with AI Toggle */}
{/* Header */}
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-3xl font-bold text-[#1C323E]">Create New Order</h1>
<h1 className="text-3xl font-bold text-[#1C323E]">Create Standard Order</h1>
<p className="text-slate-600 mt-1">
{useAI ? "Use AI to create your order naturally" : "Fill out the form to create your order"}
Fill out the details for your planned event
</p>
</div>
<div className="flex gap-2">
<Button
variant={useAI ? "default" : "outline"}
onClick={() => setUseAI(true)}
className={useAI ? "bg-gradient-to-r from-[#0A39DF] to-purple-600" : ""}
>
<Sparkles className="w-4 h-4 mr-2" />
AI Assistant
</Button>
<Button
variant={!useAI ? "default" : "outline"}
onClick={() => setUseAI(false)}
className={!useAI ? "bg-[#1C323E]" : ""}
>
<FileText className="w-4 h-4 mr-2" />
Form
</Button>
<Button
variant="ghost"
onClick={() => navigate(createPageUrl("Events"))}
>
<X className="w-4 h-4" />
</Button>
</div>
<Button
variant="ghost"
onClick={() => navigate(createPageUrl("ClientDashboard"))}
>
<X className="w-4 h-4" />
</Button>
</div>
{/* AI Assistant Interface */}
<AnimatePresence>
{useAI && (
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
>
<AIOrderAssistant
onOrderDataExtracted={handleAIDataExtracted}
onClose={() => setUseAI(false)}
/>
</motion.div>
)}
</AnimatePresence>
{/* Wizard Form */}
{!useAI && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
{aiExtractedData && (
<div className="mb-6 p-4 bg-green-50 border border-green-200 rounded-xl">
<div className="flex items-center gap-2 mb-2">
<Sparkles className="w-5 h-5 text-green-600" />
<span className="font-semibold text-green-900">AI Pre-filled Data</span>
{/* Conflict Warning Modal */}
{showConflictWarning && pendingEvent && (
<Card className="mb-6 border-2 border-orange-500">
<CardContent className="p-6">
<div className="flex items-start gap-4 mb-4">
<div className="w-12 h-12 bg-orange-100 rounded-full flex items-center justify-center flex-shrink-0">
<AlertTriangle className="w-6 h-6 text-orange-600" />
</div>
<p className="text-sm text-green-700 mb-3">
The form has been pre-filled with information from your conversation. Review and edit as needed.
</p>
<div>
<h3 className="font-bold text-lg text-slate-900 mb-1">
Scheduling Conflicts Detected
</h3>
<p className="text-sm text-slate-600">
This event has {pendingEvent.detected_conflicts.length} potential conflict{pendingEvent.detected_conflicts.length !== 1 ? 's' : ''}
with existing bookings. Review the conflicts below and decide how to proceed.
</p>
</div>
</div>
<div className="mb-6">
<ConflictAlert conflicts={pendingEvent.detected_conflicts} />
</div>
<div className="flex gap-3 justify-end">
<Button
variant="outline"
size="sm"
onClick={() => {
setAiExtractedData(null);
setUseAI(true);
}}
className="border-green-300 text-green-700 hover:bg-green-100"
onClick={handleCancelConflicts}
>
<Sparkles className="w-4 h-4 mr-2" />
Chat with AI Again
Go Back & Edit
</Button>
<Button
onClick={handleConfirmWithConflicts}
className="bg-orange-600 hover:bg-orange-700"
>
Create Anyway
</Button>
</div>
)}
<EventFormWizard
event={aiExtractedData}
onSubmit={handleSubmit}
isSubmitting={createEventMutation.isPending}
currentUser={currentUser}
onCancel={() => navigate(createPageUrl("Events"))}
/>
</motion.div>
</CardContent>
</Card>
)}
<EventFormWizard
event={null}
onSubmit={handleSubmit}
isSubmitting={createEventMutation.isPending}
currentUser={currentUser}
onCancel={() => navigate(createPageUrl("ClientDashboard"))}
/>
</div>
</div>
);