├── app ├── favicon.ico ├── (auth) │ ├── sign-in │ │ └── [[...sign-in]] │ │ │ └── page.tsx │ ├── sign-up │ │ └── [[...sign-up]] │ │ │ └── page.tsx │ └── layout.tsx ├── api │ ├── uploadthing │ │ ├── route.ts │ │ └── core.ts │ └── webhook │ │ ├── stripe │ │ └── route.ts │ │ └── clerk │ │ └── route.ts ├── (root) │ ├── layout.tsx │ ├── events │ │ ├── create │ │ │ └── page.tsx │ │ └── [id] │ │ │ ├── update │ │ │ └── page.tsx │ │ │ └── page.tsx │ ├── page.tsx │ ├── orders │ │ └── page.tsx │ └── profile │ │ └── page.tsx ├── layout.tsx └── globals.css ├── postcss.config.js ├── public ├── assets │ ├── images │ │ ├── hero.png │ │ ├── test.png │ │ ├── test-2.png │ │ ├── placeholder.png │ │ ├── dotted-pattern.png │ │ └── logo.svg │ └── icons │ │ ├── loader.svg │ │ ├── arrow.svg │ │ ├── spinner.svg │ │ ├── logo-grey.svg │ │ ├── clock.svg │ │ ├── menu.svg │ │ ├── search.svg │ │ ├── location-grey.svg │ │ ├── location.svg │ │ ├── calendar.svg │ │ ├── delete.svg │ │ ├── edit.svg │ │ ├── dollar.svg │ │ ├── link.svg │ │ ├── upload.svg │ │ └── file-upload.svg ├── vercel.svg └── next.svg ├── lib ├── uploadthing.ts ├── database │ ├── models │ │ ├── category.model.ts │ │ ├── user.model.ts │ │ ├── order.model.ts │ │ └── event.model.ts │ └── index.ts ├── validator.ts ├── actions │ ├── category.actions.ts │ ├── user.actions.ts │ ├── order.actions.ts │ └── event.actions.ts └── utils.ts ├── next.config.js ├── components.json ├── middleware.ts ├── constants └── index.ts ├── .gitignore ├── components ├── shared │ ├── Footer.tsx │ ├── NavItems.tsx │ ├── MobileNav.tsx │ ├── CheckoutButton.tsx │ ├── Header.tsx │ ├── Checkout.tsx │ ├── Pagination.tsx │ ├── Search.tsx │ ├── DeleteConfirmation.tsx │ ├── Collection.tsx │ ├── FileUploader.tsx │ ├── CategoryFilter.tsx │ ├── Dropdown.tsx │ ├── Card.tsx │ └── EventForm.tsx └── ui │ ├── label.tsx │ ├── textarea.tsx │ ├── separator.tsx │ ├── input.tsx │ ├── checkbox.tsx │ ├── button.tsx │ ├── form.tsx │ ├── sheet.tsx │ ├── alert-dialog.tsx │ ├── select.tsx │ └── dropdown-menu.tsx ├── tsconfig.json ├── package.json ├── tailwind.config.ts ├── types └── index.ts └── README.md /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianhajdin/event_platform/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/assets/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianhajdin/event_platform/HEAD/public/assets/images/hero.png -------------------------------------------------------------------------------- /public/assets/images/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianhajdin/event_platform/HEAD/public/assets/images/test.png -------------------------------------------------------------------------------- /public/assets/images/test-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianhajdin/event_platform/HEAD/public/assets/images/test-2.png -------------------------------------------------------------------------------- /public/assets/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianhajdin/event_platform/HEAD/public/assets/images/placeholder.png -------------------------------------------------------------------------------- /public/assets/images/dotted-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrianhajdin/event_platform/HEAD/public/assets/images/dotted-pattern.png -------------------------------------------------------------------------------- /app/(auth)/sign-in/[[...sign-in]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignIn } from "@clerk/nextjs"; 2 | 3 | export default function Page() { 4 | return 5 | } -------------------------------------------------------------------------------- /app/(auth)/sign-up/[[...sign-up]]/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignUp } from "@clerk/nextjs"; 2 | 3 | export default function Page() { 4 | return 5 | } -------------------------------------------------------------------------------- /lib/uploadthing.ts: -------------------------------------------------------------------------------- 1 | import { generateReactHelpers } from "@uploadthing/react/hooks"; 2 | 3 | import type { OurFileRouter } from "@/app/api/uploadthing/core"; 4 | 5 | export const { useUploadThing, uploadFiles } = generateReactHelpers(); -------------------------------------------------------------------------------- /app/api/uploadthing/route.ts: -------------------------------------------------------------------------------- 1 | import { createNextRouteHandler } from "uploadthing/next"; 2 | 3 | import { ourFileRouter } from "./core"; 4 | 5 | // Export routes for Next App Router 6 | export const { GET, POST } = createNextRouteHandler({ 7 | router: ourFileRouter, 8 | }); -------------------------------------------------------------------------------- /app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | const Layout = ({ children }: { children: React.ReactNode }) => { 2 | return ( 3 |
4 | {children} 5 |
6 | ) 7 | } 8 | 9 | export default Layout -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | domains: ['utfs.io'], 5 | remotePatterns: [ 6 | { 7 | protocol: 'https', 8 | hostname: 'utfs.io', 9 | port: '' 10 | } 11 | ] 12 | } 13 | } 14 | 15 | module.exports = nextConfig 16 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } -------------------------------------------------------------------------------- /lib/database/models/category.model.ts: -------------------------------------------------------------------------------- 1 | import { Document, Schema, model, models } from "mongoose"; 2 | 3 | export interface ICategory extends Document { 4 | _id: string; 5 | name: string; 6 | } 7 | 8 | const CategorySchema = new Schema({ 9 | name: { type: String, required: true, unique: true }, 10 | }) 11 | 12 | const Category = models.Category || model('Category', CategorySchema); 13 | 14 | export default Category; -------------------------------------------------------------------------------- /app/(root)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Footer from "@/components/shared/Footer" 2 | import Header from "@/components/shared/Header" 3 | 4 | export default function RootLayout({ 5 | children, 6 | }: { 7 | children: React.ReactNode 8 | }) { 9 | return ( 10 |
11 |
12 |
{children}
13 |
14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { authMiddleware } from "@clerk/nextjs"; 2 | 3 | export default authMiddleware({ 4 | publicRoutes: [ 5 | '/', 6 | '/events/:id', 7 | '/api/webhook/clerk', 8 | '/api/webhook/stripe', 9 | '/api/uploadthing' 10 | ], 11 | ignoredRoutes: [ 12 | '/api/webhook/clerk', 13 | '/api/webhook/stripe', 14 | '/api/uploadthing' 15 | ] 16 | }); 17 | 18 | export const config = { 19 | matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'], 20 | }; 21 | -------------------------------------------------------------------------------- /constants/index.ts: -------------------------------------------------------------------------------- 1 | export const headerLinks = [ 2 | { 3 | label: 'Home', 4 | route: '/', 5 | }, 6 | { 7 | label: 'Create Event', 8 | route: '/events/create', 9 | }, 10 | { 11 | label: 'My Profile', 12 | route: '/profile', 13 | }, 14 | ] 15 | 16 | export const eventDefaultValues = { 17 | title: '', 18 | description: '', 19 | location: '', 20 | imageUrl: '', 21 | startDateTime: new Date(), 22 | endDateTime: new Date(), 23 | categoryId: '', 24 | price: '', 25 | isFree: false, 26 | url: '', 27 | } 28 | -------------------------------------------------------------------------------- /lib/database/models/user.model.ts: -------------------------------------------------------------------------------- 1 | import { Schema, model, models } from "mongoose"; 2 | 3 | const UserSchema = new Schema({ 4 | clerkId: { type: String, required: true, unique: true }, 5 | email: { type: String, required: true, unique: true }, 6 | username: { type: String, required: true, unique: true }, 7 | firstName: { type: String, required: true }, 8 | lastName: {type: String, required: true }, 9 | photo: { type: String, required: true }, 10 | }) 11 | 12 | const User = models.User || model('User', UserSchema); 13 | 14 | export default User; -------------------------------------------------------------------------------- /public/assets/icons/loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /lib/database/index.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const MONGODB_URI = process.env.MONGODB_URI; 4 | 5 | let cached = (global as any).mongoose || { conn: null, promise: null }; 6 | 7 | export const connectToDatabase = async () => { 8 | if (cached.conn) return cached.conn; 9 | 10 | if(!MONGODB_URI) throw new Error('MONGODB_URI is missing'); 11 | 12 | cached.promise = cached.promise || mongoose.connect(MONGODB_URI, { 13 | dbName: 'evently', 14 | bufferCommands: false, 15 | }) 16 | 17 | cached.conn = await cached.promise; 18 | 19 | return cached.conn; 20 | } -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/validator.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod" 2 | 3 | export const eventFormSchema = z.object({ 4 | title: z.string().min(3, 'Title must be at least 3 characters'), 5 | description: z.string().min(3, 'Description must be at least 3 characters').max(400, 'Description must be less than 400 characters'), 6 | location: z.string().min(3, 'Location must be at least 3 characters').max(400, 'Location must be less than 400 characters'), 7 | imageUrl: z.string(), 8 | startDateTime: z.date(), 9 | endDateTime: z.date(), 10 | categoryId: z.string(), 11 | price: z.string(), 12 | isFree: z.boolean(), 13 | url: z.string().url() 14 | }) -------------------------------------------------------------------------------- /components/shared/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image" 2 | import Link from "next/link" 3 | 4 | const Footer = () => { 5 | return ( 6 |
7 |
8 | 9 | logo 15 | 16 | 17 |

2023 Evently. All Rights reserved.

18 |
19 |
20 | ) 21 | } 22 | 23 | export default Footer -------------------------------------------------------------------------------- /app/(root)/events/create/page.tsx: -------------------------------------------------------------------------------- 1 | import EventForm from "@/components/shared/EventForm" 2 | import { auth } from "@clerk/nextjs"; 3 | 4 | const CreateEvent = () => { 5 | const { sessionClaims } = auth(); 6 | 7 | const userId = sessionClaims?.userId as string; 8 | 9 | return ( 10 | <> 11 |
12 |

Create Event

13 |
14 | 15 |
16 | 17 |
18 | 19 | ) 20 | } 21 | 22 | export default CreateEvent -------------------------------------------------------------------------------- /public/assets/icons/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/icons/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Poppins } from 'next/font/google' 3 | import { ClerkProvider } from '@clerk/nextjs' 4 | 5 | import './globals.css' 6 | 7 | const poppins = Poppins({ 8 | subsets: ['latin'], 9 | weight: ['400', '500', '600', '700'], 10 | variable: '--font-poppins', 11 | }) 12 | 13 | export const metadata: Metadata = { 14 | title: 'Evently', 15 | description: 'Evently is a platform for event management.', 16 | icons: { 17 | icon: '/assets/images/logo.svg' 18 | } 19 | } 20 | 21 | export default function RootLayout({ 22 | children, 23 | }: { 24 | children: React.ReactNode 25 | }) { 26 | return ( 27 | 28 | 29 | {children} 30 | 31 | 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /lib/actions/category.actions.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | 3 | import { CreateCategoryParams } from "@/types" 4 | import { handleError } from "../utils" 5 | import { connectToDatabase } from "../database" 6 | import Category from "../database/models/category.model" 7 | 8 | export const createCategory = async ({ categoryName }: CreateCategoryParams) => { 9 | try { 10 | await connectToDatabase(); 11 | 12 | const newCategory = await Category.create({ name: categoryName }); 13 | 14 | return JSON.parse(JSON.stringify(newCategory)); 15 | } catch (error) { 16 | handleError(error) 17 | } 18 | } 19 | 20 | export const getAllCategories = async () => { 21 | try { 22 | await connectToDatabase(); 23 | 24 | const categories = await Category.find(); 25 | 26 | return JSON.parse(JSON.stringify(categories)); 27 | } catch (error) { 28 | handleError(error) 29 | } 30 | } -------------------------------------------------------------------------------- /components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface TextareaProps 6 | extends React.TextareaHTMLAttributes {} 7 | 8 | const Textarea = React.forwardRef( 9 | ({ className, ...props }, ref) => { 10 | return ( 11 |