├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── about │ └── page.tsx ├── actions │ ├── createProject.ts │ ├── deleteFeedback.ts │ └── deleteProject.ts ├── api │ ├── auth │ │ └── [...nextauth] │ │ │ └── route.ts │ └── feedback │ │ └── route.ts ├── dashboard │ ├── feedbacks │ │ └── [id] │ │ │ └── page.tsx │ ├── layout.tsx │ ├── page.tsx │ ├── pricing │ │ └── page.tsx │ └── steps │ │ └── page.tsx ├── favicon.ico ├── global-error.tsx ├── globals.css ├── layout.tsx ├── not-found.tsx ├── opengraph-image.png ├── page.tsx ├── privacy │ └── page.tsx ├── robots.ts └── sitemap.ts ├── bun.lockb ├── components.json ├── components ├── Accordian.tsx ├── Announcement.tsx ├── AuthButtons.tsx ├── CodeTabs.tsx ├── CopyToClipboard.tsx ├── CreateProject.tsx ├── DashboardCards.tsx ├── FeaturesSection.tsx ├── Footer.tsx ├── GetStartedButton.tsx ├── HomeAvatars.tsx ├── HomeForm.tsx ├── HowToUse.tsx ├── Navbar.tsx ├── Pricing.tsx ├── ProjectDeleteButton.tsx ├── Sidebar.tsx ├── TechStack.tsx ├── feedback-table.tsx ├── feedback-view.tsx ├── magicui │ ├── blur-fade.tsx │ ├── border-beam.tsx │ └── particles.tsx ├── steps-page.tsx ├── theme-provider.tsx └── ui │ ├── accordion.tsx │ ├── avatar.tsx │ ├── button.tsx │ ├── card.tsx │ ├── dialog.tsx │ ├── dropdown-menu.tsx │ ├── hero-video-dialog.tsx │ ├── input.tsx │ ├── label.tsx │ ├── progress.tsx │ ├── rainbow-button.tsx │ ├── select.tsx │ ├── separator.tsx │ ├── sheet.tsx │ ├── sonner.tsx │ ├── table.tsx │ ├── tabs.tsx │ ├── textarea.tsx │ └── tooltip.tsx ├── env.example ├── lib ├── auth.ts ├── db.ts └── utils.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── prisma ├── migrations │ ├── 20240806032711_initial_migration_for_authentication │ │ └── migration.sql │ ├── 20240806042013_corrected_auth_table │ │ └── migration.sql │ ├── 20240806174843_added_project_schema │ │ └── migration.sql │ ├── 20240807043409_added_schema_for_the_feedbacks_and_updated_at_constraints │ │ └── migration.sql │ ├── 20240807103708_fixed_the_feedback_schema │ │ └── migration.sql │ ├── 20240807105030_refactored_whole_schema │ │ └── migration.sql │ ├── 20240807180653_added_foreign_key_in_feedback_model │ │ └── migration.sql │ ├── 20240807181025_refatored │ │ └── migration.sql │ ├── 20240807181559_refactored │ │ └── migration.sql │ ├── 20240809085715_adding │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── public ├── Logo.png ├── peerlist.png ├── process1.png ├── process2.png └── process3.png ├── 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 | # 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 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | .env 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Opinify.in 2 | 3 | **Opinify.in** is a feedback collection platform that provides embeddable feedback forms for websites. Users can collect feedback directly from their websites and manage it through a centralized dashboard. This SaaS project is developed using React and TypeScript. 4 | 5 | ## Features 6 | 7 | - **Embeddable Feedback Forms**: Easily embed feedback forms on your website. 8 | - **Centralized Dashboard**: View and manage all feedback from a single, intuitive dashboard. 9 | - **Customizable Modal**: Customize the modal design to enhance the user experience. ( _🚨 Not implemented yet_) 10 | - **Real-time Updates**: Receive real-time updates and notifications for feedback forms and the dashboard. 11 | - **Analytics & Reporting**: Access detailed feedback reports and analytics insights. ( _🚨 Not implemented yet_) 12 | 13 | ## Installation 14 | 15 | ### Prerequisites 16 | 17 | - Node.js (>=14.x) 18 | - npm (>=6.x) or yarn 19 | 20 | ### Clone the Repository 21 | 22 | ```bash 23 | git clone https://github.com/avayyyyyyy/opinify.git 24 | cd opinify 25 | ``` 26 | 27 | ### Install Dependencies 28 | 29 | ```bash 30 | npm install 31 | # or 32 | yarn install 33 | # or 34 | bun install 35 | ``` 36 | 37 | ### Run the Development Server 38 | 39 | ```bash 40 | npm run dev 41 | # or 42 | yarn dev 43 | # or 44 | bun dev 45 | ``` 46 | 47 | Open your browser and navigate to `http://localhost:3000` to see the application in action. 48 | 49 | ## Configuration 50 | 51 | - **Environment Variables**: Create a `.env` file in the root directory and set the required environment variables. 52 | 53 | ```env 54 | NEXTAUTH_SECRET= 55 | DATABASE_URL= 56 | GOOGLE_CLIENT_ID= 57 | GOOGLE_CLIENT_SECRET= 58 | AUTH_TRUST_HOST=http://localhost:3000 59 | ``` 60 | 61 | - **Feedback Form Integration**: Embed the feedback form into your website using the provided embed code. 62 | 63 | ## Usage 64 | 65 | 1. **Embedding Feedback Form**: 66 | 67 | - Go to your dashboard and create a new feedback form. 68 | - Copy the provided embed code and paste it into your website's HTML where you want the feedback form to appear. 69 | 70 | 2. **Viewing Feedback**: 71 | 72 | - Log in to the Opinify.in dashboard. 73 | - Navigate to the 'Feedback' section to view and manage collected feedback. 74 | 75 | ## Contributing 76 | 77 | Contributions are welcome! Please follow these guidelines to contribute: 78 | 79 | 1. Fork the repository. 80 | 2. Create a new branch (`git checkout -b feature/your-feature`). 81 | 3. Make your changes. 82 | 4. Commit your changes (`git commit -am 'Add new feature'`). 83 | 5. Push to the branch (`git push origin feature/your-feature`). 84 | 6. Create a new Pull Request. 85 | 86 | ## License 87 | 88 | Distributed under the MIT License. See `LICENSE` for more information. 89 | 90 | ## Contact 91 | 92 | Shubhankit Jain - [jabhi465@gmail.com](mailto:jabhi465@gmail.com) 93 | 94 | Project Link: [https://opinify.in/](https://opinify.in/) 95 | -------------------------------------------------------------------------------- /app/about/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Image from "next/image"; 3 | import Link from "next/link"; 4 | import { Metadata } from "next"; 5 | 6 | export const metadata: Metadata = { 7 | title: "About", 8 | }; 9 | 10 | const Article1 = () => { 11 | return ( 12 |
13 |
14 |
15 |
16 |

