├── .eslintrc.json ├── app ├── favicon.ico ├── signup │ └── page.tsx ├── signin │ └── page.tsx ├── layout.tsx └── page.tsx ├── postcss.config.js ├── lib └── utils.ts ├── utils └── supabase.ts ├── next.config.js ├── providers ├── toast-provider.tsx ├── user-provider.tsx ├── theme-provider.tsx ├── modal-provider.tsx └── supabase-provider.tsx ├── types.ts ├── hooks ├── use-upload-modal.tsx └── use-user.tsx ├── .gitignore ├── public ├── vercel.svg └── next.svg ├── components ├── realtime.tsx ├── inputs │ ├── ImageInput.tsx │ └── attachment-details.tsx ├── ui │ ├── label.tsx │ ├── textarea.tsx │ ├── input.tsx │ ├── separator.tsx │ ├── mode-toggle.tsx │ ├── button.tsx │ ├── card.tsx │ ├── icons.tsx │ ├── select.tsx │ └── dropdown-menu.tsx ├── navbar.tsx ├── modals │ ├── gallery-tab.tsx │ ├── upload-modal.tsx │ └── modal.tsx ├── button.tsx ├── card-body.tsx ├── sign-in-form.tsx └── sign-up-form.tsx ├── tsconfig.json ├── middleware.ts ├── LICENSE ├── package.json ├── README.md ├── styles ├── mdx.css └── globals.css ├── types_db.ts └── tailwind.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhinishere/next-gallery-modal/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /utils/supabase.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | 3 | export default createClient( 4 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 5 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! 6 | ); 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | domains: [ 5 | "images.pexels.com", 6 | "images.unsplash.com", 7 | "vlagkaxrpfclxyzmgqdf.supabase.co", 8 | ], 9 | }, 10 | }; 11 | 12 | module.exports = nextConfig; 13 | -------------------------------------------------------------------------------- /providers/toast-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Toaster } from "react-hot-toast"; 4 | 5 | const ToastProvider = () => { 6 | return ( 7 | 15 | ); 16 | }; 17 | 18 | export default ToastProvider; 19 | -------------------------------------------------------------------------------- /providers/user-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { MyUserContextProvider } from "@/hooks/use-user"; 4 | 5 | interface UserProviderProps { 6 | children: React.ReactNode; 7 | } 8 | 9 | const UserProvider: React.FC = ({ children }) => { 10 | return {children}; 11 | }; 12 | 13 | export default UserProvider; 14 | -------------------------------------------------------------------------------- /providers/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 5 | import { type ThemeProviderProps } from "next-themes/dist/types"; 6 | 7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 8 | return {children}; 9 | } 10 | -------------------------------------------------------------------------------- /providers/modal-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useEffect, useState } from "react"; 3 | 4 | const ModalProvider = () => { 5 | const [isMounted, setIsMounted] = useState(false); 6 | 7 | useEffect(() => { 8 | setIsMounted(true); 9 | }, []); 10 | 11 | if (!isMounted) { 12 | return null; 13 | } 14 | 15 | return <>; 16 | }; 17 | 18 | export default ModalProvider; 19 | -------------------------------------------------------------------------------- /types.ts: -------------------------------------------------------------------------------- 1 | export interface ImageType { 2 | id: string; 3 | created_at: string; 4 | user_id: string; 5 | author: string; 6 | title: string; 7 | image_path: string; 8 | alt: string; 9 | caption: string; 10 | description: string; 11 | status?: string; 12 | } 13 | 14 | export interface UserDetails { 15 | id: string; 16 | first_name: string; 17 | last_name: string; 18 | full_name?: string; 19 | avatar_url?: string; 20 | } 21 | -------------------------------------------------------------------------------- /hooks/use-upload-modal.tsx: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface UploadModalProps { 4 | isOpen: boolean; 5 | imageSetter?: Function; 6 | onOpen: (imageSetter: Function) => void; 7 | onClose: () => void; 8 | } 9 | 10 | const useUploadModal = create((set) => ({ 11 | isOpen: false, 12 | onOpen: (imageSetter?: Function) => set({ isOpen: true, imageSetter }), 13 | onClose: () => set({ isOpen: false }), 14 | })); 15 | 16 | export default useUploadModal; 17 | -------------------------------------------------------------------------------- /.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 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | .env 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # vscode 39 | /.vscode/ 40 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/realtime.tsx: -------------------------------------------------------------------------------- 1 | import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"; 2 | import UploadModal from "./modals/upload-modal"; 3 | import { cookies } from "next/headers"; 4 | 5 | export const revalidate = 0; 6 | 7 | export default async function Realtime() { 8 | console.log("getting initial values"); 9 | const supabaseClient = createServerComponentClient({ cookies: cookies }); 10 | const { data, error } = await supabaseClient 11 | .from("images") 12 | .select("*") 13 | .order("created_at", { ascending: false }); 14 | if (error) { 15 | console.log(error.message); 16 | } 17 | return ; 18 | } 19 | -------------------------------------------------------------------------------- /components/inputs/ImageInput.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | interface ImageInputProps { 4 | image: string; 5 | title: string; 6 | selected?: boolean; 7 | onClick: (value: string) => void; 8 | } 9 | 10 | const ImageInput: React.FC = ({ 11 | image, 12 | title, 13 | selected, 14 | onClick, 15 | }) => { 16 | return ( 17 |
31 | 32 |
33 | ); 34 | }; 35 | 36 | export default ImageInput; 37 | -------------------------------------------------------------------------------- /providers/supabase-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Database } from "@/types_db"; 4 | import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; 5 | import { SessionContextProvider } from "@supabase/auth-helpers-react"; 6 | import { useState } from "react"; 7 | 8 | interface SupabaseProviderProps { 9 | children: React.ReactNode; 10 | } 11 | 12 | const SupabaseProvider: React.FC = ({ children }) => { 13 | const [supabaseClient] = useState(() => 14 | createClientComponentClient() 15 | ); 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | export default SupabaseProvider; 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 |