submit form contactus and start api projects
This commit is contained in:
@@ -1,32 +1,148 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Button from '@/components/uikit/button';
|
||||
import FormField from '@/components/uikit/inputs';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import api from '@/lib/axios';
|
||||
import { setContact } from '@/services/contact';
|
||||
|
||||
const ContactForm = () => {
|
||||
const t = useTranslations();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const validate = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
|
||||
if (!formData.first_name.trim())
|
||||
newErrors.first_name = t('com_contact_first_name_required');
|
||||
if (!formData.last_name.trim())
|
||||
newErrors.last_name = t('com_contact_last_name_required');
|
||||
|
||||
if (!formData.email) {
|
||||
newErrors.email = t('com_contact_email_required');
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
||||
newErrors.email = t('com_contact_email_error');
|
||||
}
|
||||
|
||||
if (!formData.phone) {
|
||||
newErrors.phone = t('com_contact_phone_required');
|
||||
} else if (!/^09\d{9}$/.test(formData.phone)) {
|
||||
newErrors.phone = t('com_contact_phone_error');
|
||||
}
|
||||
|
||||
if (!formData.message.trim()) {
|
||||
newErrors.message = t('com_contact_textarea_required');
|
||||
} else if (formData.message.trim().length < 10) {
|
||||
newErrors.message = t('com_contact_textarea_error');
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleChange = (field: string, value: string) => {
|
||||
setFormData({ ...formData, [field]: value });
|
||||
setErrors({ ...errors, [field]: '' });
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
console.log(e);
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setContact(formData).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full rounded-4xl bg-white p-4 md:w-1/2">
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="z-10 w-full rounded-4xl bg-white p-4 md:w-1/2"
|
||||
>
|
||||
<h2 className="mt-8 mb-10 text-5xl text-gray-800">
|
||||
{t('com_contact_title')}{' '}
|
||||
<span className="text-5xl font-bold">
|
||||
{t('com_contact_title_bold')}
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col gap-6 md:flex-row md:gap-8">
|
||||
<FormField type="text" placeholder={t('com_contact_first_name')} />
|
||||
<FormField type="text" placeholder={t('com_contact_last_name')} />
|
||||
</div>
|
||||
<FormField type="email" placeholder={t('com_contact_email')} />
|
||||
<FormField type="tel" placeholder={t('com_contact_phone')} />
|
||||
<FormField type="textarea" placeholder={t('com_contact_textarea')} />
|
||||
<div className="w-full">
|
||||
<FormField
|
||||
type="text"
|
||||
placeholder={t('com_contact_first_name')}
|
||||
value={formData.first_name}
|
||||
onChange={(e: any) => handleChange('first_name', e.target.value)}
|
||||
/>
|
||||
{errors.first_name && (
|
||||
<p className="text-primary mt-1 text-sm">{errors.first_name}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button>Submit Message</Button>
|
||||
<div className="w-full">
|
||||
<FormField
|
||||
type="text"
|
||||
placeholder={t('com_contact_last_name')}
|
||||
value={formData.last_name}
|
||||
onChange={(e: any) => handleChange('last_name', e.target.value)}
|
||||
/>
|
||||
{errors.last_name && (
|
||||
<p className="text-primary mt-1 text-sm">{errors.last_name}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormField
|
||||
type="email"
|
||||
placeholder={t('com_contact_email')}
|
||||
value={formData.email}
|
||||
onChange={(e: any) => handleChange('email', e.target.value)}
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="text-primary mt-1 text-sm">{errors.email}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormField
|
||||
type="tel"
|
||||
placeholder={t('com_contact_phone')}
|
||||
value={formData.phone}
|
||||
onChange={(e: any) => handleChange('phone', e.target.value)}
|
||||
/>
|
||||
{errors.phone && (
|
||||
<p className="text-primary mt-1 text-sm">{errors.phone}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FormField
|
||||
type="textarea"
|
||||
placeholder={t('com_contact_textarea')}
|
||||
value={formData.message}
|
||||
onChange={(e: any) => handleChange('message', e.target.value)}
|
||||
/>
|
||||
{errors.message && (
|
||||
<p className="text-primary mt-1 text-sm">{errors.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button disabled={loading}>{t('com_contact_button_submit')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import SectionTitle from '@/components/layout/sectionTitle';
|
||||
import { Icon } from '@/components/uikit/icons';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
@@ -8,23 +9,18 @@ const TouchUs = () => {
|
||||
|
||||
return (
|
||||
<div className="w-full p-8 md:w-1/2">
|
||||
<div className="mb-3.5 flex items-center gap-0.5 text-base">
|
||||
<Icon name="setting" className="text-primary h-4 w-4" />
|
||||
<p>{t('pages_contact_title')}</p>
|
||||
</div>
|
||||
<h1 className="mb-5 text-3xl font-light text-gray-800 md:text-5xl">
|
||||
{t('com_contact_touch_title')}{' '}
|
||||
<span className="text-3xl font-light md:text-5xl md:font-bold">
|
||||
{t('com_contact_touch_title_bold')}
|
||||
</span>
|
||||
</h1>
|
||||
<p className="mb-10 text-base font-normal text-gray-600">
|
||||
<SectionTitle
|
||||
title={t('pages_contact_title')}
|
||||
description={t('com_contact_touch_title')}
|
||||
description_bold={t('com_contact_touch_title_bold')}
|
||||
/>
|
||||
<p className="mt-4 mb-10 text-base font-normal text-gray-600">
|
||||
{t('com_contact_touch_description')}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col justify-between gap-10">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4 flex h-10 w-10 items-center justify-center rounded-full bg-red-500">
|
||||
<div className="me-4 flex h-10 w-10 items-center justify-center rounded-full bg-red-500">
|
||||
<Icon name="phone" className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -36,7 +32,7 @@ const TouchUs = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4 flex h-10 w-10 items-center justify-center rounded-full bg-red-500">
|
||||
<div className="me-4 flex h-10 w-10 items-center justify-center rounded-full bg-red-500">
|
||||
<Icon name="email" className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -48,7 +44,7 @@ const TouchUs = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4 flex h-10 w-10 items-center justify-center rounded-full bg-red-500">
|
||||
<div className="me-4 flex h-10 w-10 items-center justify-center rounded-full bg-red-500">
|
||||
<Icon name="location" className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import images from '@/assets/images/images';
|
||||
import SectionTitle from '@/components/layout/sectionTitle';
|
||||
import { getProductCategories } from '@/services/products';
|
||||
import Link from 'next/link';
|
||||
import routeFactory from '@/assets/constants/routeFactory';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { TLocales } from '@/models/layout';
|
||||
import { getProjectCategories } from '@/services/projects';
|
||||
import { IProjectCardProps } from '../project/project';
|
||||
|
||||
interface Props {
|
||||
locale: TLocales;
|
||||
}
|
||||
|
||||
export default async function HomePageProjects({ locale }: Props) {
|
||||
const t = await getTranslations({ locale });
|
||||
|
||||
// const projectCategories: IProjectCardProps[] = await getProjectCategories();
|
||||
const routes = routeFactory(t, locale);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={`relative w-full bg-white bg-cover bg-fixed bg-center bg-no-repeat pt-28 pb-48`}
|
||||
// style={{ backgroundImage: `url(${images.homeProductBack.src})` }}
|
||||
>
|
||||
<div className="absolute inset-0 z-10 bg-black opacity-50" />
|
||||
<div className="relative z-20 container mx-auto grid grid-cols-2 gap-28">
|
||||
<SectionTitle
|
||||
title={t('pages_product_categories_title')}
|
||||
description={t('com_home_products_title')}
|
||||
reverseColor
|
||||
/>
|
||||
<h4 className="pt-10 text-base font-extralight tracking-normal text-white">
|
||||
{t('com_home_products_description')}
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative top-[-100px] z-20 container mx-auto w-full">
|
||||
{/* <div className="grid grid-cols-1 gap-8 rounded-4xl bg-gray-50 py-10 shadow-2xl md:grid-cols-2 lg:grid-cols-4">
|
||||
{projectCategories.map((category, index) => (
|
||||
<Link
|
||||
href={'#'}
|
||||
key={index}
|
||||
// href={routes.products.route(index)}
|
||||
>
|
||||
<div
|
||||
className={`flex flex-col items-center justify-center gap-4 p-6 text-center ${index ? 'border-s-[1px] border-gray-100' : ''}`}
|
||||
>
|
||||
<h3 className="text-xl font-bold text-gray-500">
|
||||
{category.title}
|
||||
</h3>
|
||||
{/* <p className="text-lg font-light text-gray-400">
|
||||
{t('com_products_category_types_count', {
|
||||
count: category.typesCount,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>*/}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,39 +1,40 @@
|
||||
import images from '@/assets/images/images';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { IProjectCardProps } from './project';
|
||||
|
||||
const projects = [
|
||||
{
|
||||
title: 'Sustainable Practices Reducing Waste in Industrial Production',
|
||||
image: images.projectImg_1.src,
|
||||
imageSrc: images.projectImg_1.src,
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'Advanced Robotics Revolutionizing Industrial Workflows',
|
||||
image: images.projectImg_1.src,
|
||||
imageSrc: images.projectImg_1.src,
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'Top Benefits of the Robotics in Manufacturing',
|
||||
image: images.projectImg_1.src,
|
||||
imageSrc: images.projectImg_1.src,
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'Leveraging Data Analytics for Smarter Production',
|
||||
image: images.projectImg_1.src,
|
||||
imageSrc: images.projectImg_1.src,
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'Reducing Operational Costs Through Automation',
|
||||
image: '/images/post5.jpg',
|
||||
imageSrc: '/images/post5.jpg',
|
||||
link: '#',
|
||||
},
|
||||
{
|
||||
title: 'The Advantages of Customized Manufacturing Solutions',
|
||||
image: '/images/post6.jpg',
|
||||
imageSrc: '/images/post6.jpg',
|
||||
link: '#',
|
||||
},
|
||||
];
|
||||
] as IProjectCardProps[];
|
||||
|
||||
export default function ProjectsGrid() {
|
||||
return (
|
||||
@@ -46,7 +47,7 @@ export default function ProjectsGrid() {
|
||||
>
|
||||
<div className="relative aspect-[1.09] w-full overflow-hidden rounded-4xl">
|
||||
<Image
|
||||
src={project.image}
|
||||
src={project.imageSrc}
|
||||
alt={project.title}
|
||||
fill
|
||||
className="h-auto w-full object-cover"
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export interface IProjectCardProps {
|
||||
imageSrc: string;
|
||||
title: string;
|
||||
link: string;
|
||||
}
|
||||
Reference in New Issue
Block a user