testimonails added
27
.github/workflows/mdx-content.yml
vendored
|
@ -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
|
|
|
@ -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 (
|
return (
|
||||||
<section id="logs-page" className="wrapper container">
|
<section id="logs-page" className="wrapper container">
|
||||||
<h1 className="text-3xl font-bold py-6">Server Logs Form</h1>
|
<h1 className="text-3xl font-bold py-6">Server Logs Form</h1>
|
||||||
|
@ -268,69 +228,6 @@ const AdminLogPage = () => {
|
||||||
</form>
|
</form>
|
||||||
</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 to list and delete logs */}
|
||||||
<section id="logs-list" className="py-16 md:py-24">
|
<section id="logs-list" className="py-16 md:py-24">
|
||||||
<h2 className="text-3xl md:text-4xl font-bold">Existing Logs</h2>
|
<h2 className="text-3xl md:text-4xl font-bold">Existing Logs</h2>
|
||||||
|
|
|
@ -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>;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,17 +5,19 @@ import Hero from "@/components/shared/Hero";
|
||||||
import HowItWorks from "@/components/shared/HowItWorks";
|
import HowItWorks from "@/components/shared/HowItWorks";
|
||||||
import Newsletter from "@/components/shared/Newsletter";
|
import Newsletter from "@/components/shared/Newsletter";
|
||||||
import Partners from "@/components/shared/Partners";
|
import Partners from "@/components/shared/Partners";
|
||||||
|
import Testimonials from "@/components/shared/Testimonials";
|
||||||
|
|
||||||
const RootPage = () => {
|
const RootPage = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Hero />
|
<Hero />
|
||||||
<HowItWorks />
|
<HowItWorks />
|
||||||
|
<Testimonials />
|
||||||
<Partners />
|
<Partners />
|
||||||
<About />
|
<About />
|
||||||
{/* <DataTable /> */}
|
{/* <DataTable /> */}
|
||||||
<Newsletter />
|
|
||||||
<Faq />
|
<Faq />
|
||||||
|
<Newsletter />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
55
components/cards/testimonialCard.tsx
Normal 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;
|
105
components/shared/Testimonials.tsx
Normal 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;
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
title: svrjs trail
|
|
||||||
---
|
|
||||||
# SVRJS this is good
|
|
||||||
|
|
||||||
## this is second heading
|
|
||||||
|
|
||||||
```json
|
|
||||||
{hi this iajdiajsidj}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
asdasidajsidjasi
|
|
35
lib/log.ts
|
@ -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"));
|
|
||||||
}
|
|
BIN
public/testimonials/avata10.webp
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
public/testimonials/avatar1.webp
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
public/testimonials/avatar2.webp
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
public/testimonials/avatar3.webp
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
public/testimonials/avatar4.webp
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
public/testimonials/avatar5.webp
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
public/testimonials/avatar6.webp
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
public/testimonials/avatar7.webp
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
public/testimonials/avatar8.webp
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
public/testimonials/avatar9.webp
Normal file
After Width: | Height: | Size: 6.3 KiB |
156
public/testimonials/stars.svg
Normal 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 |
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|