Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 991029a66a | |||
| 5d680badc5 | |||
| 3040ee7707 | |||
| 9a3f4d6c2c | |||
| 1a1ca65727 | |||
| e779a2f06d | |||
| de509fb6f9 | |||
| fd1d1351d5 | |||
| d9cca644e1 | |||
| 83a1fc5e15 | |||
| 6e3dc7d215 | |||
| 4cd5edc456 | |||
| 03c0beff67 | |||
| 86262a145e | |||
| 53cbc550eb | |||
| 283cbc8441 | |||
| 8e4a6f0594 | |||
| 7813c4d12c | |||
| f46a8dcd39 | |||
| d86585a83e | |||
| 7d9e0f70a9 |
@@ -1,286 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleFullscreen from "@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen";
|
||||
import { useState, useMemo } from "react";
|
||||
import { Search, Filter, ChevronUp, ChevronDown } from "lucide-react";
|
||||
|
||||
const navItems = [
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Membership", id: "/membership" },
|
||||
{ name: "Services", id: "#services" },
|
||||
{ name: "Dashboard", id: "/dashboard" },
|
||||
];
|
||||
|
||||
interface ServiceRequest {
|
||||
requestId: string;
|
||||
clientName: string;
|
||||
address: string;
|
||||
issueType: string;
|
||||
membershipPlan: string;
|
||||
status: "pending" | "in-progress" | "completed" | "cancelled";
|
||||
date: string;
|
||||
}
|
||||
|
||||
const mockRequests: ServiceRequest[] = [
|
||||
{
|
||||
requestId: "SR-001", clientName: "John Smith", address: "123 Oak Avenue, Springfield", issueType: "Plumbing", membershipPlan: "Premium", status: "completed", date: "2025-01-15"},
|
||||
{
|
||||
requestId: "SR-002", clientName: "Sarah Johnson", address: "456 Elm Street, Shelbyville", issueType: "HVAC", membershipPlan: "Standard", status: "in-progress", date: "2025-01-18"},
|
||||
{
|
||||
requestId: "SR-003", clientName: "Michael Davis", address: "789 Maple Drive, Capital City", issueType: "Electrical", membershipPlan: "Elite", status: "pending", date: "2025-01-20"},
|
||||
{
|
||||
requestId: "SR-004", clientName: "Emily Wilson", address: "321 Pine Road, Shelbyville", issueType: "Roofing", membershipPlan: "Basic", status: "completed", date: "2025-01-10"},
|
||||
{
|
||||
requestId: "SR-005", clientName: "David Brown", address: "654 Cedar Lane, Springfield", issueType: "General Maintenance", membershipPlan: "Premium", status: "in-progress", date: "2025-01-19"},
|
||||
{
|
||||
requestId: "SR-006", clientName: "Jessica Martinez", address: "987 Birch Court, Capital City", issueType: "Emergency Repair", membershipPlan: "Elite", status: "pending", date: "2025-01-20"},
|
||||
];
|
||||
|
||||
type SortField = "requestId" | "clientName" | "address" | "issueType" | "membershipPlan" | "status" | "date";
|
||||
type SortOrder = "asc" | "desc";
|
||||
|
||||
const getStatusColor = (
|
||||
status: "pending" | "in-progress" | "completed" | "cancelled"
|
||||
): string => {
|
||||
switch (status) {
|
||||
case "pending":
|
||||
return "bg-yellow-100 text-yellow-800";
|
||||
case "in-progress":
|
||||
return "bg-blue-100 text-blue-800";
|
||||
case "completed":
|
||||
return "bg-green-100 text-green-800";
|
||||
case "cancelled":
|
||||
return "bg-red-100 text-red-800";
|
||||
default:
|
||||
return "bg-gray-100 text-gray-800";
|
||||
}
|
||||
};
|
||||
|
||||
export default function DashboardPage() {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [sortField, setSortField] = useState<SortField>("date");
|
||||
const [sortOrder, setSortOrder] = useState<SortOrder>("desc");
|
||||
const [statusFilter, setStatusFilter] = useState<string>("all");
|
||||
|
||||
const filteredAndSortedRequests = useMemo(() => {
|
||||
let filtered = mockRequests;
|
||||
|
||||
// Search filter
|
||||
if (searchQuery) {
|
||||
const query = searchQuery.toLowerCase();
|
||||
filtered = filtered.filter(
|
||||
(req) =>
|
||||
req.requestId.toLowerCase().includes(query) ||
|
||||
req.clientName.toLowerCase().includes(query) ||
|
||||
req.address.toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (statusFilter !== "all") {
|
||||
filtered = filtered.filter((req) => req.status === statusFilter);
|
||||
}
|
||||
|
||||
// Sort
|
||||
filtered.sort((a, b) => {
|
||||
const aVal = a[sortField] || "";
|
||||
const bVal = b[sortField] || "";
|
||||
|
||||
if (aVal < bVal) return sortOrder === "asc" ? -1 : 1;
|
||||
if (aVal > bVal) return sortOrder === "asc" ? 1 : -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}, [searchQuery, sortField, sortOrder, statusFilter]);
|
||||
|
||||
const toggleSort = (field: SortField) => {
|
||||
if (sortField === field) {
|
||||
setSortOrder(sortOrder === "asc" ? "desc" : "asc");
|
||||
} else {
|
||||
setSortField(field);
|
||||
setSortOrder("asc");
|
||||
}
|
||||
};
|
||||
|
||||
const SortIcon = ({ field }: { field: SortField }) => {
|
||||
if (sortField !== field) return null;
|
||||
return sortOrder === "asc" ? (
|
||||
<ChevronUp className="w-4 h-4 inline ml-1" />
|
||||
) : (
|
||||
<ChevronDown className="w-4 h-4 inline ml-1" />
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="hover-magnetic"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="circleGradient"
|
||||
cardStyle="glass-elevated"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<NavbarStyleFullscreen
|
||||
navItems={navItems}
|
||||
brandName="OW HomeShield"
|
||||
bottomLeftText="24/7 Support"
|
||||
bottomRightText="support@owhomeshield.com"
|
||||
/>
|
||||
|
||||
<div className="min-h-screen pt-20 pb-20 px-4">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<h1 className="text-4xl font-bold mb-4">Service Requests Dashboard</h1>
|
||||
<p className="text-foreground/70 mb-8">
|
||||
Manage and track all service requests from clients.
|
||||
</p>
|
||||
|
||||
{/* Filters and Search */}
|
||||
<div className="bg-card rounded-lg p-6 border border-foreground/10 mb-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{/* Search */}
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-3 w-5 h-5 text-foreground/40" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search by ID, name, or address..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Status Filter */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Filter className="w-5 h-5 text-foreground/40" />
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value)}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
>
|
||||
<option value="all">All Statuses</option>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="in-progress">In Progress</option>
|
||||
<option value="completed">Completed</option>
|
||||
<option value="cancelled">Cancelled</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Results Count */}
|
||||
<div className="flex items-center justify-end">
|
||||
<span className="text-sm text-foreground/70">
|
||||
{filteredAndSortedRequests.length} requests
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-card rounded-lg border border-foreground/10 overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-foreground/10 bg-background/50">
|
||||
<th className="px-6 py-4 text-left">
|
||||
<button
|
||||
onClick={() => toggleSort("requestId")}
|
||||
className="font-semibold text-sm hover:text-primary-cta transition-colors flex items-center"
|
||||
>
|
||||
Request ID
|
||||
<SortIcon field="requestId" />
|
||||
</button>
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left">
|
||||
<button
|
||||
onClick={() => toggleSort("clientName")}
|
||||
className="font-semibold text-sm hover:text-primary-cta transition-colors flex items-center"
|
||||
>
|
||||
Client Name
|
||||
<SortIcon field="clientName" />
|
||||
</button>
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left">
|
||||
<button
|
||||
onClick={() => toggleSort("address")}
|
||||
className="font-semibold text-sm hover:text-primary-cta transition-colors flex items-center"
|
||||
>
|
||||
Address
|
||||
<SortIcon field="address" />
|
||||
</button>
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left">
|
||||
<button
|
||||
onClick={() => toggleSort("issueType")}
|
||||
className="font-semibold text-sm hover:text-primary-cta transition-colors flex items-center"
|
||||
>
|
||||
Issue Type
|
||||
<SortIcon field="issueType" />
|
||||
</button>
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left">
|
||||
<button
|
||||
onClick={() => toggleSort("membershipPlan")}
|
||||
className="font-semibold text-sm hover:text-primary-cta transition-colors flex items-center"
|
||||
>
|
||||
Membership Plan
|
||||
<SortIcon field="membershipPlan" />
|
||||
</button>
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left">
|
||||
<button
|
||||
onClick={() => toggleSort("status")}
|
||||
className="font-semibold text-sm hover:text-primary-cta transition-colors flex items-center"
|
||||
>
|
||||
Status
|
||||
<SortIcon field="status" />
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredAndSortedRequests.length > 0 ? (
|
||||
filteredAndSortedRequests.map((request) => (
|
||||
<tr
|
||||
key={request.requestId}
|
||||
className="border-b border-foreground/10 hover:bg-background/50 transition-colors"
|
||||
>
|
||||
<td className="px-6 py-4 text-sm font-medium">
|
||||
{request.requestId}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm">{request.clientName}</td>
|
||||
<td className="px-6 py-4 text-sm text-foreground/80">
|
||||
{request.address}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm">{request.issueType}</td>
|
||||
<td className="px-6 py-4 text-sm">{request.membershipPlan}</td>
|
||||
<td className="px-6 py-4 text-sm">
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||
getStatusColor(request.status)
|
||||
}`}
|
||||
>
|
||||
{request.status.charAt(0).toUpperCase() +
|
||||
request.status.slice(1).replace("-", " ")}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-6 py-12 text-center text-foreground/50">
|
||||
No service requests found.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleFullscreen from "@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen";
|
||||
import { useState } from "react";
|
||||
import { Upload } from "lucide-react";
|
||||
|
||||
const navItems = [
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Membership", id: "/membership" },
|
||||
{ name: "Services", id: "#services" },
|
||||
{ name: "Dashboard", id: "/dashboard" },
|
||||
];
|
||||
|
||||
interface MembershipFormData {
|
||||
fullName: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
homeAddress: string;
|
||||
propertyType: string;
|
||||
serviceDescription: string;
|
||||
photoFile: File | null;
|
||||
}
|
||||
|
||||
export default function MembershipPage() {
|
||||
const [formData, setFormData] = useState<MembershipFormData>({
|
||||
fullName: "", phone: "", email: "", homeAddress: "", propertyType: "", serviceDescription: "", photoFile: null,
|
||||
});
|
||||
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
>
|
||||
) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
photoFile: e.target.files![0],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
console.log("Form submitted:", formData);
|
||||
setSubmitted(true);
|
||||
setTimeout(() => {
|
||||
setFormData({
|
||||
fullName: "", phone: "", email: "", homeAddress: "", propertyType: "", serviceDescription: "", photoFile: null,
|
||||
});
|
||||
setSubmitted(false);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="hover-magnetic"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="circleGradient"
|
||||
cardStyle="glass-elevated"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<NavbarStyleFullscreen
|
||||
navItems={navItems}
|
||||
brandName="OW HomeShield"
|
||||
bottomLeftText="24/7 Support"
|
||||
bottomRightText="support@owhomeshield.com"
|
||||
/>
|
||||
|
||||
<div className="min-h-screen pt-20 pb-20 px-4">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<h1 className="text-4xl font-bold mb-4 text-center">
|
||||
New Member Signup
|
||||
</h1>
|
||||
<p className="text-center text-foreground/70 mb-12">
|
||||
Join OW HomeShield and get professional home maintenance and repair
|
||||
services.
|
||||
</p>
|
||||
|
||||
<div className="bg-card rounded-lg p-8 border border-foreground/10">
|
||||
{submitted ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="text-5xl mb-4">✓</div>
|
||||
<h2 className="text-2xl font-bold mb-2">
|
||||
Thank you for signing up!
|
||||
</h2>
|
||||
<p className="text-foreground/70">
|
||||
We'll review your information and contact you shortly.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Full Name */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Full Name *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="fullName"
|
||||
value={formData.fullName}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Phone */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Phone Number *
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="(555) 123-4567"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Email Address *
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="john@example.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Home Address */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Home Address *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="homeAddress"
|
||||
value={formData.homeAddress}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="123 Main Street, City, State 12345"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Property Type */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Property Type *
|
||||
</label>
|
||||
<select
|
||||
name="propertyType"
|
||||
value={formData.propertyType}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
>
|
||||
<option value="">Select a property type</option>
|
||||
<option value="single-family">Single Family Home</option>
|
||||
<option value="condo">Condo/Townhouse</option>
|
||||
<option value="apartment">Apartment</option>
|
||||
<option value="multi-family">Multi-Family</option>
|
||||
<option value="commercial">Commercial</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Service Description */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Service Description *
|
||||
</label>
|
||||
<textarea
|
||||
name="serviceDescription"
|
||||
value={formData.serviceDescription}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
rows={4}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="Describe the services you need..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Photo Upload */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Upload Photo (Optional)
|
||||
</label>
|
||||
<div className="border-2 border-dashed border-foreground/20 rounded-lg p-6 text-center hover:border-primary-cta transition-colors">
|
||||
<input
|
||||
type="file"
|
||||
name="photo"
|
||||
onChange={handleFileChange}
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
id="photo-upload"
|
||||
/>
|
||||
<label
|
||||
htmlFor="photo-upload"
|
||||
className="cursor-pointer flex flex-col items-center gap-2"
|
||||
>
|
||||
<Upload className="w-8 h-8 text-primary-cta" />
|
||||
<span className="text-sm font-medium">
|
||||
{formData.photoFile
|
||||
? formData.photoFile.name
|
||||
: "Click to upload or drag and drop"}
|
||||
</span>
|
||||
<span className="text-xs text-foreground/50">
|
||||
PNG, JPG, GIF up to 10MB
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-primary-cta text-white font-medium py-3 rounded-lg hover:opacity-90 transition-opacity"
|
||||
>
|
||||
Submit Application
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleFullscreen from "@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen";
|
||||
import { useState } from "react";
|
||||
import { Upload } from "lucide-react";
|
||||
|
||||
const navItems = [
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Membership", id: "/membership" },
|
||||
{ name: "Services", id: "#services" },
|
||||
{ name: "Dashboard", id: "/dashboard" },
|
||||
];
|
||||
|
||||
interface ServiceRequestFormData {
|
||||
memberName: string;
|
||||
phone: string;
|
||||
address: string;
|
||||
membershipPlan: string;
|
||||
issueType: string;
|
||||
description: string;
|
||||
photoFile: File | null;
|
||||
}
|
||||
|
||||
export default function ServiceRequestPage() {
|
||||
const [formData, setFormData] = useState<ServiceRequestFormData>({
|
||||
memberName: "", phone: "", address: "", membershipPlan: "", issueType: "", description: "", photoFile: null,
|
||||
});
|
||||
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<
|
||||
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
>
|
||||
) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
photoFile: e.target.files![0],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
console.log("Service request submitted:", formData);
|
||||
setSubmitted(true);
|
||||
setTimeout(() => {
|
||||
setFormData({
|
||||
memberName: "", phone: "", address: "", membershipPlan: "", issueType: "", description: "", photoFile: null,
|
||||
});
|
||||
setSubmitted(false);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="hover-magnetic"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="rounded"
|
||||
contentWidth="medium"
|
||||
sizing="medium"
|
||||
background="circleGradient"
|
||||
cardStyle="glass-elevated"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<NavbarStyleFullscreen
|
||||
navItems={navItems}
|
||||
brandName="OW HomeShield"
|
||||
bottomLeftText="24/7 Support"
|
||||
bottomRightText="support@owhomeshield.com"
|
||||
/>
|
||||
|
||||
<div className="min-h-screen pt-20 pb-20 px-4">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<h1 className="text-4xl font-bold mb-4 text-center">
|
||||
Service Request
|
||||
</h1>
|
||||
<p className="text-center text-foreground/70 mb-12">
|
||||
Submit a service request and our team will respond within 24 hours.
|
||||
</p>
|
||||
|
||||
<div className="bg-card rounded-lg p-8 border border-foreground/10">
|
||||
{submitted ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="text-5xl mb-4">✓</div>
|
||||
<h2 className="text-2xl font-bold mb-2">
|
||||
Service Request Submitted!
|
||||
</h2>
|
||||
<p className="text-foreground/70">
|
||||
A technician will contact you within 24 hours.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Member Name */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Member Name *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="memberName"
|
||||
value={formData.memberName}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="John Doe"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Phone */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Phone Number *
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="(555) 123-4567"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Address */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Service Address *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="address"
|
||||
value={formData.address}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="123 Main Street, City, State 12345"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Membership Plan */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Membership Plan *
|
||||
</label>
|
||||
<select
|
||||
name="membershipPlan"
|
||||
value={formData.membershipPlan}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
>
|
||||
<option value="">Select a plan</option>
|
||||
<option value="basic">Basic Plan</option>
|
||||
<option value="standard">Standard Plan</option>
|
||||
<option value="premium">Premium Plan</option>
|
||||
<option value="elite">Elite Plan</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Issue Type */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Issue Type *
|
||||
</label>
|
||||
<select
|
||||
name="issueType"
|
||||
value={formData.issueType}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
>
|
||||
<option value="">Select issue type</option>
|
||||
<option value="plumbing">Plumbing</option>
|
||||
<option value="electrical">Electrical</option>
|
||||
<option value="hvac">HVAC</option>
|
||||
<option value="roofing">Roofing</option>
|
||||
<option value="general">General Maintenance</option>
|
||||
<option value="emergency">Emergency Repair</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Issue Description *
|
||||
</label>
|
||||
<textarea
|
||||
name="description"
|
||||
value={formData.description}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
rows={4}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg bg-background focus:outline-none focus:border-primary-cta"
|
||||
placeholder="Describe the issue in detail..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Photo Upload */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Upload Photo (Optional)
|
||||
</label>
|
||||
<div className="border-2 border-dashed border-foreground/20 rounded-lg p-6 text-center hover:border-primary-cta transition-colors">
|
||||
<input
|
||||
type="file"
|
||||
name="photo"
|
||||
onChange={handleFileChange}
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
id="photo-upload"
|
||||
/>
|
||||
<label
|
||||
htmlFor="photo-upload"
|
||||
className="cursor-pointer flex flex-col items-center gap-2"
|
||||
>
|
||||
<Upload className="w-8 h-8 text-primary-cta" />
|
||||
<span className="text-sm font-medium">
|
||||
{formData.photoFile
|
||||
? formData.photoFile.name
|
||||
: "Click to upload or drag and drop"}
|
||||
</span>
|
||||
<span className="text-xs text-foreground/50">
|
||||
PNG, JPG, GIF up to 10MB
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-primary-cta text-white font-medium py-3 rounded-lg hover:opacity-90 transition-opacity"
|
||||
>
|
||||
Submit Service Request
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -10,15 +10,15 @@
|
||||
--accent: #ffffff;
|
||||
--background-accent: #ffffff; */
|
||||
|
||||
--background: #ffffff;
|
||||
--card: #f9f9f9;
|
||||
--foreground: #000612e6;
|
||||
--primary-cta: #106EFB;
|
||||
--background: #f7f6f7;
|
||||
--card: #ffffff;
|
||||
--foreground: #1a1a1a;
|
||||
--primary-cta: #0798ff;
|
||||
--primary-cta-text: #ffffff;
|
||||
--secondary-cta: #f9f9f9;
|
||||
--secondary-cta: #ffffff;
|
||||
--secondary-cta-text: #000612e6;
|
||||
--accent: #e2e2e2;
|
||||
--background-accent: #106EFB;
|
||||
--accent: #93c7ff;
|
||||
--background-accent: #a8cde8;
|
||||
|
||||
/* text sizing - set by ThemeProvider */
|
||||
/* --text-2xs: clamp(0.465rem, 0.62vw, 0.62rem);
|
||||
|
||||
Reference in New Issue
Block a user