commit 3247e787f654e37d2adeb392a7af5d9e7865e585 Author: Alaguraj0361 Date: Tue Aug 12 10:25:18 2025 +0530 first commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..89b75eb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..1c2aa65 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c87c9b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..9068716 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": true, + "singleQuote": true, + "printWidth": 200 +} diff --git a/App.tsx b/App.tsx new file mode 100644 index 0000000..c77b676 --- /dev/null +++ b/App.tsx @@ -0,0 +1,40 @@ +'use client'; +import { PropsWithChildren, useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { IRootState } from '@/store'; +import { toggleRTL, toggleTheme, toggleMenu, toggleLayout, toggleAnimation, toggleNavbar, toggleSemidark } from '@/store/themeConfigSlice'; +import Loading from '@/components/layouts/loading'; +import { getTranslation } from '@/i18n'; + +function App({ children }: PropsWithChildren) { + const themeConfig = useSelector((state: IRootState) => state.themeConfig); + const dispatch = useDispatch(); + const { initLocale } = getTranslation(); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + dispatch(toggleTheme(localStorage.getItem('theme') || themeConfig.theme)); + dispatch(toggleMenu(localStorage.getItem('menu') || themeConfig.menu)); + dispatch(toggleLayout(localStorage.getItem('layout') || themeConfig.layout)); + dispatch(toggleRTL(localStorage.getItem('rtlClass') || themeConfig.rtlClass)); + dispatch(toggleAnimation(localStorage.getItem('animation') || themeConfig.animation)); + dispatch(toggleNavbar(localStorage.getItem('navbar') || themeConfig.navbar)); + dispatch(toggleSemidark(localStorage.getItem('semidark') || themeConfig.semidark)); + // locale + initLocale(themeConfig.locale); + + setIsLoading(false); + }, [dispatch, initLocale, themeConfig.theme, themeConfig.menu, themeConfig.layout, themeConfig.rtlClass, themeConfig.animation, themeConfig.navbar, themeConfig.locale, themeConfig.semidark]); + + return ( +
+ {isLoading ? : children} +
+ ); +} + +export default App; diff --git a/README.md b/README.md new file mode 100644 index 0000000..965a122 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx new file mode 100644 index 0000000..483a128 --- /dev/null +++ b/app/(auth)/layout.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const AuthLayout = ({ children }: { children: React.ReactNode }) => { + return
{children}
; +}; + +export default AuthLayout; diff --git a/app/(defaults)/create-event-gallery/page.tsx b/app/(defaults)/create-event-gallery/page.tsx new file mode 100644 index 0000000..2b5ef01 --- /dev/null +++ b/app/(defaults)/create-event-gallery/page.tsx @@ -0,0 +1,21 @@ + +'use client'; +export const dynamic = "force-dynamic"; +import CreateEventForm from '@/components/gallery/CreateEventForm'; +import CreateEventGalleryForm from '@/components/gallery/CreateEventGalleryForm'; +import { Metadata } from 'next'; +import { useSearchParams } from 'next/navigation'; +import React from 'react'; + +const CreateEventGallery = () => { + + const searchParams = useSearchParams() + const eventId = searchParams.get('eventid') + return ( + <> + + + ); +}; + +export default CreateEventGallery; diff --git a/app/(defaults)/create-event/page.tsx b/app/(defaults)/create-event/page.tsx new file mode 100644 index 0000000..1d921d8 --- /dev/null +++ b/app/(defaults)/create-event/page.tsx @@ -0,0 +1,17 @@ +import CreateEventForm from '@/components/gallery/CreateEventForm'; +import { Metadata } from 'next'; +import React from 'react'; + +export const metadata: Metadata = { + title: 'Create Event', +}; + +const CreateEvent = () => { + return ( + <> + + + ); +}; + +export default CreateEvent; diff --git a/app/(defaults)/edit-event/page.tsx b/app/(defaults)/edit-event/page.tsx new file mode 100644 index 0000000..f306eb6 --- /dev/null +++ b/app/(defaults)/edit-event/page.tsx @@ -0,0 +1,24 @@ +'use client' +export const dynamic = "force-dynamic"; +import CreateEventForm from '@/components/gallery/CreateEventForm'; +import EditEventForm from '@/components/gallery/EditEventForm'; +import { Metadata } from 'next'; +import { useSearchParams } from 'next/navigation'; +import React from 'react'; + +// export const metadata: Metadata = { +// title: 'Update Event', +// }; + +const CreateEvent = () => { + + const searchParams = useSearchParams() + const eventId = searchParams.get('event') + return ( + <> + + + ); +}; + +export default CreateEvent; diff --git a/app/(defaults)/event-gallery/page.tsx b/app/(defaults)/event-gallery/page.tsx new file mode 100644 index 0000000..c9b876c --- /dev/null +++ b/app/(defaults)/event-gallery/page.tsx @@ -0,0 +1,20 @@ +'use client' +export const dynamic = "force-dynamic"; +import CreateEventForm from '@/components/gallery/CreateEventForm'; +import ListOfEventsGallery from '@/components/gallery/ListOfEventGallery'; +import { Metadata } from 'next'; +import { useSearchParams } from 'next/navigation'; +import React from 'react'; + +const CreateEvent = () => { + + const searchParams = useSearchParams() + const eventId = searchParams.get('eventid') + return ( + <> + + + ); +}; + +export default CreateEvent; diff --git a/app/(defaults)/layout.tsx b/app/(defaults)/layout.tsx new file mode 100644 index 0000000..0c743f6 --- /dev/null +++ b/app/(defaults)/layout.tsx @@ -0,0 +1,45 @@ +import ContentAnimation from '@/components/layouts/content-animation'; +import Footer from '@/components/layouts/footer'; +import Header from '@/components/layouts/header'; +import MainContainer from '@/components/layouts/main-container'; +import Overlay from '@/components/layouts/overlay'; +import ScrollToTop from '@/components/layouts/scroll-to-top'; +import Setting from '@/components/layouts/setting'; +import Sidebar from '@/components/layouts/sidebar'; +import Portals from '@/components/portals'; + +export default function DefaultLayout({ children }: { children: React.ReactNode }) { + return ( + <> + {/* BEGIN MAIN CONTAINER */} +
+ + + + {/* BEGIN APP SETTING LAUNCHER */} + {/* */} + {/* END APP SETTING LAUNCHER */} + + + {/* BEGIN SIDEBAR */} + + {/* END SIDEBAR */} +
+ {/* BEGIN TOP NAVBAR */} +
+ {/* END TOP NAVBAR */} + + {/* BEGIN CONTENT AREA */} + {children} + {/* END CONTENT AREA */} + + {/* BEGIN FOOTER */} +
+ {/* END FOOTER */} + +
+
+
+ + ); +} diff --git a/app/(defaults)/page.tsx b/app/(defaults)/page.tsx new file mode 100644 index 0000000..b946bd1 --- /dev/null +++ b/app/(defaults)/page.tsx @@ -0,0 +1,17 @@ +import ListOfEvents from '@/components/gallery/ListOfEvents'; +import { Metadata } from 'next'; +import React from 'react'; + +export const metadata: Metadata = { + title: 'Sales Admin', +}; + +const Sales = () => { + return ( + <> + + + ); +}; + +export default Sales; diff --git a/app/icon.png b/app/icon.png new file mode 100644 index 0000000..2f61b78 Binary files /dev/null and b/app/icon.png differ diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..10d6deb --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,28 @@ +import ProviderComponent from '@/components/layouts/provider-component'; +import 'react-perfect-scrollbar/dist/css/styles.css'; +import '../styles/tailwind.css'; +import { Metadata } from 'next'; +import { Nunito } from 'next/font/google'; + +export const metadata: Metadata = { + title: { + template: 'Tamil Culture Association', + default: 'Tamil culture Association', + }, +}; +const nunito = Nunito({ + weight: ['400', '500', '600', '700', '800'], + subsets: ['latin'], + display: 'swap', + variable: '--font-nunito', +}); + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + ); +} diff --git a/app/loading.tsx b/app/loading.tsx new file mode 100644 index 0000000..65a8328 --- /dev/null +++ b/app/loading.tsx @@ -0,0 +1,8 @@ +import Loading from '@/components/layouts/loading'; +import React from 'react'; + +const loading = () => { + return ; +}; + +export default loading; diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..f232f65 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,26 @@ +import { Metadata } from 'next'; +import Link from 'next/link'; +import React from 'react'; + +export const metadata: Metadata = { + title: 'Error 404', +}; + +const NotFound = () => { + return ( +
+
+
+ 404 + 404 +

The page you requested was not found!

+ + Home + +
+
+
+ ); +}; + +export default NotFound; diff --git a/components/dropdown.tsx b/components/dropdown.tsx new file mode 100644 index 0000000..30bb517 --- /dev/null +++ b/components/dropdown.tsx @@ -0,0 +1,57 @@ +'use client'; +import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'; +import { usePopper } from 'react-popper'; + +const Dropdown = (props: any, forwardedRef: any) => { + const [visibility, setVisibility] = useState(false); + + const referenceRef = useRef(); + const popperRef = useRef(); + + const { styles, attributes } = usePopper(referenceRef.current, popperRef.current, { + placement: props.placement || 'bottom-end', + modifiers: [ + { + name: 'offset', + options: { + offset: props.offset || [0], + }, + }, + ], + }); + + const handleDocumentClick = (event: any) => { + if (referenceRef.current.contains(event.target) || popperRef.current.contains(event.target)) { + return; + } + + setVisibility(false); + }; + + useEffect(() => { + document.addEventListener('mousedown', handleDocumentClick); + return () => { + document.removeEventListener('mousedown', handleDocumentClick); + }; + }, []); + + useImperativeHandle(forwardedRef, () => ({ + close() { + setVisibility(false); + }, + })); + + return ( + <> + + +
setVisibility(!visibility)}> + {visibility && props.children} +
+ + ); +}; + +export default forwardRef(Dropdown); diff --git a/components/gallery/CreateEventForm.tsx b/components/gallery/CreateEventForm.tsx new file mode 100644 index 0000000..945a207 --- /dev/null +++ b/components/gallery/CreateEventForm.tsx @@ -0,0 +1,233 @@ +'use client'; +import React, { useState, ChangeEvent, FormEvent } from 'react'; +import IconTrashLines from '../icon/icon-trash-lines'; +import axios from 'axios'; +import { useRouter } from 'next/navigation'; +import { showMessage } from '@/utils/CommonFunction.utils'; + +interface FormValues { + year: string; + eventdate: string; + eventtitle: string; + eventimageurl: File | null; + eventdescription: string; +} + +interface FormErrors { + [key: string]: string; +} + +const CreateEventForm: React.FC = () => { + + const router = useRouter() + + const [formData, setFormData] = useState({ + year: '', + eventdate: '', + eventtitle: '', + eventimageurl: null, + eventdescription: '', + }); + + const [errors, setErrors] = useState({}); + const [previewUrl, setPreviewUrl] = useState(null); + + const handleChange = (e: ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value, + })); + }; + + const handleFileChange = (e: ChangeEvent) => { + const file = e.target.files?.[0] || null; + setFormData(prev => ({ + ...prev, + eventimageurl: file, + })); + + if (file) { + const url = URL.createObjectURL(file); + setPreviewUrl(url); + } else { + setPreviewUrl(null); + } + }; + + const validateForm = (): boolean => { + const newErrors: FormErrors = {}; + + if (!formData.year.trim()) newErrors.year = 'Year is required'; + if (!formData.eventdate.trim()) newErrors.eventdate = 'Event date is required'; + if (!formData.eventtitle.trim()) newErrors.eventtitle = 'Event title is required'; + if (!formData.eventimageurl) { + newErrors.eventimageurl = 'Please upload an image'; + } else if (!formData.eventimageurl.type.startsWith('image/')) { + newErrors.eventimageurl = 'Only image files are allowed'; + } + if (!formData.eventdescription.trim()) newErrors.eventdescription = 'Description is required'; + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + if (!validateForm()) return; + console.log("formData", formData) + + + const data = new FormData(); + + if (formData.eventimageurl && formData.eventimageurl.type.startsWith("image/")) { + data.append("file", formData.eventimageurl); // ✅ Use correct field name + } + + try { + const ImageUpload = await axios.post(`https://api.tamilculturewaterloo.org/api/upload/single`, data, { + headers: { + "Content-Type": "multipart/form-data", // important for file upload + }, + }) + console.log("ImageUpload", ImageUpload) + const createData = { + year: formData.year, + eventdate: formData.eventdate, + eventtitle: formData.eventtitle, + eventdescription: formData.eventdescription, + eventimageurl: ImageUpload?.data?.data?.fullUrl + } + + const res = await axios.post(`https://api.tamilculturewaterloo.org/api/events`, createData) + console.log("res", res) + showMessage("Event Created Successfully", "success") + router?.push(`/`) + } catch (error: any) { + showMessage(`${error?.response?.data?.message ? error?.response?.data?.message : "error"}`) + + } + + }; + + const handleImageDelete = () => { + setFormData(prev => ({ + ...prev, + eventimageurl: null, + })); + setPreviewUrl(null); + }; + return ( +
+

Create Event

+ +
+
+ {/* Year */} +
+ + + {errors.year &&

{errors.year}

} +
+ + {/* Event Date */} +
+ + + {errors.eventdate &&

{errors.eventdate}

} +
+ + {/* Event Title */} +
+ + + {errors.eventtitle &&

{errors.eventtitle}

} +
+ + {/* Event Description */} +
+ +