17 | About Page 18 |

19 | 20 |

21 | What is Opinify.in? 22 |

23 |

24 | {new Date().toLocaleString("default", { month: "long" })},{" "} 25 | {new Date().getFullYear()} 26 |

27 | 28 |
29 |

30 | Welcome to Opinify.in, your go-to solution for effortless and 31 | efficient feedback collection. In today’s fast-paced digital 32 | world, understanding user experience and gathering actionable 33 | insights is crucial for success. Opinify.in is here to simplify 34 | that process, offering a powerful platform to collect, manage, 35 | and analyze feedback directly from your website. 36 |

37 | About Hero 45 |

46 | Our Technology 47 |

48 |

49 | Our platform is built with a modern and robust tech stack to 50 | ensure a seamless and reliable experience: 51 |

52 |
    53 |
  • 54 | React.js: For 55 | building a dynamic and responsive user interface. 56 |
  • 57 |
  • 58 | Next.js: To 59 | offer server-side rendering and static site generation for 60 | improved performance and SEO. 61 |
  • 62 |
  • 63 | TypeScript:{" "} 64 | Ensuring type safety and better development experience. 65 |
  • 66 |
  • 67 | PostgreSQL: A 68 | powerful and scalable database to handle your data needs 69 | efficiently. 70 |
  • 71 |
  • 72 | Prisma: For an 73 | intuitive and flexible database access layer. 74 |
  • 75 |
  • 76 | NextAuth: To 77 | manage authentication and user sessions securely. 78 |
  • 79 |
80 |

81 | Our Mission 82 |

83 |

