Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const Footer: React.FC = () => {
</a>
</div>
</div>

<div>
<h4 className="text-lg font-semibold mb-6">{t('footer.services')}</h4>
<ul className="space-y-3">
Expand All @@ -75,7 +75,7 @@ const Footer: React.FC = () => {
</li>
</ul>
</div>

<div>
<h4 className="text-lg font-semibold mb-6">{t('footer.company')}</h4>
<ul className="space-y-3">
Expand All @@ -90,16 +90,13 @@ const Footer: React.FC = () => {
</li>
</ul>
</div>

<div>
<h4 className="text-lg font-semibold mb-6">{t('footer.contact')}</h4>
<ul className="space-y-4">
<li className="flex items-start">
<MapPin className="w-5 h-5 mr-3 text-accent" />
<span className="text-gray-400 whitespace-pre-line">
{t('footer.address')}
</span>

<span className="text-gray-400">{t('footer.address')}</span>
</li>
<li className="flex items-center">
<Phone className="w-5 h-5 mr-3 text-accent" />
Expand All @@ -114,7 +111,7 @@ const Footer: React.FC = () => {
</ul>
</div>
</div>

<div className="border-t border-gray-800 mt-12 pt-8 flex flex-col md:flex-row justify-between items-center">
<p className="text-gray-400 text-sm mb-4 md:mb-0">
&copy; {new Date().getFullYear()} GoBuild. All rights reserved.
Expand Down
171 changes: 171 additions & 0 deletions src/components/HeroArchitectForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React, { useState } from 'react';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Calendar } from '@/components/ui/calendar';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Calendar as CalendarIcon, Phone, MapPin } from 'lucide-react';
import { format } from 'date-fns';
import { useToast } from '@/hooks/use-toast';
import { supabase } from '@/integrations/supabase/client';

export function HeroArchitectForm() {
const [preferredDate, setPreferredDate] = useState<Date | undefined>();
const [dateOpen, setDateOpen] = useState(false);
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [projectType, setProjectType] = useState('');
const [location, setLocation] = useState('');
const [budget, setBudget] = useState('');
const [message, setMessage] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const { toast } = useToast();

const validate = () => {
if (!name.trim()) return 'Please enter your name';
if (!phone.trim() || phone.replace(/\D/g, '').length < 10) return 'Please enter a valid 10-digit phone number';
if (!projectType) return 'Please select a project type';
return null;
};

const handleSubmit = async () => {
const errorMsg = validate();
if (errorMsg) {
toast({ title: 'Error', description: errorMsg, variant: 'destructive' });
return;
}

if (isSubmitting) return;
setIsSubmitting(true);

try {
const payload: any = {
client_name: name.trim(),
phone: phone.trim(),
project_type: projectType,
preferred_date: preferredDate ? format(preferredDate, 'yyyy-MM-dd') : null,
location: location.trim() || null,
budget: budget ? Number(budget) : null,
message: message.trim() || null,
status: 'pending',
created_at: new Date().toISOString(),
};

const { error } = await supabase.from('ArchitectRequest').insert([payload]);

if (error) throw error;

toast({ title: 'Request sent', description: 'We will contact you soon.', });

// reset
setPreferredDate(undefined);
setName('');
setPhone('');
setProjectType('');
setLocation('');
setBudget('');
setMessage('');
} catch (err: any) {
console.error('Architect request error', err);
toast({ title: 'Error', description: err?.message || 'Failed to submit request', variant: 'destructive' });
} finally {
setIsSubmitting(false);
}
};

return (
<section id="hero-form" className="hero-pattern from-primary/5 to-background py-16">
<div className="container mx-auto px-4">
<h1 className="text-4xl font-bold mb-8 text-center">Book Architect/Designer Services</h1>

<div className="max-w-4xl mx-auto bg-white p-8 rounded-2xl shadow-2xl">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-4">
<div>
<label className="text-sm font-medium text-sky-900">Full name</label>
<Input value={name} onChange={(e) => setName(e.target.value)} placeholder="Your full name" />
</div>

<div>
<label className="text-sm font-medium text-sky-900">Phone</label>
<div className="relative">
<Phone className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500" />
<Input className="pl-10" value={phone} onChange={(e) => setPhone(e.target.value.replace(/\D/g, '').slice(0, 10))} placeholder="Phone number" />
</div>
{phone && phone.length < 10 && <p className="text-sm text-red-500">Enter 10 digits</p>}
</div>

<div>
<label className="text-sm font-medium text-sky-900">Project type</label>
<Select value={projectType} onValueChange={setProjectType}>
<SelectTrigger className="bg-white">
<SelectValue placeholder="Select project type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="residential">Residential</SelectItem>
<SelectItem value="commercial">Commercial</SelectItem>
<SelectItem value="interior">Interior Design</SelectItem>
<SelectItem value="landscape">Landscape</SelectItem>
<SelectItem value="renovation">Renovation/Remodel</SelectItem>
</SelectContent>
</Select>
</div>

<div>
<label className="text-sm font-medium text-sky-900">Preferred date</label>
<Popover open={dateOpen} onOpenChange={setDateOpen}>
<PopoverTrigger asChild>
<Button variant="outline" className={`w-full justify-start text-left font-normal bg-white ${!preferredDate ? 'text-muted-foreground' : ''}`} onClick={() => setDateOpen(!dateOpen)}>
<CalendarIcon className="mr-2 h-4 w-4" />
{preferredDate ? format(preferredDate, 'PPP') : <span>Select date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={preferredDate}
onSelect={(d) => { setPreferredDate(d); setDateOpen(false); }}
initialFocus
className="p-3"
disabled={(d) => d <= new Date(new Date().setHours(0, 0, 0, 0))}
/>
</PopoverContent>
</Popover>
</div>
</div>

<div className="space-y-4">
<div>
<label className="text-sm font-medium text-sky-900">Location</label>
<div className="relative">
<MapPin className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500" />
<Input className="pl-10" value={location} onChange={(e) => setLocation(e.target.value)} placeholder="Enter your address" />
</div>
</div>

<div>
<label className="text-sm font-medium text-sky-900">Estimated budget (optional)</label>
<Input value={budget} onChange={(e) => setBudget(e.target.value.replace(/[^0-9]/g, ''))} placeholder="Budget" />
</div>

<div>
<label className="text-sm font-medium text-sky-900">Message (optional)</label>
<Textarea className="h-32" value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Brief project details" />
</div>
</div>
</div>

<div className="mt-6">
<Button onClick={handleSubmit} className="w-full animate-pulse-shadow" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Request Architect'}
</Button>
</div>
</div>
</div>
</section>
);
}

export default HeroArchitectForm;
40 changes: 15 additions & 25 deletions src/components/HeroForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,40 +28,30 @@ const [isSubmitting, setIsSubmitting] = useState(false);

const locationList = [
'Katra, Jammu',
'Janipur, Jammu',
'Jaanipur, Jammu',
'High Court, Jammu',
'Satwari Chowk, Jammu',
'Gandhinagar, Jammu',
'Gangyal Industrial Area, Jammu',
'Ganghiyal Industrial Area, Jammu',
'Bahu Plaza, Jammu',
'Residency Road, Jammu',
'RaghuNath Nagar, Jammu',
'Bus Stand, Jammu',
'Bari Brahmana, Jammu',
'Sainik Colony, Jammu',
'Trikuta Nagar, jammu',
'Bhagwati Nagar, jammu',
'Shastri Nagar, jammu',
'Rehari, jammu',
'Talab Tillo, jammu',
'Muthi, jammu',
'Channi Himmat, jammu',
];

const locationCoords: any = {
'katra, jammu': { lat: 32.9917, lng: 74.9319 },
'Janipur, jammu': { lat: 32.7496, lng: 74.8373 },
'satwari chowk, jammu': { lat: 32.6923199, lng: 74.8462223 },
'gandhinagar, jammu': { lat: 32.7043905, lng: 74.8518208 },
'gangyal industrial area, jammu': { lat: 32.6722807, lng: 74.866613 },
'bahu plaza, jammu': { lat: 32.7038042, lng: 74.8698721 },
'residency Road, jammu': { lat: 32.7293127, lng: 74.8654757 },
'bari brahmana, jammu': { lat: 32.636539, lng: 74.9038354 },
'sainik colony, jammu': { lat: 32.6738936, lng: 74.8723597 },
'trikuta nagar, jammu': { lat: 32.6927306, lng: 74.8565729 },
'bhagwati nagar, jammu': { lat: 32.7277661, lng: 74.826981 },
'shastri nagar, jammu': { lat: 32.6931782, lng: 74.8514339 },
'rehari, jammu': { lat: 32.7478515, lng: 74.8463835 },
'talab tillo, jammu': { lat: 32.724354, lng: 74.8406902 },
'muthi, jammu': { lat: 32.7585189, lng: 74.8114887 },
'channi himmat, jammu': { lat: 32.6934058, lng: 74.873381 },
'jaanipur, jammu': { lat: 32.7496, lng: 74.8373 },
'high court, jammu': { lat: 32.7294, lng: 74.8648 },
'satwari chowk, jammu': { lat: 32.6887, lng: 74.8371 },
'gandhinagar, jammu': { lat: 32.7266, lng: 74.8570 },
'ganghiyal industrial area, jammu': { lat: 32.6749, lng: 74.8090 },
'bahu plaza, jammu': { lat: 32.7150, lng: 74.8600 },
'raghunath nagar, jammu': { lat: 32.7300, lng: 74.8605 },
'bus stand, jammu': { lat: 32.7357, lng: 74.8733 },
'bari brahmana, jammu': { lat: 32.6204, lng: 74.9072 },
'sainik colony, jammu': { lat: 32.6927, lng: 74.8905 },
};

const handleLocationSelect = (value: string) => {
Expand Down
87 changes: 42 additions & 45 deletions src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,56 +45,53 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
}
};

useEffect(() => {
// Set up the auth state listener first
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, currentSession) => {
setSession(currentSession);
setUser(currentSession?.user ?? null);

setTimeout(async()=>{
if (currentSession?.user) {
// Fetch user role when user is signed in
const role = await fetchUserRole(currentSession.user.id);
setUserRole(role);
} else {
// Clear user role when user is signed out
setUserRole(null);
}
// Handle events
if (event === 'SIGNED_IN') {
toast({
title: "Welcome back!",
description: "You have successfully signed in.",
});
} else if (event === 'SIGNED_OUT') {
toast({
title: "Signed out",
description: "You have been signed out successfully.",
});
}
},0);
}
);

// Then check for existing session
supabase.auth.getSession().then(async ({ data: { session: currentSession } }) => {
useEffect(() => {
let lastUserId = null;
let lastEvent = null;

const fetchAndSetUserRole = async (userId) => {
if (!userId || userId === lastUserId) return;
lastUserId = userId;

const role = await fetchUserRole(userId);
setUserRole(role);
};

supabase.auth.getSession().then(async ({ data: { session: currentSession } }) => {
setSession(currentSession);
setUser(currentSession?.user ?? null);

if (currentSession?.user) {
await fetchAndSetUserRole(currentSession.user.id);
}

setLoading(false);
});

const { data: { subscription } } = supabase.auth.onAuthStateChange(
async (event, currentSession) => {
setSession(currentSession);
setUser(currentSession?.user ?? null);


if (event === lastEvent) return;
lastEvent = event;

if (currentSession?.user) {
// Fetch user role for existing session
const role = await fetchUserRole(currentSession.user.id);
setUserRole(role);
await fetchAndSetUserRole(currentSession.user.id);
} else {
setUserRole(null);
}

setLoading(false);
});

return () => {
subscription.unsubscribe();
};
}, [navigate]);
if (event === 'SIGNED_IN') {
toast({ title: "Welcome back!", description: "You have successfully signed in." });
} else if (event === 'SIGNED_OUT') {
toast({ title: "Signed out", description: "You have been signed out successfully." });
}
}
);

return () => subscription.unsubscribe();
}, [navigate]);

const signIn = async (email: string, password: string) => {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"company": "Company",
"howItWorks": "How It Works",
"contact": "Contact",
"address": "House No - 251\nNear Chaddha Classes, Rehari Colony\nJammu & Kashmir"
"address": "H/No- 251, Front of Chaddha Classes,Rehari Colony,Jammu, Jammu & Kashmir"
},
"howItWorks": {
"title": "How GoBuild Works",
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ContactUs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const ContactUs: React.FC = () => {
<div>
<h3 className="font-medium text-lg">{t('contactUs.contactInfo.address')}</h3>
<p className="text-muted-foreground">
House No - 251 <br></br> Near Chaddha Classes, Rehari Colony <br></br>
H/No- 251, Front of Chaddha Classes,Rehari Colony,<br></br>
Jammu & Kashmir
</p>
</div>
Expand Down
Loading