diff --git a/index.html b/index.html index a57f7a8..9f54d1e 100644 --- a/index.html +++ b/index.html @@ -17,7 +17,7 @@ } - + Mountain Children Home diff --git a/public/mountain.svg b/public/mountain.svg new file mode 100644 index 0000000..a727bfb --- /dev/null +++ b/public/mountain.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/App/App.tsx b/src/App/App.tsx index e8d09fe..172ca17 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -26,6 +26,7 @@ import BlogDetail from '@/pages/BlogDetail'; import Budget from '@/pages/Budget'; import ContactUs from '@/pages/ContactUs'; import Event from '@/pages/Event'; +import EventDetail from '@/pages/EventDetail'; import Gallery from '@/pages/Gallery'; import Home from '@/pages/Home'; import Login from '@/pages/Login'; @@ -101,6 +102,10 @@ const router = createBrowserRouter([ path: '/event', element: , }, + { + path: '/event/:id', + element: , + }, ], }, @@ -198,5 +203,4 @@ function App() { ); } - export default App; diff --git a/src/assets/Codex-01.png b/src/assets/Codex-01.png new file mode 100644 index 0000000..a5d4c9a Binary files /dev/null and b/src/assets/Codex-01.png differ diff --git a/src/assets/Codex.jpg b/src/assets/Codex.jpg new file mode 100644 index 0000000..2e53ee4 Binary files /dev/null and b/src/assets/Codex.jpg differ diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..eab2c03 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { + cva, + type VariantProps, +} from 'class-variance-authority'; + +import { cn } from '@/lib/utils'; + +const badgeVariants = cva( + 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', + { + variants: { + variant: { + default: + 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', + secondary: + 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', + destructive: + 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +); + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<'span'> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : 'span'; + + return ( + + ); +} + +export { Badge, badgeVariants }; diff --git a/src/constants/index.tsx b/src/constants/index.tsx index 77e3470..19d12a6 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -20,7 +20,7 @@ export const navigation = [ url: '/budget', }, { - title: 'Event', + title: 'Events', url: '/event', }, diff --git a/src/pages/AboutUs/OurStory.tsx b/src/pages/AboutUs/OurStory.tsx index dfc0fe4..aa57cf5 100644 --- a/src/pages/AboutUs/OurStory.tsx +++ b/src/pages/AboutUs/OurStory.tsx @@ -1,3 +1,4 @@ +import image1 from '@/assets/1.jpg'; import MaxWidthWrapper from '@/components/MaxWidthWrapper'; function OurStory() { @@ -28,7 +29,7 @@ function OurStory() {
Mountain Children Home building diff --git a/src/pages/BlogDetail/index.tsx b/src/pages/BlogDetail/index.tsx index 14904e8..dd65210 100644 --- a/src/pages/BlogDetail/index.tsx +++ b/src/pages/BlogDetail/index.tsx @@ -155,151 +155,153 @@ function BlogDetail() { return ( - {/* Breadcrumb */} - +
+ {/* Breadcrumb */} + - {/* Article Header */} -
-
- {primaryTag && ( - - {primaryTag.name} - - )} - - {formatDate(blog.published_at)} - - - {blog.reading_time} - {' '} - min read -
+ {/* Article Header */} +
+
+ {primaryTag && ( + + {primaryTag.name} + + )} + + {formatDate(blog.published_at)} + + + {blog.reading_time} + {' '} + min read +
-

- {blog.title} -

+

+ {blog.title} +

-
-
- - - By - {' '} - {blog.author_name} - +
+
+ + + By + {' '} + {blog.author_name} + +
-
- {blog.title} -
+ {blog.title} +
- {/* Article Content */} -
-
-
+ {/* Article Content */} +
+
+
- {/* Tags */} - {blog.tags && blog.tags.length > 0 && ( -
-

Tags

-
- {blog.tags.map((tag) => ( - - {tag.name} - - ))} + {/* Tags */} + {blog.tags && blog.tags.length > 0 && ( +
+

Tags

+
+ {blog.tags.map((tag) => ( + + {tag.name} + + ))} +
-
- )} + )} - {/* Social Sharing */} -
-

Share this story

-
- - - + + + }} + className="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded font-medium transition-colors" + > + Copy Link + +
-
- {/* Related Articles */} - {relatedBlogs.length > 0 && ( -
-

Related Stories

-
- {relatedBlogs.map((relatedBlog) => ( - -
- {relatedBlog.title} -
-
- - {formatDate(relatedBlog.published_at)} + {/* Related Articles */} + {relatedBlogs.length > 0 && ( +
+

Related Stories

+
+ {relatedBlogs.map((relatedBlog) => ( + +
+ {relatedBlog.title} +
+
+ + {formatDate(relatedBlog.published_at)} +
+

+ {relatedBlog.title} +

+

+ {relatedBlog.content.replace(/<[^>]*>/g, '').substring(0, 100)} + ... +

-

- {relatedBlog.title} -

-

- {relatedBlog.content.replace(/<[^>]*>/g, '').substring(0, 100)} - ... -

-
-
- - ))} -
-
- )} +
+ + ))} +
+ + )} + ); } diff --git a/src/pages/ContactUs/ContactSection.tsx b/src/pages/ContactUs/ContactSection.tsx index ff3e7c5..cf157ca 100644 --- a/src/pages/ContactUs/ContactSection.tsx +++ b/src/pages/ContactUs/ContactSection.tsx @@ -1,30 +1,76 @@ +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; import { Label } from '@radix-ui/react-label'; +import { useMutation } from '@tanstack/react-query'; import { Calendar, ContactIcon, } from 'lucide-react'; +import * as z from 'zod'; import MaxWidthWrapper from '@/components/MaxWidthWrapper'; +import { baseRequest } from '@/lib/base'; -interface FormData { - name: string, - email: string, - phone: string, - subject: string, - message: string, - inquiryType: string, -} +// API function +const postContact = async (body: ContactFormData) => { + const response = await baseRequest({ + url: '/cms/contact-messages/', // Updated to match your API endpoint + method: 'POST', + body, + contentType: 'application/json', + }); -// Define the props interface -interface ContactSectionProps { - formData: FormData; - handleSubmit: (e: React.FormEvent) => void; - handleInputChange: (e: React.ChangeEvent) => void; -} + if (!response.ok) { + throw new Error(response.data?.detail || `Error: ${response.status} - ${response.statusText}`); + } + + return response; +}; + +// Form validation schema +const contactFormSchema = z.object({ + name: z.string().min(1, { message: 'Name is required' }), + email: z.string().email({ message: 'Invalid email address' }), + phone: z.string().optional(), + message: z.string().min(1, { message: 'Message is required' }), +}); + +type ContactFormData = z.infer; + +function ContactSection() { + const { + register, + handleSubmit, + formState: { errors }, + reset, + } = useForm({ + resolver: zodResolver(contactFormSchema), + defaultValues: { + name: '', + email: '', + phone: '', + message: '', + }, + }); + + const { + mutate, + isPending, + isError, + error, + isSuccess, + } = useMutation({ + mutationFn: (data: ContactFormData) => postContact(data), + onSuccess: () => { + reset(); + // Optional: Add toast notification here + }, + }); + + const onSubmit = (data: ContactFormData) => { + mutate(data); + }; -function ContactSection(props:ContactSectionProps) { - const { formData, handleSubmit, handleInputChange } = props; return (
@@ -32,7 +78,30 @@ function ContactSection(props:ContactSectionProps) { {/* Contact Form */}

Send us a Message

-
+ + {/* Success Message */} + {isSuccess && ( +
+

+ Success! + {' '} + Thank you for your message! We'll get back to you soon. +

+
+ )} + + {/* Error Message */} + {isError && ( +
+

+ Error: + {' '} + {error instanceof Error ? error.message : 'Failed to send message. Please try again.'} +

+
+ )} + +
@@ -74,47 +145,15 @@ function ContactSection(props:ContactSectionProps) { + {errors.phone && ( +

{errors.phone.message}

+ )}
-
- - -
- - -
- -
@@ -123,21 +162,23 @@ function ContactSection(props:ContactSectionProps) {