84 | At Opinify.in, we are dedicated to helping businesses and 85 | developers harness the power of user feedback. Our mission is to 86 | provide a tool that not only simplifies the feedback collection 87 | process but also delivers meaningful insights that can drive 88 | growth and innovation. By leveraging advanced technology and 89 | user-centric design, we aim to transform the way you engage with 90 | your audience and improve your offerings. 91 |

92 |
93 |

94 | Join the Opinify.in community today and discover how our 95 | platform can help you turn user feedback into actionable 96 | strategies for success. Experience a new level of insight and 97 | efficiency with Opinify.in. 98 |

99 |
100 |
101 | {/*
102 |
109 |
*/} 110 | 111 | {/*
112 | AuroraUI components 120 |
*/} 121 |
122 |

123 | Still Something on Your Mind? 124 |

125 |

126 | We believe in the power of communication and are always here to 127 | help. If you have any questions, suggestions, or need further 128 | assistance, don’t hesitate to reach out. Whether you’re curious 129 | about how Opinify.in works, need help integrating it into your 130 | website, or simply want to share your thoughts, we’re all ears. 131 | Your feedback is as important to us as it is to you. 132 |

133 |
134 |

135 | Let’s{" "} 136 | 141 | connect 142 | {" "} 143 | and make the most out of your feedback journey with 144 | Opinify.in. 145 |

