testimonails added

This commit is contained in:
Cypro Freelance 2024-07-23 22:38:18 +05:30
parent 54ac1d08d6
commit b22184af49
22 changed files with 329 additions and 400 deletions

View file

@ -1,27 +0,0 @@
name: Deploy MDX Content
on:
push:
branches:
- main
paths:
- "data/pages/**"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Install dependencies
run: npm install
- name: Run MDX build or processing script
run: npm run build-mdx

View file

@ -152,46 +152,6 @@ const AdminLogPage = () => {
}
};
const createNewPage = async () => {
setLoading(true);
const response = await fetch(`/api/mdx/pages`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: pageTitle }),
});
if (response.ok) {
fetchPages();
setLoading(false);
toast({ description: "Page successfully created" });
setOpen(false);
setPageTitle("");
} else {
setLoading(false);
toast({ description: "Page creation Failed", variant: "destructive" });
}
};
const handlePageSelect = async (slug: string) => {
try {
const response = await fetch(`/api/mdx/pages/${slug}`);
if (response.ok) {
const data: PageEntry = await response.json();
setSelectedPage(data);
} else {
toast({
description: "Failed to fetch page data",
variant: "destructive",
});
}
} catch (error: any) {
toast({
description: error.message || "Failed to fetch page data",
variant: "destructive",
});
}
};
return (
<section id="logs-page" className="wrapper container">
<h1 className="text-3xl font-bold py-6">Server Logs Form</h1>
@ -268,69 +228,6 @@ const AdminLogPage = () => {
</form>
</Form>
{/* Section to create new page */}
<section id="create-page" className="py-16">
<h2 className="text-3xl md:text-4xl font-bold mb-2">Multi Log page</h2>
<Button variant={"secondary"} onClick={() => setOpen(true)}>
Create New Page
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Enter Page Title</DialogTitle>
</DialogHeader>
<Input
value={pageTitle}
onChange={(e) => setPageTitle(e.target.value)}
placeholder="Page Title"
/>
<DialogFooter>
<Button onClick={createNewPage} disabled={loading}>
Continue
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</section>
{/* Section to list and delete pages */}
<section id="pages-list" className="pb-16">
<h2 className="text-3xl md:text-4xl font-bold">Existing Pages</h2>
<p className="mb-4">Total Pages: {pages.length}</p>
<Table className="w-full mt-4 border-muted">
<TableHeader>
<TableRow>
<TableHead className="border-b px-4 py-2">Slug</TableHead>
<TableHead className="border-b px-4 py-2">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{pages.map((page) => (
<TableRow key={page.slug}>
<TableCell className="border-b px-4 py-2">
<a
href={`/changelogs/${page.slug}`}
className="text-blue-500 underline"
>
{page.slug}
</a>
</TableCell>
<TableCell className="border-b px-4 py-2">
<Button
variant={"outline"}
onClick={() =>
router.push(`/admin/changelogs/${page.slug}`)
}
>
Edit
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</section>
{/* Section to list and delete logs */}
<section id="logs-list" className="py-16 md:py-24">
<h2 className="text-3xl md:text-4xl font-bold">Existing Logs</h2>

View file

@ -1,31 +0,0 @@
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import { serialize } from "next-mdx-remote/serialize";
import { MDXRemote } from "next-mdx-remote/rsc";
import ReactMarkdown from "react-markdown";
import { ChangelogLayout } from "@/components/shared/providers/changelogLayout";
import { getBlogPosts } from "@/lib/log";
const changelogsDir = path.resolve(process.cwd(), "data", "pages");
export default async function ChangelogPage({
params,
}: {
params: { slug: string };
}) {
const { slug } = params;
let posts = await getBlogPosts();
let post = posts.find((post) => post.slug === params.slug);
try {
return (
<ChangelogLayout>
<ReactMarkdown>{post?.content}</ReactMarkdown>
</ChangelogLayout>
);
} catch (error) {
console.error("Error loading changelog page:", error);
return <div>Error loading the page.</div>;
}
}

View file

@ -5,19 +5,21 @@ import Hero from "@/components/shared/Hero";
import HowItWorks from "@/components/shared/HowItWorks";
import Newsletter from "@/components/shared/Newsletter";
import Partners from "@/components/shared/Partners";
import Testimonials from "@/components/shared/Testimonials";
const RootPage = () => {
return (
<>
<Hero />
<HowItWorks />
<Partners />
<About />
{/* <DataTable /> */}
<Newsletter />
<Faq />
</>
);
return (
<>
<Hero />
<HowItWorks />
<Testimonials />
<Partners />
<About />
{/* <DataTable /> */}
<Faq />
<Newsletter />
</>
);
};
export default RootPage;

View file

@ -1,66 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { promises as fs } from "fs";
import path from "path";
import matter from "gray-matter";
const changelogsDir = path.resolve(process.cwd(), "data/pages");
async function getMDXFiles(dir: string): Promise<string[]> {
const files = await fs.readdir(dir);
return files.filter((file) => file.endsWith(".mdx"));
}
export async function GET() {
try {
const mdxFiles = await getMDXFiles(changelogsDir);
const pages = await Promise.all(
mdxFiles.map(async (file) => {
const filePath = path.join(changelogsDir, file);
const content = await fs.readFile(filePath, "utf-8");
const { data, content: mdxContent } = matter(content);
const slug = path.basename(file, path.extname(file));
return {
metadata: data,
slug,
content: mdxContent,
};
})
);
return NextResponse.json(pages, { status: 200 });
} catch (error) {
console.error("Failed to fetch pages:", error);
return NextResponse.json(
{ error: "Failed to fetch pages" },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
const { title } = await request.json();
const slug = title.toLowerCase().replace(/\s+/g, "-");
const filePath = path.join(changelogsDir, `${slug}.mdx`);
try {
if (
await fs
.stat(filePath)
.then(() => true)
.catch(() => false)
) {
return NextResponse.json(
{ error: "Page already exists" },
{ status: 400 }
);
}
await fs.writeFile(filePath, `---\ntitle: ${title}\n---\n`);
return NextResponse.json({ title, slug }, { status: 201 });
} catch (error) {
console.error("Failed to create page:", error);
return NextResponse.json(
{ error: "Failed to create page" },
{ status: 500 }
);
}
}

View file

@ -1,64 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { promises as fs } from "fs";
import path from "path";
import matter from "gray-matter";
// Define the path to the changelogs directory
const pagesDir = path.join(process.cwd(), "data", "pages");
export async function GET(
request: NextRequest,
{ params }: { params: { slug: string } }
) {
const { slug } = params;
const filePath = path.join(pagesDir, `${slug}.mdx`);
try {
const content = await fs.readFile(filePath, "utf8");
// Use gray-matter to parse the front matter and content
const { data, content: mdxContent } = matter(content);
return NextResponse.json({ title: data.title, content: mdxContent });
} catch (error) {
console.error("Failed to load page:", error);
return NextResponse.json({ error: "Page not found" }, { status: 404 });
}
}
export async function PUT(
request: NextRequest,
{ params }: { params: { slug: string } }
) {
const { slug } = params;
const filePath = path.join(pagesDir, `${slug}.mdx`);
const { title, content } = await request.json();
try {
await fs.writeFile(filePath, `---\ntitle: ${title}\n---\n${content}`);
return NextResponse.json({ title, slug, content });
} catch (error) {
console.error("Failed to update page:", error);
return NextResponse.json(
{ error: "Failed to update page" },
{ status: 500 }
);
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { slug: string } }
) {
const { slug } = params;
const filePath = path.join(pagesDir, `${slug}.mdx`);
try {
await fs.unlink(filePath);
return NextResponse.json({}, { status: 204 });
} catch (error) {
console.error("Failed to delete page:", error);
return NextResponse.json(
{ error: "Failed to delete page" },
{ status: 500 }
);
}
}

View file

@ -0,0 +1,55 @@
import Image from "next/image";
import React from "react";
interface TestimonialCard {
avatar: string;
name: string;
role?: string;
testimonial: string;
rating: number;
}
const TestimonialCard = ({
avatar,
name,
role,
testimonial,
rating,
}: TestimonialCard) => {
return (
<li className="inline-block w-full">
<div className="bg-[#1c1c1c] mx-auto mb-5 flex w-full cursor-default flex-col gap-4 rounded-2xl px-[30px] py-6 shadow-md transition-all hover:scale-[103%]">
<div className="flex flex-row items-center gap-3">
<div>
<Image
src={`/testimonials/${avatar}.webp`}
alt="avatar1"
width={40}
height={40}
className="rounded-full"
/>
</div>
<div className="space-y-1">
<div className="flex items-center gap-1">
<div className="small-semibold text-white">{name}</div>
</div>
<div className="small-regular text-white-800">{role}</div>
</div>
</div>
<p className="body-regular text-white">{testimonial}</p>
<div className="hue-rotate-90 text-lg">
{/* <Image
src="/testimonials/stars.svg"
alt="star"
width={120}
height={120}
className="object-cover"
/> */}
{"⭐".repeat(rating)}
</div>
</div>
</li>
);
};
export default TestimonialCard;

View file

@ -0,0 +1,105 @@
import React from "react";
import TestimonialCard from "../cards/testimonialCard";
const testimonials = [
{
name: "John Doe",
role: "CEO, Example Corp.",
avatar: "avatar1",
testimonial:
"Working with this team was a fantastic experience. They developed our website exactly to our specifications, and everything was seamless and well-integrated.",
rating: 5,
},
{
name: "Jane Smith",
role: "CEO, CleanCo",
avatar: "avatar2",
testimonial:
"We're thrilled with the website. It's simple, clean, and has significantly boosted our sales. The developers did an excellent job.",
rating: 4,
},
{
name: "Sam Green",
role: "Web Developer",
avatar: "avatar3",
testimonial:
"Collaborating with this team to build a SaaS-integrated website was a perfect experience. I look forward to working with them again.",
rating: 5,
},
{
name: "Chris Brown",
role: "Web Coder",
avatar: "avatar4",
testimonial:
"The team's understanding of our needs and their ability to provide fitting solutions was impressive. Their support and guidance were invaluable.",
rating: 4,
},
{
name: "Alex Johnson",
avatar: "avatar5",
testimonial:
"Exceptional service and outstanding results. They consistently deliver on time and within budget, making them our go-to partner for all our projects.",
rating: 5,
},
{
name: "Patricia Taylor",
role: "Web Developer",
avatar: "avatar6",
testimonial:
"It was great to work with them. I needed a design for a SaaS project, and it was delivered within 2 days.",
rating: 4,
},
{
name: "Emily Davis",
role: "UX Designer, Creative Agency",
avatar: "avatar7",
testimonial:
"Collaborating with them has been a pleasure. Their creativity and user-centric approach have significantly enhanced our product's usability.",
rating: 5,
},
{
name: "Michael Lee",
avatar: "avatar8",
testimonial:
"They have a keen understanding of our business needs and consistently deliver top-notch solutions. Their reliability and efficiency are commendable.",
rating: 5,
},
{
name: "Sarah Wilson",
avatar: "avatar9",
testimonial:
"Their dedication to client satisfaction is evident in everything they do. We've seen remarkable improvements in our processes thanks to their expertise.",
rating: 4,
},
];
const Testimonials = () => {
return (
<section className="mx-auto flex w-full max-w-7xl flex-col pt-12 md:pt-24">
<div className="flex flex-row items-center justify-center space-x-1">
<span className="text-white/50 text-xs lg:text-base">Testimonials</span>
</div>
<h1 className="text-3xl md:text-5xl font-bold text-center">
Hear it from{" "}
<span className="bg-gradient-to-b from-green-200 to-primary text-transparent bg-clip-text">
our users
</span>
</h1>
<div className="columns-1 gap-5 md:columns-2 lg:columns-3 py-6 mt-6">
{testimonials.map((testimonial, idx) => (
<TestimonialCard
avatar={testimonial.avatar}
name={testimonial.name}
role={testimonial.role}
testimonial={testimonial.testimonial}
rating={testimonial.rating}
key={idx}
/>
))}
</div>
</section>
);
};
export default Testimonials;

View file

@ -1,13 +0,0 @@
---
title: svrjs trail
---
# SVRJS this is good
## this is second heading
```json
{hi this iajdiajsidj}
```
asdasidajsidjasi

View file

@ -1,35 +0,0 @@
import fs from "fs";
import path from "path";
import matter from "gray-matter";
function getMDXFiles(dir: string): string[] {
return fs.readdirSync(dir).filter((file) => file.endsWith(".mdx"));
}
function readMDXFile(filePath: string): { metadata: any; content: string } {
const source = fs.readFileSync(filePath, "utf-8");
const { data: metadata, content } = matter(source);
return { metadata, content };
}
function extractTweetIds(content: string): string[] {
// Implement your tweet ID extraction logic here
return [];
}
export function getMDXData(dir: string) {
let mdxFiles = getMDXFiles(dir);
return mdxFiles.map((file) => {
let { metadata, content } = readMDXFile(path.join(dir, file));
let slug = path.basename(file, path.extname(file));
return {
metadata,
slug,
content,
};
});
}
export function getBlogPosts() {
return getMDXData(path.join(process.cwd(), "data", "pages"));
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View file

@ -0,0 +1,156 @@
<svg
width="101"
height="20"
viewBox="0 0 101 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.35352 18.3333L6.70768 12.4792L2.16602 8.54167L8.16602 8.02083L10.4993 2.5L12.8327 8.02083L18.8327 8.54167L14.291 12.4792L15.6452 18.3333L10.4993 15.2292L5.35352 18.3333Z"
fill="url(#green)"
></path>
<path
d="M25.35352 18.3333L26.70768 12.4792L22.16602 8.54167L28.16602 8.02083L30.4993 2.5L32.8327 8.02083L38.8327 8.54167L34.291 12.4792L35.6452 18.3333L30.4993 15.2292L25.35352 18.3333Z"
fill="url(#green)"
></path>
<path
d="M45.35352 18.3333L46.70768 12.4792L42.16602 8.54167L48.16602 8.02083L50.4993 2.5L52.8327 8.02083L58.8327 8.54167L54.291 12.4792L55.6452 18.3333L50.4993 15.2292L45.35352 18.3333Z"
fill="url(#green)"
></path>
<path
d="M65.35352 18.3333L66.70768 12.4792L62.16602 8.54167L68.16602 8.02083L70.4993 2.5L72.8327 8.02083L78.8327 8.54167L74.291 12.4792L75.6452 18.3333L70.4993 15.2292L65.35352 18.3333Z"
fill="url(#green)"
></path>
<path
d="M85.35352 18.3333L86.70768 12.4792L82.16602 8.54167L88.16602 8.02083L90.4993 2.5L92.8327 8.02083L98.8327 8.54167L94.291 12.4792L95.6452 18.3333L90.4993 15.2292L85.35352 18.3333Z"
fill="url(#green)"
></path>
<defs>
<linearGradient
id="orange"
x1="2.55563"
y1="10.5169"
x2="88.9626"
y2="10.5169"
gradientUnits="userSpaceOnUse"
>
<animate
attributeName="x1"
dur="1.8s"
values="80%; 0%; 80%"
calcMode="spline"
keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
repeatCount="indefinite"
></animate>
<stop stop-color="#FF7170"></stop>
<stop offset="1" stop-color="#FFE57F"></stop>
</linearGradient>
<linearGradient
id="blue"
x1="2.55563"
y1="10.5169"
x2="88.9626"
y2="10.5169"
gradientUnits="userSpaceOnUse"
>
<animate
attributeName="x1"
dur="1.8s"
values="80%; 0%; 80%"
calcMode="spline"
keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
repeatCount="indefinite"
></animate>
<stop stop-color="#4C73FF"></stop>
<stop offset="1" stop-color="#389BFF"></stop>
</linearGradient>
<linearGradient
xmlns="http://www.w3.org/2000/svg"
id="pink"
x1="2.55563"
y1="10.5169"
x2="88.9626"
y2="10.5169"
gradientUnits="userSpaceOnUse"
>
<animate
attributeName="x1"
dur="1.8s"
values="80%; 0%; 80%"
calcMode="spline"
keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
repeatCount="indefinite"
></animate>
<stop stop-color="#FF4CC2"></stop>
<stop offset="1" stop-color="#F87393"></stop>
</linearGradient>
<linearGradient
xmlns="http://www.w3.org/2000/svg"
id="green"
x1="0.166016"
y1="8.41663"
x2="86.8327"
y2="8.41663"
gradientUnits="userSpaceOnUse"
>
<animate
attributeName="x1"
dur="1.8s"
values="80%; 0%; 80%"
calcMode="spline"
keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
repeatCount="indefinite"
></animate>
<stop stop-color="#7AFFD7"></stop>
<stop offset="1" stop-color="#00FFB2"></stop>
</linearGradient>
<linearGradient
xmlns="http://www.w3.org/2000/svg"
id="gold"
x1="2.55563"
y1="10.5169"
x2="88.9626"
y2="10.5169"
gradientUnits="userSpaceOnUse"
>
<animate
attributeName="x1"
dur="1.8s"
values="50%; 0%; 50%"
calcMode="spline"
keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
repeatCount="indefinite"
></animate>{" "}
<animate
attributeName="x1"
dur="3s"
values="50%; 0%; 50%"
calcMode="spline"
keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
repeatCount="indefinite"
></animate>
<stop stop-color="#FF7170"></stop>
<stop offset="1" stop-color="#FFE57F"></stop>
</linearGradient>
<linearGradient
xmlns="http://www.w3.org/2000/svg"
id="purple"
x1="1.13689"
y1="4.45834"
x2="18.5566"
y2="5.48068"
gradientUnits="userSpaceOnUse"
>
<animate
attributeName="x1"
dur="1.8s"
values="100%; 40%; 100%"
calcMode="spline"
keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
repeatCount="indefinite"
></animate>
<stop stop-color="#854CFF"></stop>
<stop offset="1" stop-color="#B673F8"></stop>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -1,50 +0,0 @@
const fs = require("fs");
const path = require("path");
const { compile } = require("@mdx-js/mdx");
const { remark } = require("remark");
const { toVFile } = require("to-vfile");
const { format } = require("prettier");
const rehypeStringify = require("rehype-stringify");
const remarkParse = require("remark-parse");
const rehypeParse = require("rehype-parse");
const { unified } = require("unified");
const mdxDir = path.join(__dirname, "../data/pages");
const outputDir = path.join(__dirname, "../data/mdx");
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.readdir(mdxDir, (err, files) => {
if (err) throw err;
files.forEach(async (file) => {
if (path.extname(file) === ".mdx") {
const filePath = path.join(mdxDir, file);
const outputFilePath = path.join(
outputDir,
path.basename(file, ".mdx") + ".html"
);
try {
const mdxContent = fs.readFileSync(filePath, "utf8");
const processedContent = await compile(mdxContent, {
remarkPlugins: [remarkParse],
rehypePlugins: [rehypeStringify],
});
const html = processedContent.toString();
// ig optional?
const formattedHtml = format(html, { parser: "html" });
fs.writeFileSync(outputFilePath, formattedHtml);
console.log(`Processed ${file} -> ${outputFilePath}`);
} catch (error) {
console.error(`Failed to process ${file}:`, error);
}
}
});
});