├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── (auth) │ ├── (routes) │ │ ├── sign-in │ │ │ └── page.tsx │ │ └── sign-up │ │ │ └── page.tsx │ └── layout.tsx ├── (dashboard) │ ├── (routes) │ │ ├── dashboard │ │ │ └── page.tsx │ │ ├── email-generator │ │ │ ├── constants.ts │ │ │ └── page.tsx │ │ └── settings │ │ │ └── page.tsx │ └── layout.tsx ├── (landing) │ ├── layout.tsx │ └── page.tsx ├── api │ ├── emailgenerator │ │ └── route.ts │ ├── lemon │ │ └── route.ts │ └── webhook │ │ └── route.ts ├── favicon.ico ├── globals.css └── layout.tsx ├── components.json ├── components ├── empty.tsx ├── features.tsx ├── footer.tsx ├── free-counter.tsx ├── heading.tsx ├── hero.tsx ├── loader.tsx ├── mobile-sidebar.tsx ├── modal-provider.tsx ├── navbar-landing.tsx ├── navbar.tsx ├── pro-modal.tsx ├── sidebar.tsx ├── subscription-button.tsx ├── toaster-provider.tsx └── ui │ ├── avatar.tsx │ ├── badge.tsx │ ├── button.tsx │ ├── card.tsx │ ├── dialog.tsx │ ├── form.tsx │ ├── input.tsx │ ├── label.tsx │ ├── progress.tsx │ ├── select.tsx │ ├── sheet.tsx │ └── textarea.tsx ├── constants.ts ├── hooks └── use-pro-modal.tsx ├── lib ├── api-limit.ts ├── lemon.ts ├── prismadb.ts ├── subscription.ts └── utils.ts ├── middleware.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── prisma ├── migrations │ ├── 20231217152338_init │ │ └── migration.sql │ ├── 20231217190716_init │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── public ├── empty.png ├── loading.png ├── logo.png ├── next.svg └── vercel.svg ├── tailwind.config.js ├── tailwind.config.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.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 | .env 17 | 18 | # production 19 | /build 20 | 21 | # misc 22 | .DS_Store 23 | *.pem 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # local env files 31 | .env*.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | next-env.d.ts 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 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). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | 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. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /app/(auth)/(routes)/sign-in/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignIn } from "@clerk/nextjs"; 2 | 3 | export default function SignInpage() { 4 | return ; 5 | } -------------------------------------------------------------------------------- /app/(auth)/(routes)/sign-up/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignUp } from "@clerk/nextjs"; 2 | 3 | export default function SignUppage() { 4 | return ; 5 | } -------------------------------------------------------------------------------- /app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | 2 | export default function AuthLayout({ 3 | children, 4 | }: { 5 | children: React.ReactNode 6 | }) { 7 | return ( 8 |
9 | {children} 10 | 11 |
12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Card } from '@/components/ui/card'; 4 | import { cn } from '@/lib/utils'; 5 | import { ArrowRight, MessageSquare } from 'lucide-react'; 6 | import { useRouter } from 'next/navigation'; 7 | 8 | 9 | const tools = [ 10 | { 11 | label: "Email generator", 12 | icon: MessageSquare, 13 | href: "/email-generator", 14 | color: "text-violet-500", 15 | bgColor : "bg-violet-500/10", 16 | 17 | } 18 | 19 | ] 20 | 21 | export default function DashboardPage() { 22 | 23 | const router = useRouter() 24 | return ( 25 |
26 |
27 |

28 | Generate your emails 29 |

30 |

31 | Use our AI assistant to generate awesome emails! 32 | 33 |

34 |
35 | {tools.map((tool) => ( 36 | router.push(tool.href)} 38 | className="p-4 border-black/5 flex items-center justify-between hover:shadow-md transition cursor-pointer" 39 | > 40 |
41 |
42 | 43 | 44 |
45 |
46 | {tool.label} 47 |
48 | 49 |
50 | 51 |
52 | ))} 53 |
54 | 55 |
56 |
57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/email-generator/constants.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod" 2 | 3 | export const formSchema = z.object({ 4 | message : z.string().min(1,{ 5 | message : "Write your email", 6 | }), 7 | tone : z.string().min(1), 8 | languages : z.string().min(1), 9 | words : z.string().min(1), 10 | purpose : z.string().min(1) 11 | }) 12 | 13 | export const languageOptions = [ 14 | {label: "Albanian", value: "Albanian"}, 15 | {label: "Arabic", value: "Arabic"}, 16 | {label: "Armenian", value: "Armenian"}, 17 | {label: "Awadhi", value: "Awadhi"}, 18 | {label: "Azerbaijani", value: "Azerbaijani"}, 19 | {label: "Bashkir", value: "Bashkir"}, 20 | {label: "Basque", value: "Basque"}, 21 | {label: "Belarusian", value: "Belarusian"}, 22 | {label: "Bengali", value: "Bengali"}, 23 | {label: "Bhojpuri", value: "Bhojpuri"}, 24 | {label: "Bosnian", value: "Bosnian"}, 25 | {label: "Brazilian Portuguese", value: "Brazilian Portuguese"}, 26 | {label: "Bulgarian", value: "Bulgarian"}, 27 | {label: "Cantonese (Yue)", value: "Cantonese (Yue)"}, 28 | {label: "Catalan", value: "Catalan"}, 29 | {label: "Chhattisgarhi", value: "Chhattisgarhi"}, 30 | {label: "Chinese", value: "Chinese"}, 31 | {label: "Croatian", value: "Croatian"}, 32 | {label: "Czech", value: "Czech"}, 33 | {label: "Danish", value: "Danish"}, 34 | {label: "Dogri", value: "Dogri"}, 35 | {label: "Dutch", value: "Dutch"}, 36 | {label: "English", value: "English"}, 37 | {label: "Estonian", value: "Estonian"}, 38 | {label: "Faroese", value: "Faroese"}, 39 | {label: "Finnish", value: "Finnish"}, 40 | {label: "French", value: "French"}, 41 | {label: "Galician", value: "Galician"}, 42 | {label: "Georgian", value: "Georgian"}, 43 | {label: "German", value: "German"}, 44 | {label: "Greek", value: "Greek"}, 45 | {label: "Gujarati", value: "Gujarati"}, 46 | {label: "Haryanvi", value: "Haryanvi"}, 47 | {label: "Hindi", value: "Hindi"}, 48 | {label: "Hungarian", value: "Hungarian"}, 49 | {label: "Indonesian", value: "Indonesian"}, 50 | {label: "Irish", value: "Irish"}, 51 | {label: "Italian", value: "Italian"}, 52 | {label: "Japanese", value: "Japanese"}, 53 | {label: "Javanese", value: "Javanese"}, 54 | {label: "Kannada", value: "Kannada"}, 55 | {label: "Kashmiri", value: "Kashmiri"}, 56 | {label: "Kazakh", value: "Kazakh"}, 57 | {label: "Konkani", value: "Konkani"}, 58 | {label: "Korean", value: "Korean"}, 59 | {label: "Kyrgyz", value: "Kyrgyz"}, 60 | {label: "Latvian", value: "Latvian"}, 61 | {label: "Lithuanian", value: "Lithuanian"}, 62 | {label: "Macedonian", value: "Macedonian"}, 63 | {label: "Maithili", value: "Maithili"}, 64 | {label: "Malay", value: "Malay"}, 65 | {label: "Maltese", value: "Maltese"}, 66 | {label: "Mandarin", value: "Mandarin"}, 67 | {label: "Mandarin Chinese", value: "Mandarin Chinese"}, 68 | {label: "Marathi", value: "Marathi"}, 69 | {label: "Marwari", value: "Marwari"}, 70 | {label: "Min Nan", value: "Min Nan"}, 71 | {label: "Moldovan", value: "Moldovan"}, 72 | {label: "Mongolian", value: "Mongolian"}, 73 | {label: "Montenegrin", value: "Montenegrin"}, 74 | {label: "Nepali", value: "Nepali"}, 75 | {label: "Norwegian", value: "Norwegian"}, 76 | {label: "Oriya", value: "Oriya"}, 77 | {label: "Pashto", value: "Pashto"}, 78 | {label: "Persian (Farsi)", value: "Persian (Farsi)"}, 79 | {label: "Polish", value: "Polish"}, 80 | {label: "Portuguese", value: "Portuguese"}, 81 | {label: "Punjabi", value: "Punjabi"}, 82 | {label: "Rajasthani", value: "Rajasthani"}, 83 | {label: "Romanian", value: "Romanian"}, 84 | {label: "Russian", value: "Russian"}, 85 | {label: "Sanskrit", value: "Sanskrit"}, 86 | {label: "Santali", value: "Santali"}, 87 | {label: "Serbian", value: "Serbian"}, 88 | {label: "Sindhi", value: "Sindhi"}, 89 | {label: "Sinhala", value: "Sinhala"}, 90 | {label: "Slovak", value: "Slovak"}, 91 | {label: "Slovene", value: "Slovene"}, 92 | {label: "Slovenian", value: "Slovenian"}, 93 | {label: "Spanish", value: "Spanish"}, 94 | {label: "Swahili", value: "Swahili"}, 95 | {label: "Swedish", value: "Swedish"}, 96 | {label: "Tajik", value: "Tajik"}, 97 | {label: "Tamil", value: "Tamil"}, 98 | {label: "Tatar", value: "Tatar"}, 99 | {label: "Telugu", value: "Telugu"}, 100 | {label: "Thai", value: "Thai"}, 101 | {label: "Turkish", value: "Turkish"}, 102 | {label: "Turkmen", value: "Turkmen"}, 103 | {label: "Ukrainian", value: "Ukrainian"}, 104 | {label: "Urdu", value: "Urdu"}, 105 | {label: "Uzbek", value: "Uzbek"}, 106 | 107 | ] 108 | 109 | export const voiceOptions = [ 110 | { 111 | value : "Professional", 112 | label : "Professional" 113 | }, 114 | { 115 | value : "Formal", 116 | label : "Formal" 117 | }, 118 | { 119 | value : "Informal", 120 | label : "Informal" 121 | }, 122 | { 123 | value : "Funny", 124 | label : "Funny" 125 | }, 126 | { 127 | value : "Urgent", 128 | label : "Urgent" 129 | }, 130 | ] 131 | export const Wordsptions = [ 132 | 133 | { 134 | value : "100", 135 | label : "Short" 136 | }, 137 | { 138 | value : "500", 139 | label : "Medium" 140 | }, 141 | { 142 | value : "1000", 143 | label : "Large" 144 | }, 145 | 146 | ] 147 | 148 | export const PurposeOptions = [ 149 | 150 | {label: "Apology", value: "an apology"}, 151 | {label: "Birthday Wishes", value: "a birthday Wishes"}, 152 | {label: "Celebration", value: "a celebration"}, 153 | {label: "Cold Email", value: " a cold Email"}, 154 | {label: "Company Updates", value: "a company update"}, 155 | {label: "Deadline Reminders", value: "a deadline reminder"}, 156 | {label: "Event Invitations", value: "a event invitation"}, 157 | {label: "Follow-ups", value: "a follow-up"}, 158 | {label: "Greetings", value: "a greeting"}, 159 | {label: "Holiday Greetings", value: "a holiday Greeting"}, 160 | {label: "Job Application", value: "a job Application"}, 161 | {label: "Meeting Requests", value: "a meeting Request"}, 162 | {label: "Mentorship Requests", value: "a Mentorship Request"}, 163 | {label: "Official Communications", value: "a official communication"}, 164 | {label: "Sales", value: "a sales message"}, 165 | {label: "Security Alerts", value: "a security alert"} 166 | 167 | 168 | ] 169 | 170 | -------------------------------------------------------------------------------- /app/(dashboard)/(routes)/email-generator/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import axios from "axios"; 4 | import * as z from "zod"; 5 | import { Heading } from "@/components/heading"; 6 | import { ClipboardCopy, Mail } from "lucide-react"; 7 | import { useForm } from "react-hook-form"; 8 | import { zodResolver } from "@hookform/resolvers/zod"; 9 | import toast from "react-hot-toast"; 10 | 11 | import { 12 | formSchema, 13 | languageOptions, 14 | PurposeOptions, 15 | voiceOptions, 16 | Wordsptions, 17 | } from "./constants"; 18 | import { Form, FormControl, FormField, FormItem } from "@/components/ui/form"; 19 | 20 | import { Button } from "@/components/ui/button"; 21 | import { useRouter } from "next/navigation"; 22 | import { useState } from "react"; 23 | import ChatCompletionRequestMessage from "openai"; 24 | 25 | import ReactMarkdown from "react-markdown"; 26 | import { Card, CardContent, CardHeader } from "@/components/ui/card"; 27 | import { Avatar } from "@radix-ui/react-avatar"; 28 | import { Label } from "@radix-ui/react-label"; 29 | import { 30 | Select, 31 | SelectContent, 32 | SelectItem, 33 | SelectTrigger, 34 | SelectValue, 35 | } from "@/components/ui/select"; 36 | import { Textarea } from "@/components/ui/textarea"; 37 | import * as copy from "copy-to-clipboard"; 38 | import Loader from "@/components/loader"; 39 | import Empty from "@/components/empty"; 40 | 41 | 42 | export default function EmailGeneratorPage() { 43 | const router = useRouter(); 44 | const [messages, setMessages] = useState([]); 45 | 46 | const form = useForm>({ 47 | resolver: zodResolver(formSchema), 48 | defaultValues: { 49 | message: "", 50 | words: "500", 51 | languages: "English", 52 | tone: "Formal", 53 | purpose : "a greeting" 54 | }, 55 | }); 56 | 57 | const isLoading = form.formState.isSubmitting; 58 | 59 | const onSubmit = async (values: z.infer) => { 60 | try { 61 | setMessages([]); 62 | const email = 63 | "Generate an email about " + values.purpose + " with the message " + 64 | values.message + 65 | " words limit " + 66 | values.words + 67 | " language " + 68 | values.languages + 69 | " voice tone " + 70 | values.tone; 71 | const userMessage: ChatCompletionRequestMessage = { 72 | // @ts-ignore 73 | role: "user", 74 | content: email, 75 | }; 76 | const newEmail = [userMessage]; 77 | 78 | const response = await axios.post("/api/emailgenerator", { 79 | messages: newEmail, 80 | }); 81 | 82 | 83 | setMessages(response.data); 84 | 85 | form.reset(); 86 | } catch (error: any) { 87 | } finally { 88 | router.refresh(); 89 | } 90 | }; 91 | 92 | const copyToClipboard = () => { 93 | // @ts-ignore 94 | if (messages.content) { 95 | // @ts-ignore 96 | copy(messages.content); 97 | } 98 | 99 | toast.success("Copied"); 100 | }; 101 | 102 | return ( 103 |
104 | 111 |
112 |
113 | 114 | 115 |
116 | 117 |

Compose the perfect email

118 |
119 |
120 | 121 |
122 | 123 |
124 | 125 | 126 | ( 129 | 130 | 131 |