146 |
147 |
148 |
149 |
150 |
151 |
152 | ); 153 | }; 154 | 155 | export default Article1; 156 | -------------------------------------------------------------------------------- /app/actions/createProject.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | import { auth } from "@/lib/auth"; 3 | import prisma from "@/lib/db"; 4 | import { z } from "zod"; 5 | 6 | export const submitProject = async ({ 7 | name, 8 | url, 9 | description, 10 | }: { 11 | name: string; 12 | url: string; 13 | description: string; 14 | }) => { 15 | const user = await auth(); 16 | 17 | const findUser = await prisma.user.findUnique({ 18 | where: { 19 | email: user?.user?.email!, 20 | }, 21 | }); 22 | 23 | const projects = await prisma.project.findMany({ 24 | where: { 25 | userId: findUser?.id, 26 | }, 27 | }); 28 | 29 | if (projects.length >= 5) { 30 | // console.log("Maximum project limit reached"); 31 | return { success: false }; 32 | } 33 | 34 | try { 35 | const schema = z.object({ 36 | name: z.string(), 37 | url: z.string().url(), 38 | description: z.string().min(5), 39 | }); 40 | 41 | const data = schema.safeParse({ 42 | name, 43 | url, 44 | description, 45 | }); 46 | 47 | if (!data.success) { 48 | // console.log("Invalid data"); 49 | throw new Error("Invalid data"); 50 | } 51 | if (!findUser?.id) { 52 | // console.log("User not found"); 53 | throw new Error("User not found"); 54 | } 55 | 56 | const dbSave = await prisma.project.create({ 57 | data: { 58 | name: name, 59 | url: url, 60 | description: description, 61 | userId: findUser?.id, 62 | }, 63 | }); 64 | 65 | if (!dbSave) { 66 | // console.log("Failed to save data"); 67 | throw new Error("Failed to save data"); 68 | } 69 | 70 | // console.log("Project saved:", dbSave); 71 | 72 | return { success: true, projectId: dbSave.id }; 73 | } catch (error) { 74 | // console.log("Error:", error); 75 | return { success: false }; 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /app/actions/deleteFeedback.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import prisma from "@/lib/db"; 4 | 5 | export const deleteFeedback = async (id: string) => { 6 | try { 7 | // console.log("id: ", id); 8 | const deleteFeedback = await prisma.feedback.delete({ 9 | where: { 10 | id: id, 11 | }, 12 | }); 13 | 14 | // console.log("deleteFeedback: ", deleteFeedback); 15 | 16 | return { success: true }; 17 | } catch (error) { 18 | return { success: false }; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /app/actions/deleteProject.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { auth } from "@/lib/auth"; 4 | import prisma from "@/lib/db"; 5 | 6 | export const deleteProject = async (id: string) => { 7 | try { 8 | const user = await auth(); 9 | 10 | if (!user?.user?.email) { 11 | // console.log("User not authenticated"); 12 | return { success: false, error: "User not authenticated" }; 13 | } 14 | 15 | // Fetch the user ID based on email 16 | const userRecord = await prisma.user.findUnique({ 17 | where: { 18 | email: user.user.email, 19 | }, 20 | select: { 21 | id: true, 22 | }, 23 | }); 24 | 25 | if (!userRecord?.id) { 26 | // console.log("User ID not found"); 27 | return { success: false, error: "User ID not found" }; 28 | } 29 | 30 | // Check if the project exists for the user 31 | const project = await prisma.project.findUnique({ 32 | where: { 33 | id: id, 34 | userId: userRecord.id, 35 | }, 36 | }); 37 | 38 | if (!project) { 39 | // console.log("Project not found"); 40 | return { success: false, error: "Project not found" }; 41 | } 42 | 43 | // Delete the project 44 | const deletedProject = await prisma.project.delete({ 45 | where: { 46 | id: id, 47 | }, 48 | }); 49 | 50 | // console.log("Deleted project:", deletedProject); 51 | 52 | return { success: true }; 53 | } catch (error) { 54 | console.error("Error deleting project: ", error); 55 | return { success: false, error: "Error deleting project" }; 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import { handlers } from "@/lib/auth"; 2 | export const { GET, POST } = handlers; 3 | -------------------------------------------------------------------------------- /app/api/feedback/route.ts: -------------------------------------------------------------------------------- 1 | import prisma from "@/lib/db"; 2 | import { NextRequest, NextResponse } from "next/server"; 3 | import { z } from "zod"; 4 | 5 | const feedbackSchema = z.object({ 6 | name: z.string(), 7 | email: z.string().email(), 8 | feedback: z.string(), 9 | rating: z.number().int().min(1).max(5), 10 | projectid: z.string(), 11 | }); 12 | 13 | export async function POST(req: NextRequest) { 14 | try { 15 | const feedback = await req.json(); 16 | 17 | // console.log("Body: ", feedback); 18 | 19 | // Validate feedback data using zod 20 | const validation = feedbackSchema.safeParse(feedback); 21 | 22 | // console.log("validation", validation); 23 | 24 | if (!validation.success) { 25 | return NextResponse.json( 26 | { success: false, error: "Invalid feedback data" }, 27 | { status: 400 } 28 | ); 29 | } 30 | 31 | // Save feedback to database 32 | const submitFeedback = await prisma.feedback.create({ 33 | data: { 34 | name: validation.data.name, 35 | email: validation.data.email, 36 | feedback: validation.data.feedback, 37 | rating: validation.data.rating, 38 | projectId: validation.data.projectid, 39 | }, 40 | }); 41 | 42 | // console.log("submitFeedback: ", submitFeedback); 43 | 44 | return NextResponse.json( 45 | { 46 | success: true, 47 | message: "Feedback received successfully", 48 | }, 49 | { status: 200 } 50 | ); 51 | } catch (error) { 52 | return NextResponse.json( 53 | { success: false, error: "Failed to process feedback" }, 54 | { status: 500 } 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/dashboard/feedbacks/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import { FeedbackTable } from "@/components/feedback-table"; 2 | import { auth } from "@/lib/auth"; 3 | import prisma from "@/lib/db"; 4 | import { redirect } from "next/navigation"; 5 | import React from "react"; 6 | import EmbedCodeTabs from "@/components/CodeTabs"; // Adjust the path as needed 7 | import { Metadata } from "next"; 8 | import Markdown from "react-markdown"; 9 | 10 | interface Params { 11 | params: { id: string }; 12 | } 13 | 14 | async function getProjectName(id: string) { 15 | const isValidID = await prisma.project.findUnique({ 16 | where: { 17 | id: id, 18 | }, 19 | }); 20 | 21 | if (!isValidID) { 22 | return redirect("/error"); 23 | } 24 | const projectName = await prisma.project.findUnique({ 25 | where: { 26 | id: id, 27 | }, 28 | select: { 29 | name: true, 30 | }, 31 | }); 32 | return projectName?.name; 33 | } 34 | 35 | export async function generateMetadata({ params }: Params): Promise { 36 | const projectName = await getProjectName(params.id); 37 | return { 38 | title: projectName || "Project Page", 39 | }; 40 | } 41 | 42 | const Page = async ({ params }: Params) => { 43 | try { 44 | const user = await auth(); 45 | 46 | if (!user) { 47 | return redirect("/"); 48 | } 49 | 50 | const feedbacks = await prisma.feedback.findMany({ 51 | where: { 52 | projectId: params.id, 53 | }, 54 | }); 55 | 56 | feedbacks.forEach((feedback) => { 57 | feedback.createdAt = new Date(feedback.createdAt); 58 | }); 59 | 60 | console.log("feedbacks: ", feedbacks); 61 | let projectName = await getProjectName(params.id); 62 | // console.log("id: ", params.id); 63 | 64 | return ( 65 |
66 | {/*
67 |
68 | What our expert thinks about you website 🤖 69 |
70 |
71 | 72 | {`"general_feedback": "The provided feedback data seems to lack substance and clarity. Most of the feedback entries appear to be test data with repetitive or nonsensical content. ", "specific_feedback": ["- **Feedback Content:** The 'feedback' field often contains repeated strings (\"clzl2hbzl0002xqbah92ket38\") or meaningless sequences (\"jnjnjnjnjnjnj\"), which makes it difficult to derive any meaningful insights. ", "- **User Information:** Multiple entries share the same email address (\"jabhi465@gmail.com\"), potentially indicating test submissions or inaccurate data collection.", "- **Data Relevance:** Without context about the website itself, it's challenging to provide specific recommendations based on the feedback. ", "- **Actionable Insights:** To extract valuable guidance, focus on gathering specific and descriptive feedback from real users about their experience on the website. "], "recommendation": "Encourage users to provide detailed feedback on specific aspects of the website, such as design, usability, content, or functionality. This will help you identify areas for improvement and enhance the overall user experience."`} 73 | 74 |
75 |
*/} 76 |
77 |
81 | 🚨 Note: 82 |
83 |
84 | Please embed the updated widget code from below and paste it in your 85 | website. Thank you! 86 |
87 |
88 |
89 | {feedbacks.length > 0 ? ( 90 | 97 | ) : ( 98 |

No feedbacks found 🥲...

99 | )} 100 |
101 | 102 |
103 | ); 104 | } catch (error) { 105 | console.error("Error fetching feedbacks:", error); 106 | return
Error fetching feedbacks
; 107 | } 108 | }; 109 | 110 | export default Page; 111 | -------------------------------------------------------------------------------- /app/dashboard/layout.tsx: -------------------------------------------------------------------------------- 1 | import Sidebar from "@/components/Sidebar"; 2 | import { auth } from "@/lib/auth"; 3 | import { redirect } from "next/navigation"; 4 | import React from "react"; 5 | 6 | const layout = async ({ children }: { children: React.ReactNode }) => { 7 | const user = await auth(); 8 | 9 | if (!user?.user?.email) { 10 | return redirect("/"); 11 | } 12 | 13 | return ( 14 |
15 | 16 | {children} 17 |
18 | ); 19 | }; 20 | 21 | export default layout; 22 | -------------------------------------------------------------------------------- /app/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | import CreateProject from "@/components/CreateProject"; 2 | import DashboardCards from "@/components/DashboardCards"; 3 | import { Button } from "@/components/ui/button"; 4 | import { auth } from "@/lib/auth"; 5 | import prisma from "@/lib/db"; 6 | import { Metadata } from "next"; 7 | import { redirect } from "next/navigation"; 8 | import React from "react"; 9 | 10 | export const metadata: Metadata = { 11 | title: "Dashboard", 12 | }; 13 | 14 | const Page = async () => { 15 | const user = await auth(); 16 | 17 | // console.log(user); 18 | 19 | if (!user) { 20 | return redirect("/"); 21 | } 22 | 23 | const currentUser = await prisma.user.findUnique({ 24 | where: { 25 | email: user?.user?.email!, 26 | }, 27 | }); 28 | 29 | const data = await prisma.project.findMany({ 30 | where: { 31 | userId: currentUser?.id, 32 | }, 33 | }); 34 | 35 | // console.log(data); 36 | 37 | return ( 38 |
39 |
40 | {" "} 41 |

Dashboard Page 📄

42 | {data.length > 5 ? ( 43 | 46 | ) : null} 47 |
48 |
49 |
50 | {data && data.length > 0 ? ( 51 | data.map((data, index) => ( 52 | 59 | )) 60 | ) : ( 61 |
62 |
No projects found 🥲
63 | 64 |
65 | )} 66 |
67 |
68 | ); 69 | }; 70 | 71 | export default Page; 72 | -------------------------------------------------------------------------------- /app/dashboard/pricing/page.tsx: -------------------------------------------------------------------------------- 1 | import PricingPage from "@/components/Pricing"; 2 | import { auth } from "@/lib/auth"; 3 | import { Metadata } from "next"; 4 | import { redirect } from "next/navigation"; 5 | import React from "react"; 6 | 7 | export const metadata: Metadata = { 8 | title: "Pricing", 9 | }; 10 | 11 | const page = async () => { 12 | const user = await auth(); 13 | 14 | if (!user) { 15 | return redirect("/"); 16 | } 17 | 18 | return ( 19 |
20 | 21 |
22 | ); 23 | }; 24 | 25 | export default page; 26 | -------------------------------------------------------------------------------- /app/dashboard/steps/page.tsx: -------------------------------------------------------------------------------- 1 | import { StepsPage } from "@/components/steps-page"; 2 | import { Metadata } from "next"; 3 | import React from "react"; 4 | 5 | export const metadata: Metadata = { 6 | title: "Steps", 7 | }; 8 | 9 | const page = () => { 10 | return ( 11 |
12 | 13 |
14 | ); 15 | }; 16 | 17 | export default page; 18 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avayyyyyyy/Opinify/28d07c505e5d12896fa341847e59dbe50567f186/app/favicon.ico -------------------------------------------------------------------------------- /app/global-error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import Link from "next/link"; 4 | import React from "react"; 5 | 6 | interface ErrorPageProps { 7 | statusCode?: number; 8 | title?: string; 9 | message?: string; 10 | } 11 | 12 | const ErrorPage: React.FC = ({ 13 | statusCode = 500, 14 | title = "An error occurred", 15 | message = "We're sorry, but something went wrong. Please try again later.", 16 | }) => { 17 | return ( 18 |
19 |
20 |

21 | {statusCode} | {title} 22 |

23 |

{message}

24 | 27 |

28 | - Opinify.in - 29 |

30 |
31 |
32 | ); 33 | }; 34 | 35 | export default ErrorPage; 36 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | * { 6 | margin: 0px; 7 | padding: 0px; 8 | box-sizing: border-box; 9 | } 10 | 11 | @layer base { 12 | :root { 13 | --background: 0 0% 100%; 14 | --foreground: 240 10% 3.9%; 15 | --card: 0 0% 100%; 16 | --card-foreground: 240 10% 3.9%; 17 | --popover: 0 0% 100%; 18 | --popover-foreground: 240 10% 3.9%; 19 | --primary: 240 5.9% 10%; 20 | --primary-foreground: 0 0% 98%; 21 | --secondary: 240 4.8% 95.9%; 22 | --secondary-foreground: 240 5.9% 10%; 23 | --muted: 240 4.8% 95.9%; 24 | --muted-foreground: 240 3.8% 46.1%; 25 | --accent: 240 4.8% 95.9%; 26 | --accent-foreground: 240 5.9% 10%; 27 | --destructive: 0 84.2% 60.2%; 28 | --destructive-foreground: 0 0% 98%; 29 | --border: 240 5.9% 90%; 30 | --input: 240 5.9% 90%; 31 | --ring: 240 5.9% 10%; 32 | --radius: 0.5rem; 33 | --chart-1: 12 76% 61%; 34 | --chart-2: 173 58% 39%; 35 | --chart-3: 197 37% 24%; 36 | --chart-4: 43 74% 66%; 37 | --chart-5: 27 87% 67%; 38 | --color-1: 0 100% 63%; 39 | --color-2: 270 100% 63%; 40 | --color-3: 210 100% 63%; 41 | --color-4: 195 100% 63%; 42 | --color-5: 90 100% 63%; 43 | } 44 | 45 | .dark { 46 | --background: 240 10% 3.9%; 47 | --foreground: 0 0% 98%; 48 | --card: 240 10% 3.9%; 49 | --card-foreground: 0 0% 98%; 50 | --popover: 240 10% 3.9%; 51 | --popover-foreground: 0 0% 98%; 52 | --primary: 0 0% 98%; 53 | --primary-foreground: 240 5.9% 10%; 54 | --secondary: 240 3.7% 15.9%; 55 | --secondary-foreground: 0 0% 98%; 56 | --muted: 240 3.7% 15.9%; 57 | --muted-foreground: 240 5% 64.9%; 58 | --accent: 240 3.7% 15.9%; 59 | --accent-foreground: 0 0% 98%; 60 | --destructive: 0 62.8% 30.6%; 61 | --destructive-foreground: 0 0% 98%; 62 | --border: 240 3.7% 15.9%; 63 | --input: 240 3.7% 15.9%; 64 | --ring: 240 4.9% 83.9%; 65 | --chart-1: 220 70% 50%; 66 | --chart-2: 160 60% 45%; 67 | --chart-3: 30 80% 55%; 68 | --chart-4: 280 65% 60%; 69 | --chart-5: 340 75% 55%; 70 | --color-1: 0 100% 63%; 71 | --color-2: 270 100% 63%; 72 | --color-3: 210 100% 63%; 73 | --color-4: 195 100% 63%; 74 | --color-5: 90 100% 63%; 75 | } 76 | } 77 | 78 | @layer base { 79 | * { 80 | @apply border-border; 81 | } 82 | body { 83 | @apply bg-background text-foreground; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { SpeedInsights } from "@vercel/speed-insights/next"; 2 | import { Toaster } from "@/components/ui/sonner"; 3 | import { ThemeProvider } from "@/components/theme-provider"; 4 | import type { Metadata } from "next"; 5 | import { Analytics } from "@vercel/analytics/react"; 6 | import { Poppins } from "next/font/google"; 7 | import "./globals.css"; 8 | import Navbar from "@/components/Navbar"; 9 | import Footer from "@/components/Footer"; 10 | import { SessionProvider } from "next-auth/react"; 11 | import Head from "next/head"; 12 | import NextTopLoader from "nextjs-toploader"; 13 | import Announcement from "@/components/Announcement"; 14 | 15 | const inter = Poppins({ 16 | subsets: ["latin"], 17 | weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"], 18 | }); 19 | 20 | export const metadata: Metadata = { 21 | metadataBase: new URL("https://www.opinify.in"), 22 | title: { 23 | default: 24 | "Opinify: Real-Time Feedback Platform | Enhance Customer Engagement", 25 | template: "%s - Opinify.in", 26 | }, 27 | description: 28 | "Enhance customer engagement with Opinify's real-time feedback platform. Collect, manage, and analyze user insights to drive data-driven improvements.", 29 | twitter: { 30 | card: "summary_large_image", 31 | title: "Opinify: Real-Time Feedback Platform | Enhance Customer Engagement", 32 | description: 33 | "Enhance customer engagement with Opinify's real-time feedback platform. Collect, manage, and analyze user insights to drive data-driven improvements.", 34 | images: ["https://utfs.io/f/9e53247a-090e-4b56-ae1e-03c9e4bff653-md47.png"], 35 | }, 36 | openGraph: { 37 | title: "Opinify: Real-Time Feedback Platform | Enhance Customer Engagement", 38 | description: 39 | "Enhance customer engagement with Opinify's real-time feedback platform. Collect, manage, and analyze user insights to drive data-driven improvements.", 40 | url: "https://www.opinify.in", 41 | siteName: "Opinify", 42 | images: [ 43 | { 44 | url: "https://utfs.io/f/9e53247a-090e-4b56-ae1e-03c9e4bff653-md47.png", 45 | width: 1200, 46 | height: 630, 47 | }, 48 | ], 49 | locale: "en_US", 50 | type: "website", 51 | }, 52 | other: { 53 | "schema-org": JSON.stringify({ 54 | "@context": "https://schema.org", 55 | "@type": "WebSite", 56 | name: "Opinify", 57 | url: "https://www.opinify.in", 58 | potentialAction: { 59 | "@type": "SearchAction", 60 | target: "https://www.opinify.in/search?q={search_term_string}", 61 | "query-input": "required name=search_term_string", 62 | }, 63 | }), 64 | }, 65 | }; 66 | 67 | declare global { 68 | namespace JSX { 69 | interface IntrinsicElements { 70 | "widget-web-component": React.DetailedHTMLProps< 71 | React.HTMLAttributes, 72 | HTMLElement 73 | > & { 74 | projectid: string; 75 | theme: string; 76 | }; 77 | } 78 | } 79 | } 80 | 81 | export default async function RootLayout({ 82 | children, 83 | }: Readonly<{ 84 | children: React.ReactNode; 85 | }>) { 86 | return ( 87 | 88 | 89 | 90 | 91 | 92 | 98 | 99 | 100 | 106 | 107 |
{children}
108 |