Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 39 additions & 17 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use client";
import { MainHomeLayout } from "@repo/web-ui/layout";
import React, { useState } from "react";
import React, { useCallback, Suspense } from "react";
import { useSearchParams, useRouter } from "next/navigation";
import "./page.module.css";
import QuizMarkdownEditor from "../components/markdown-editor";
import ChatgptEditor from "../components/chatgpt-editor";
import TwitterEditor from "../components/twitter-editor";

import XEditor from "../components/x-editor";
import NotebookEditor from "../components/notebook-editor";
import GradientEditor from "../components/gradient-editor";
import { CardSizeProvider } from "@repo/ui/context/CardSizeContext";

Expand All @@ -18,29 +19,50 @@ const tabs = [
key: "chatgpt",
label: "ChatGPT Card",
},
// {
// key: "gradient",
// label: "Gradient Card",
// },
{
key: "twitter",
label: "Twitter Card",
key: "gradient",
label: "Gradient Card",
},
{
key: "x",
label: "X Card",
},
{
key: "notebook",
label: "Notebook Card",
},

];

export default function Home() {
const [tab, setTab] = useState("markdown");
function HomeContent() {
const searchParams = useSearchParams();
const router = useRouter();

const currentTab = searchParams.get("t") || "markdown";

const setTab = useCallback((newTab: string) => {
const params = new URLSearchParams(searchParams.toString());
params.set("t", newTab);
router.push(`?${params.toString()}`);
}, [searchParams, router]);

return (
<MainHomeLayout tab={tab} tabs={tabs} setTab={setTab}>
<MainHomeLayout tab={currentTab} tabs={tabs} setTab={setTab}>
<CardSizeProvider>
{tab === "markdown" ? <QuizMarkdownEditor /> : null}
{tab === "chatgpt" ? <ChatgptEditor /> : null}
{/* {tab === "gradient" ? <GradientEditor /> : null} */}
{tab === "twitter" ? <TwitterEditor /> : null}

{currentTab === "markdown" ? <QuizMarkdownEditor /> : null}
{currentTab === "chatgpt" ? <ChatgptEditor /> : null}
{currentTab === "gradient" ? <GradientEditor /> : null}
{currentTab === "x" ? <XEditor /> : null}
{currentTab === "notebook" ? <NotebookEditor /> : null}
</CardSizeProvider>
</MainHomeLayout>
);
}

export default function Home() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HomeContent />
</Suspense>
);
}
136 changes: 136 additions & 0 deletions apps/web/components/common/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
"use client";
import { Slider } from "../ui/slider";
import { Input } from "../ui/input";
import { Textarea } from "../ui/textarea";
import { cn } from "../../lib/cn";

function SliderBox({
keyName,
value,
label,
min,
max,
unit,
widthWrapper = false,
formatValue = false,
setStateValue,
}: {
keyName: string;
value: number;
label: string;
min: number;
max: number;
unit?: string;
formatValue?: boolean;
widthWrapper?: boolean;
setStateValue: (name: string, value: any) => void;
}) {
const comp = (
<>
<label className="block mb-2">
{label || <span>{keyName}</span>}:{" "}
{formatValue ? (
<span>{new Intl.NumberFormat().format(value)}</span>
) : (
value
)}
{unit && <span className="">{unit}</span>}
</label>
<Slider
min={min}
max={max}
name={keyName}
value={[value]}
onValueChange={(val) => setStateValue(keyName, val)}
Copy link

Copilot AI Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The slider component expects a single value but receives an array. The onValueChange should extract the first element: onValueChange={(val) => setStateValue(keyName, val[0])}

Suggested change
onValueChange={(val) => setStateValue(keyName, val)}
onValueChange={(val) => setStateValue(keyName, val[0])}

Copilot uses AI. Check for mistakes.
className="w-full"
/>
</>
);

if (widthWrapper) {
return <WithWrapper>{comp}</WithWrapper>;
}
return comp;
}

function DrawInput({
keyName,
value,
placeholder,
label,
onChange: setStateValue,
isTextArea = false,
type = "text",
className = "",
}: {
keyName: string;
value: string;
placeholder?: string;
label?: React.ReactNode;
onChange: (name: string, value: any) => void;
isTextArea?: boolean;
type?: string;
className?: string;
}) {
return (
<div className={cn("mb-4", className)}>
<label className="block">{label || <span>{keyName}</span>}:</label>
{isTextArea ? (
<Textarea
value={value}
onChange={(e) => setStateValue(keyName, e.target.value)}
className="w-full px-2 border rounded-md"
placeholder={placeholder}
/>
) : (
<Input
type={type}
value={value}
onChange={(e) => setStateValue(keyName, e.target.value)}
className="w-full px-2 border rounded-md"
placeholder={placeholder}
/>
)}
</div>
);
}

function WithWrapper({ children }: { children: React.ReactNode }) {
return (
<div className="flex flex-col mt-4 border p-2 rounded-md">{children}</div>
);
}

function CheckBox({
keyName,
value,
label,
setStateValue,
widthWrapper = false,
}: {
keyName: string;
value: boolean;
label: string;
widthWrapper?: boolean;
setStateValue: (name: string, value: any) => void;
}) {
const component = (
<label className="flex items-center">
<input
type="checkbox"
checked={value}
name={keyName}
onChange={(e) => setStateValue(keyName, e.target.checked)}
className="mr-2"
/>
{label || <span>{keyName}</span>}
</label>
);

if (widthWrapper) {
return <WithWrapper>{component}</WithWrapper>;
}
return component;
}

export { SliderBox, DrawInput, CheckBox };
Loading