refactor: clean deprecated components

This commit is contained in:
Louis 2023-09-30 15:23:41 +07:00 committed by Louis
parent cc898618d8
commit 7e0361a29d
37 changed files with 2 additions and 1197 deletions

View File

@ -1,29 +0,0 @@
import { MenuAdvancedPrompt } from "../MenuAdvancedPrompt";
import { useForm } from "react-hook-form";
import BasicPromptButton from "../BasicPromptButton";
import PrimaryButton from "../PrimaryButton";
const AdvancedPrompt: React.FC = () => {
const { register, handleSubmit } = useForm();
const onSubmit = (data: any) => {};
return (
<form
className="w-[288px] h-screen flex flex-col border-r border-gray-200"
onSubmit={handleSubmit(onSubmit)}
>
<BasicPromptButton />
<MenuAdvancedPrompt register={register} />
<div className="py-3 px-2 flex flex-none gap-3 items-center justify-between border-t border-gray-200">
<PrimaryButton
fullWidth={true}
title="Generate"
onClick={() => handleSubmit(onSubmit)}
/>
</div>
</form>
);
};
export default AdvancedPrompt;

View File

@ -1,18 +0,0 @@
import React, { useState } from "react";
import TogglableHeader from "../TogglableHeader";
const AdvancedPromptGenerationParams = () => {
const [expand, setExpand] = useState(true);
return (
<>
<TogglableHeader
icon={"icons/unicorn_layers-alt.svg"}
title={"Generation Parameters"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
/>
</>
);
};
export default AdvancedPromptGenerationParams;

View File

@ -1,31 +0,0 @@
import React, { useState } from "react";
import { DropdownsList } from "../DropdownList";
import TogglableHeader from "../TogglableHeader";
import { UploadFileImage } from "../UploadFileImage";
import { FieldValues, UseFormRegister } from "react-hook-form";
type Props = {
register: UseFormRegister<FieldValues>;
};
const AdvancedPromptImageUpload: React.FC<Props> = ({ register }) => {
const [expand, setExpand] = useState(true);
const data = ["test1", "test2", "test3", "test4"];
return (
<>
<TogglableHeader
icon={"icons/ic_image.svg"}
title={"Image"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
/>
<div className={`${expand ? "flex" : "hidden"} flex-col gap-[5px]`}>
<UploadFileImage register={register} />
<DropdownsList title="Control image with ControlNet:" data={data} />
</div>
</>
);
};
export default AdvancedPromptImageUpload;

View File

@ -1,29 +0,0 @@
import React, { useState } from "react";
import { DropdownsList } from "../DropdownList";
import TogglableHeader from "../TogglableHeader";
const AdvancedPromptResolution = () => {
const [expand, setExpand] = useState(true);
const data = ["512", "524", "536"];
const ratioData = ["1:1", "2:2", "3:3"];
return (
<>
<TogglableHeader
icon={"icons/unicorn_layers-alt.svg"}
title={"Resolution"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
/>
<div className={`${expand ? "flex" : "hidden"} flex-col gap-[5px]`}>
<div className="flex gap-3 py-3">
<DropdownsList data={data} title="Width" />
<DropdownsList data={data} title="Height" />
</div>
<DropdownsList title="Select ratio" data={ratioData} />
</div>
</>
);
};
export default AdvancedPromptResolution;

View File

@ -1,41 +0,0 @@
import React, { useState } from "react";
import TogglableHeader from "../TogglableHeader";
import { AdvancedTextArea } from "../AdvancedTextArea";
import { FieldValues, UseFormRegister } from "react-hook-form";
type Props = {
register: UseFormRegister<FieldValues>;
};
const AdvancedPromptText: React.FC<Props> = ({ register }) => {
const [expand, setExpand] = useState(true);
return (
<>
<TogglableHeader
icon={"icons/messicon.svg"}
title={"Prompt"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
/>
<div className={`${expand ? "flex" : "hidden"} flex-col gap-[5px]`}>
<AdvancedTextArea
formId="prompt"
height={80}
placeholder="Prompt"
title="Prompt"
register={register}
/>
<AdvancedTextArea
formId="negativePrompt"
height={80}
placeholder="Describe what you don't want in your image"
title="Negative Prompt"
register={register}
/>
</div>
</>
);
};
export default AdvancedPromptText;

View File

@ -1,27 +0,0 @@
import { FieldValues, UseFormRegister } from "react-hook-form";
type Props = {
formId?: string;
height: number;
title: string;
placeholder: string;
register: UseFormRegister<FieldValues>;
};
export const AdvancedTextArea: React.FC<Props> = ({
formId = "",
height,
placeholder,
title,
register,
}) => (
<div className="w-full flex flex-col pt-3 gap-1">
<label className="text-sm leading-5 text-gray-800">{title}</label>
<textarea
style={{ height: `${height}px` }}
className="rounded-lg py-[13px] px-5 border outline-none resize-none border-gray-300 bg-gray-50 placeholder:gray-400 text-sm font-normal"
placeholder={placeholder}
{...register(formId, { required: formId === "prompt" ? true : false })}
/>
</div>
);

View File

@ -1,20 +0,0 @@
import Image from "next/image";
const Search: React.FC = () => {
return (
<div className="flex bg-gray-200 w-[343px] h-[36px] items-center px-2 gap-[6px] rounded-md">
<Image
src={"icons/magnifyingglass.svg"}
width={15.63}
height={15.78}
alt=""
/>
<input
className="bg-inherit outline-0 w-full border-0 p-0 focus:ring-0"
placeholder="Search"
/>
</div>
);
};
export default Search;

View File

@ -1,20 +0,0 @@
import Image from "next/image";
import Link from "next/link";
type Props = {
name: string;
imageUrl: string;
};
const AiTypeCard: React.FC<Props> = ({ imageUrl, name }) => {
return (
<Link href={`/ai/${name}`} className='flex-1'>
<div className="flex-1 h-full bg-[#F3F4F6] flex items-center justify-center gap-[10px] py-[13px] rounded-[8px] px-4 active:opacity-50 hover:opacity-20">
<Image src={imageUrl} width={82} height={82} alt="" />
<span className="font-bold">{name}</span>
</div>
</Link>
);
};
export default AiTypeCard;

View File

@ -1,15 +0,0 @@
type Props = {
title: string;
description: string;
};
export const ApiStep: React.FC<Props> = ({ description, title }) => {
return (
<div className="gap-2 flex flex-col">
<span className="text-[#8A8A8A]">{title}</span>
<div className="flex flex-col gap-[10px] p-[18px] bg-[#F9F9F9] overflow-y-hidden">
<pre className="text-sm leading-5 text-black">{description}</pre>
</div>
</div>
);
};

View File

@ -1,20 +1,14 @@
"use client";
import {
currentConversationAtom,
showingAdvancedPromptAtom,
} from "@/_helpers/JotaiWrapper";
import { useAtomValue, useSetAtom } from "jotai";
import { showingAdvancedPromptAtom } from "@/_helpers/JotaiWrapper";
import { useSetAtom } from "jotai";
import SecondaryButton from "../SecondaryButton";
import SendButton from "../SendButton";
import { ProductType } from "@/_models/Product";
const BasicPromptAccessories: React.FC = () => {
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom);
const currentConversation = useAtomValue(currentConversationAtom);
const shouldShowAdvancedPrompt = false;
// currentConversation?.product.type === ProductType.ControlNet;
return (
<div

View File

@ -1,39 +0,0 @@
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
import React, { PropsWithChildren } from "react";
type PropType = PropsWithChildren<
React.DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>
>;
export const PrevButton: React.FC<PropType> = (props) => {
const { children, ...restProps } = props;
return (
<button
className="embla__button embla__button--prev"
type="button"
{...restProps}
>
<ChevronLeftIcon width={20} height={20} />
{children}
</button>
);
};
export const NextButton: React.FC<PropType> = (props) => {
const { children, ...restProps } = props;
return (
<button
className="embla__button embla__button--next"
type="button"
{...restProps}
>
<ChevronRightIcon width={20} height={20} />
{children}
</button>
);
};

View File

@ -1,25 +0,0 @@
import { useTheme } from "next-themes";
import { SunIcon, MoonIcon } from "@heroicons/react/24/outline";
export const ThemeChanger: React.FC = () => {
const { theme, setTheme, systemTheme } = useTheme();
const currentTheme = theme === "system" ? systemTheme : theme;
if (currentTheme === "dark") {
return (
<SunIcon
className="h-6 w-6"
aria-hidden="true"
onClick={() => setTheme("light")}
/>
);
}
return (
<MoonIcon
className="h-6 w-6"
aria-hidden="true"
onClick={() => setTheme("dark")}
/>
);
};

View File

@ -1,37 +0,0 @@
import {
getActiveConvoIdAtom,
setActiveConvoIdAtom,
} from "@/_helpers/JotaiWrapper";
import { useAtomValue, useSetAtom } from "jotai";
import Image from "next/image";
type Props = {
imageUrl: string;
conversationId: string;
};
const CompactHistoryItem: React.FC<Props> = ({ imageUrl, conversationId }) => {
const activeConvoId = useAtomValue(getActiveConvoIdAtom);
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom);
const isSelected = activeConvoId === conversationId;
return (
<button
onClick={() => setActiveConvoId(conversationId)}
className={`${
isSelected ? "bg-gray-100" : "bg-transparent"
} w-14 h-14 rounded-lg`}
>
<Image
className="rounded-full mx-auto"
src={imageUrl}
width={36}
height={36}
alt=""
/>
</button>
);
};
export default CompactHistoryItem;

View File

@ -1,21 +0,0 @@
import { useAtomValue } from "jotai";
import CompactHistoryItem from "../CompactHistoryItem";
import { userConversationsAtom } from "@/_helpers/JotaiWrapper";
const CompactHistoryList: React.FC = () => {
const conversations = useAtomValue(userConversationsAtom);
return (
<div className="flex flex-col flex-1 gap-1 mt-3">
{conversations.map(({ id, image }) => (
<CompactHistoryItem
key={id}
conversationId={id ?? ""}
imageUrl={image ?? ""}
/>
))}
</div>
);
};
export default CompactHistoryList;

View File

@ -1,11 +0,0 @@
import CompactHistoryList from "../CompactHistoryList";
import CompactLogo from "../CompactLogo";
const CompactSideBar: React.FC = () => (
<div className="h-screen w-16 border-r border-gray-300 flex flex-col items-center pt-3 gap-3">
<CompactLogo />
<CompactHistoryList />
</div>
);
export default CompactSideBar;

View File

@ -1,33 +0,0 @@
import { ApiStep } from "../ApiStep";
const DescriptionPane: React.FC = () => {
const data = [
{
title: "Install the Node.js client:",
description: "npm install replicate",
},
{
title:
"Next, copy your API token and authenticate by setting it as an environment variable:",
description:
"export REPLICATE_API_TOKEN=r8_*************************************",
},
{
title: "lorem ipsum dolor asimet",
description: "come codes here",
},
];
return (
<div className="flex flex-col gap-4 w-[full]">
<h2 className="text-[20px] tracking-[-0.4px] leading-[25px]">
Run the model
</h2>
{data.map((item, index) => (
<ApiStep key={index} {...item} />
))}
</div>
);
};
export default DescriptionPane;

View File

@ -1,29 +0,0 @@
import useCreateConversation from "@/_hooks/useCreateConversation";
import { Product } from "@/_models/Product";
type Props = {
product: Product;
};
const GenerateImageCard: React.FC<Props> = ({ product }) => {
const { name, avatarUrl } = product;
const { requestCreateConvo } = useCreateConversation();
return (
<button
onClick={() => requestCreateConvo(product)}
className="relative active:opacity-50 text-left"
>
<img
src={avatarUrl}
alt=""
className="w-full h-full rounded-[8px] bg-gray-200 group-hover:opacity-75 object-cover object-center"
/>
<div className="absolute bottom-0 rounded-br-[8px] rounded-bl-[8px] bg-[rgba(0,0,0,0.5)] w-full p-3">
<span className="text-white font-semibold">{name}</span>
</div>
</button>
);
};
export default GenerateImageCard;

View File

@ -1,27 +0,0 @@
import { Product } from "@/_models/Product";
import GenerateImageCard from "../GenerateImageCard";
import { PhotoIcon } from "@heroicons/react/24/outline";
type Props = {
products: Product[];
};
const GenerateImageList: React.FC<Props> = ({ products }) => (
<>
{products.length === 0 ? null : (
<div className="flex items-center gap-3 mt-8 mb-2">
<PhotoIcon width={24} height={24} className="ml-6" />
<span className="font-semibold text-gray-900 dark:text-white">
Generate Images
</span>
</div>
)}
<div className="mt-2 mx-6 mb-6 grid grid-cols-2 gap-6 sm:gap-x-6 md:grid-cols-4 md:gap-8">
{products.map((item) => (
<GenerateImageCard key={item.name} product={item} />
))}
</div>
</>
);
export default GenerateImageList;

View File

@ -1,42 +0,0 @@
import useCreateConversation from "@/_hooks/useCreateConversation";
import { executeSerial } from "@/_services/pluginService";
import Image from "next/image";
import React from "react";
import { DataService } from "../../../shared/coreService";
const HistoryEmpty: React.FC = () => {
const { requestCreateConvo } = useCreateConversation();
const startChat = async () => {
const downloadedModels = await executeSerial(
DataService.GET_FINISHED_DOWNLOAD_MODELS
);
if (!downloadedModels || downloadedModels?.length === 0) {
alert(
"Seems like there is no model downloaded yet. Please download a model first."
);
} else {
requestCreateConvo(downloadedModels[0]);
}
};
return (
<div className="mt-5 flex flex-col w-full h-full items-center justify-center gap-4">
<Image
src={"icons/chats-circle-light.svg"}
width={50}
height={50}
alt=""
/>
<p className="text-sm leading-5 text-center text-[#9CA3AF]">
Its empty here
</p>
<button
onClick={startChat}
className="bg-[#1F2A37] py-[10px] px-5 gap-2 rounded-[8px] text-[14px] font-medium leading-[21px] text-white"
>
Let&apos;s chat
</button>
</div>
);
};
export default React.memo(HistoryEmpty);

View File

@ -1,12 +0,0 @@
import { displayDate } from "@/_utils/datetime";
import React from "react";
type Props = {
timestamp: number;
};
const HistoryItemDate: React.FC<Props> = ({ timestamp }) => {
return <p className="text-gray-400 text-xs">{displayDate(timestamp)}</p>;
};
export default React.memo(HistoryItemDate);

View File

@ -1,20 +0,0 @@
import React from "react";
import Image from "next/image";
type Props = {
title: string;
description: string;
};
const JanWelcomeTitle: React.FC<Props> = ({ title, description }) => (
<div className="flex items-center flex-col gap-3">
<h2 className="text-[22px] leading-7 font-bold">{title}</h2>
<span className="flex items-center text-xs leading-[18px]">
Operated by
<Image src={"icons/ico_logo.svg"} width={42} height={22} alt="" />
</span>
<span className="text-sm text-center font-normal">{description}</span>
</div>
);
export default React.memo(JanWelcomeTitle);

View File

@ -1,21 +0,0 @@
import AdvancedPromptText from "../AdvancedPromptText";
import AdvancedPromptImageUpload from "../AdvancedPromptImageUpload";
import AdvancedPromptResolution from "../AdvancedPromptResolution";
import AdvancedPromptGenerationParams from "../AdvancedPromptGenerationParams";
import { FieldValues, UseFormRegister } from "react-hook-form";
type Props = {
register: UseFormRegister<FieldValues>;
};
export const MenuAdvancedPrompt: React.FC<Props> = ({ register }) => (
<div className="flex flex-col flex-1 p-3 gap-[10px] overflow-x-hidden scroll">
<AdvancedPromptText register={register} />
<hr className="my-5" />
<AdvancedPromptImageUpload register={register} />
<hr className="my-5" />
<AdvancedPromptResolution />
<hr className="my-5" />
<AdvancedPromptGenerationParams />
</div>
);

View File

@ -1,69 +0,0 @@
import React from "react";
import Image from "next/image";
const MobileDownload = () => {
return (
<div className="flex items-center flex-col box-border rounded-lg border border-gray-200 p-4 bg-[#F9FAFB] mb-3">
{/** Jan logo */}
<Image
src="icons/janai_logo.svg"
alt={""}
width={32}
height={32}
style={{ objectFit: "contain" }}
/>
<b>Jan Mobile</b>
{/** Messages */}
<p className="font-light text-[12px] text-center">
Stay up to date and move work forward with Jan on iOS & Android.
Download the app today.
</p>
{/** Buttons */}
<div className="flex w-full mt-4 justify-between">
<a
href={process.env.NEXT_PUBLIC_DOWNLOAD_APP_IOS || ""}
target="_blank"
rel="noopener noreferrer"
className="w-[48%]"
>
<div className="flex box-border h-11 rounded-md bg-gray-300 p-2 items-center hover:bg-gray-200 focus:bg-gray-600">
<Image
src="icons/social_icon_apple.svg"
alt={""}
width={26}
height={26}
style={{ objectFit: "contain" }}
/>
<div className="ml-1">
<p className="text-[8px]">Download on the</p>
<p className="text-[10px] font-bold">AppStore</p>
</div>
</div>
</a>
<a
href={process.env.NEXT_PUBLIC_DOWNLOAD_APP_ANDROID || ""}
target="_blank"
rel="noopener noreferrer"
className="w-[48%]"
>
<div className="flex box-border h-11 rounded-md bg-gray-300 p-2 items-center hover:bg-gray-200 focus:bg-gray-600">
<Image
src="icons/google_play_logo.svg"
alt={""}
width={26}
height={26}
style={{ objectFit: "contain" }}
/>
<div className="ml-1">
<p className="text-[8px]">Download on the</p>
<p className="text-[10px] font-bold">Google Play</p>
</div>
</div>
</a>
</div>
</div>
);
};
export default React.memo(MobileDownload);

View File

@ -1,44 +0,0 @@
import Image from "next/image";
const MobileInstallPane: React.FC = () => {
return (
<div className="p-4 rounded-[8px] border-[1px] border-[#E5E7EB] bg-[#F9FAFB]">
<div className="flex flex-col gap-5 items-center">
<div className="flex flex-col items-center text-[12px]">
<Image src={"icons/app_icon.svg"} width={32} height={32} alt="" />
<h2 className="font-bold leading-[12px] text-center">Jan Mobie</h2>
<p className="leading-[18px] text-center">
Stay up to date and move work forward with Jan on iOS & Android.
<br />
Download the app today.
</p>
</div>
<div className="flex justify-between items-center gap-3">
<div className="bg-[#E5E7EB] rounded-[8px] gap-3 p-2 flex items-center">
<Image src={"icons/apple.svg"} width={26} height={26} alt="" />
<div className="flex flex-col">
<span className="text-[8px] leading-[12px]">Download on the</span>
<h2 className="font-bold text-[12px] leading-[15px]">AppStore</h2>
</div>
</div>
<div className="bg-[#E5E7EB] rounded-[8px] gap-3 p-2 flex items-center">
<Image
src={"icons/googleplay.svg"}
width={26}
height={26}
alt=""
/>
<div className="flex flex-col">
<span className="text-[8px] leading-[12px]">Download on the</span>
<h2 className="font-bold text-[12px] leading-[15px]">
Google Play
</h2>
</div>
</div>
</div>
</div>
</div>
);
};
export default MobileInstallPane;

View File

@ -1,91 +0,0 @@
import Image from "next/image";
import Link from "next/link";
import React from "react";
const MobileShowcase = () => {
return (
<div className="md:hidden flex flex-col px-5 mt-10 items-center justify-center w-full gap-10">
<Image
src="images/mobile.jpg"
width={638}
height={892}
alt="mobile"
className="w-full h-full"
style={{ objectFit: "contain" }}
/>
<div className="flex flex-col items-center justify-center mb-20">
<Image
src="icons/app_icon.svg"
width={200}
height={200}
className="w-[10%]"
alt="logo"
/>
<span className="text-[22px] font-semibold">Download Jan App</span>
<p className="text-center text-sm text-gray-500">
<span>Stay up to date and move work forward with Jan on iOS</span>
<span>& Android. Download the app today.</span>
</p>
<div className="flex justify-between items-center gap-3 mt-5">
<a
href={process.env.NEXT_PUBLIC_DOWNLOAD_APP_IOS ?? "#"}
target="_blank"
rel="noopener noreferrer"
className="w-[48%]"
>
<div className="flex box-border h-11 rounded-md bg-gray-300 p-2 items-center hover:bg-gray-200 focus:bg-gray-600">
<Image
src="icons/social_icon_apple.svg"
alt={""}
width={26}
height={26}
style={{ objectFit: "contain" }}
/>
<div className="ml-1">
<p className="text-[8px]">Download on the</p>
<p className="text-[10px] font-bold">AppStore</p>
</div>
</div>
</a>
<a
href={process.env.NEXT_PUBLIC_DOWNLOAD_APP_ANDROID ?? "#"}
target="_blank"
rel="noopener noreferrer"
className="w-[48%]"
>
<div className="flex box-border h-11 rounded-md bg-gray-300 p-2 items-center hover:bg-gray-200 focus:bg-gray-600">
<Image
src="icons/google_play_logo.svg"
alt={""}
width={26}
height={26}
style={{ objectFit: "contain" }}
/>
<div className="ml-1">
<p className="text-[8px]">Download on the</p>
<p className="text-[10px] font-bold">Google Play</p>
</div>
</div>
</a>
</div>
<Link
href={process.env.NEXT_PUBLIC_DISCORD_INVITATION_URL ?? "#"}
target="_blank_"
>
<div className="flex flex-row space-x-2 items-center justify-center rounded-[18px] px-2 h-[36px] bg-[#E2E5FF] mt-5">
<Image
src="icons/discord-icon.svg"
width={24}
height={24}
className=""
alt=""
/>
<span className="text-[#5865F2]">Join our Discord Community</span>
</div>
</Link>
</div>
</div>
);
};
export default MobileShowcase;

View File

@ -1,21 +0,0 @@
import React from "react";
type Props = {
inferenceTime: string;
hardware: string;
averageCostPerCall: string;
onGetApiKeyClick: () => void;
};
const ModelDetailCost: React.FC<Props> = ({
inferenceTime,
hardware,
averageCostPerCall,
onGetApiKeyClick,
}) => {
return <div>
</div>;
};
export default ModelDetailCost;

View File

@ -1,9 +0,0 @@
import OverviewPane from "../OverviewPane";
const ModelDetailSideBar: React.FC = () => (
<div className="flex w-[473px] h-full border-l-[1px] border-[#E5E7EB]">
<OverviewPane />
</div>
);
export default ModelDetailSideBar;

View File

@ -1,56 +0,0 @@
"use client";
import { useAtomValue } from "jotai";
import React from "react";
import { currentProductAtom } from "@/_helpers/JotaiWrapper";
const OverviewPane: React.FC = () => {
const product = useAtomValue(currentProductAtom);
return (
<div className="scroll overflow-y-auto">
<div className="flex flex-col flex-grow gap-6 m-3">
<AboutProductItem
title={"About this AI"}
value={product?.description ?? ""}
/>
<SmallItem title={"Model Version"} value={product?.version ?? ""} />
<div className="flex flex-col">
<span className="text-[#6B7280]">Model URL</span>
<a
className="text-[#1C64F2]"
href={product?.modelUrl ?? "#"}
target="_blank_"
>
{product?.modelUrl}
</a>
</div>
</div>
</div>
);
};
export default OverviewPane;
type Props = {
title: string;
value: string;
};
const AboutProductItem: React.FC<Props> = ({ title, value }) => {
return (
<div className="flex flex-col items-start">
<h2 className="text-black font-bold">{title}</h2>
<p className="text-[#6B7280]">{value}</p>
</div>
);
};
const SmallItem: React.FC<Props> = ({ title, value }) => {
return (
<div className="flex flex-col">
<span className="text-[#6B7280] ">{title}</span>
<span className="font-semibold">{value}</span>
</div>
);
};

View File

@ -1,10 +0,0 @@
import React from "react";
import useGetModels from "@/_hooks/useGetModels";
const ProductOverview: React.FC = () => {
const { models } = useGetModels();
return <div className="bg-gray-100 overflow-y-auto flex-grow scroll"></div>;
};
export default ProductOverview;

View File

@ -1,40 +0,0 @@
import JanWelcomeTitle from "../JanWelcomeTitle";
import { Product } from "@/_models/Product";
import { useSetAtom } from "jotai";
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
type Props = {
product: Product;
};
const SampleLlmContainer: React.FC<Props> = ({ product }) => {
const setCurrentPrompt = useSetAtom(currentPromptAtom);
const { data } = { data: { prompts: [] } };
return (
<div className="flex flex-col max-w-sm flex-shrink-0 gap-9 items-center pt-6 mx-auto">
<JanWelcomeTitle
title={product.name}
description={product.description ?? ""}
/>
<div className="flex flex-col">
<h2 className="font-semibold text-xl leading-6 tracking-[-0.4px] mb-5">
Try now
</h2>
{/* <div className="flex flex-col">
{data?.prompts.map((item) => (
<button
onClick={() => setCurrentPrompt(item.content ?? "")}
key={item.slug}
className="rounded p-2 hover:bg-[#0000000F] text-xs leading-[18px] text-gray-500 text-left"
>
<span className="line-clamp-3">{item.content}</span>
</button>
))}
</div> */}
</div>
</div>
);
};
export default SampleLlmContainer;

View File

@ -1,34 +0,0 @@
import React from "react";
import useCreateConversation from "@/_hooks/useCreateConversation";
import Image from "next/image";
import { Product } from "@/_models/Product";
type Props = {
product: Product;
};
const ShortcutItem: React.FC<Props> = ({ product }) => {
const { requestCreateConvo } = useCreateConversation();
return (
<button
className="flex items-center gap-2 mx-1 p-2"
onClick={() => requestCreateConvo(product)}
>
{product.avatarUrl && (
<Image
width={36}
height={36}
src={product.avatarUrl}
className="w-9 aspect-square rounded-full"
alt=""
/>
)}
<span className="text-gray-900 dark:text-white font-normal text-sm">
{product.name}
</span>
</button>
);
};
export default React.memo(ShortcutItem);

View File

@ -1,23 +0,0 @@
import React from "react";
import Image from "next/image";
type Props = {
onClick: () => void;
};
const ShowMoreButton: React.FC<Props> = ({ onClick }) => (
<button
className="flex text-xs leading-[18px] text-gray-800 rounded-lg py-2 px-3"
onClick={onClick}
>
Show more
<Image
src={"icons/unicorn_angle-down.svg"}
width={16}
height={16}
alt=""
/>
</button>
);
export default React.memo(ShowMoreButton);

View File

@ -1,44 +0,0 @@
import useCreateConversation from "@/_hooks/useCreateConversation";
import { Product } from "@/_models/Product";
import Image from "next/image";
type Props = {
product: Product;
};
const Slide: React.FC<Props> = ({ product }) => {
const { name, avatarUrl, description } = product;
const { requestCreateConvo } = useCreateConversation();
return (
<div className="w-full embla__slide h-[435px] relative">
<Image
className="w-full h-auto embla__slide__img"
src={avatarUrl}
fill
priority
alt=""
/>
<div className="absolute bg-[rgba(0,0,0,0.7)] w-full text-white bottom-0 right-0">
<div className="flex justify-between p-4">
<div className="flex flex-col gap-[2px]">
<h2 className="font-semibold text-xl leading-[25px] tracking-[-0.5px]">
{name}
</h2>
<span className="text-gray-300 text-xs leading-[18px]">
{description}
</span>
</div>
<button
onClick={() => requestCreateConvo(product)}
className="flex-none flex w-30 h-12 items-center text-sm justify-center gap-2 px-5 py-[10px] rounded-md bg-white leading-[21px] text-gray-800"
>
Try now
</button>
</div>
</div>
</div>
);
};
export default Slide;

View File

@ -1,54 +0,0 @@
import { FC, useCallback, useEffect, useState } from "react";
import Slide from "../Slide";
import useEmblaCarousel, { EmblaCarouselType } from "embla-carousel-react";
import { NextButton, PrevButton } from "../ButtonSlider";
import { Product } from "@/_models/Product";
type Props = {
products: Product[];
};
const Slider: FC<Props> = ({ products }) => {
const [emblaRef, emblaApi] = useEmblaCarousel();
const [prevBtnDisabled, setPrevBtnDisabled] = useState(true);
const [nextBtnDisabled, setNextBtnDisabled] = useState(true);
const scrollPrev = useCallback(
() => emblaApi && emblaApi.scrollPrev(),
[emblaApi]
);
const scrollNext = useCallback(
() => emblaApi && emblaApi.scrollNext(),
[emblaApi]
);
const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
setPrevBtnDisabled(!emblaApi.canScrollPrev());
setNextBtnDisabled(!emblaApi.canScrollNext());
}, []);
useEffect(() => {
if (!emblaApi) return;
onSelect(emblaApi);
emblaApi.on("reInit", onSelect);
emblaApi.on("select", onSelect);
}, [emblaApi, onSelect]);
return (
<div className="embla rounded-lg overflow-hidden relative mt-6 mx-6">
<div className="embla__viewport" ref={emblaRef}>
<div className="embla__container">
{products.map((product) => (
<Slide key={product.slug} product={product} />
))}
</div>
</div>
<div className="embla__buttons">
<PrevButton onClick={scrollPrev} disabled={prevBtnDisabled} />
<NextButton onClick={scrollNext} disabled={nextBtnDisabled} />
</div>
</div>
);
};
export default Slider;

View File

@ -1,11 +0,0 @@
type Props = {
title: string;
};
export const TitleBlankState: React.FC<Props> = ({ title }) => {
return (
<h2 className="text-[#6B7280] text-[20px] leading-[25px] tracking-[-0.4px] font-semibold">
{title}
</h2>
);
};

View File

@ -1,34 +0,0 @@
import React, { useState } from "react";
import Image from "next/image";
type Props = {
icon: string;
title: string;
expand: boolean;
onTitleClick: () => void;
};
const TogglableHeader: React.FC<Props> = ({
icon,
title,
expand,
onTitleClick,
}) => (
<button className="flex items-center justify-between" onClick={onTitleClick}>
<div className="flex items-center gap-2">
<Image src={icon} width={24} height={24} alt="" />
<span className="text-sm leading-5 font-semibold text-gray-900">
{title}
</span>
</div>
<Image
className={`${!expand ? "rotate-180" : "rotate-0"}`}
src={"icons/unicorn_angle-up.svg"}
width={24}
height={24}
alt=""
/>
</button>
);
export default React.memo(TogglableHeader);

View File

@ -1,102 +0,0 @@
import React, { useRef, useState } from "react";
import Image from "next/image";
import { FieldValues, UseFormRegister } from "react-hook-form";
type Props = {
register: UseFormRegister<FieldValues>;
};
export const UploadFileImage: React.FC<Props> = ({ register }) => {
const ref = useRef<HTMLInputElement>(null);
const [image, setImage] = useState<string | null>(null);
const [checked, setChecked] = useState<boolean>(true);
const [fileName, setFileName] = useState<string>("No selected file");
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const file = event.dataTransfer.files[0];
if (!file || file.type.split("/")[0] !== "image") return;
setImage(URL.createObjectURL(file));
setFileName(file.name);
};
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
};
const handleClick = () => {
ref.current?.click();
};
const onSelectedFile = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (!files || files.length === 0) return;
const file = files[0];
if (file.type.split("/")[0] !== "image") return;
setImage(URL.createObjectURL(file));
setFileName(file.name);
};
const handleDelete = () => {
setImage(null);
setFileName("No file selected");
};
return (
<div
className={`flex flex-col gap-[10px] py-3`}
onDrop={handleDrop}
onDragOver={handleDragOver}
>
{/* {image ? (
<div className="relative group">
<Image
style={{ width: "100%", height: "107px", objectFit: "cover" }}
src={image}
width={246}
height={104}
alt={fileName}
/>
<div className="hidden justify-center items-center absolute top-0 left-0 w-full h-full group-hover:flex group-hover:bg-[rgba(255, 255, 255, 0.2)]">
<button onClick={handleDelete}>Delete</button>
</div>
</div>
) : ( */}
<div
onClick={handleClick}
className="flex flex-col justify-center items-center py-5 px-2 gap-2 round-[2px] border border-dashed border-[#C8D0E0] rounded-sm"
>
{/* <Image src={"icons/ic_plus.svg"} width={14} height={14} alt="" />
<span className="text-gray-700 font-normal text-sm">
Drag an image here, or click to select
</span> */}
<input
{...register("fileInput", { required: true })}
// ref={ref}
type="file"
onChange={onSelectedFile}
accept="image/*"
/>
</div>
){/* } */}
<div
className="flex gap-2 items-center cursor-pointer"
onClick={() => setChecked(!checked)}
>
<input
checked={checked}
className="rounded"
type="checkbox"
onChange={() => setChecked(!checked)}
/>
<span className="text-sm leading-5 text-[#111928] pointer-events-none">
Crop center to fit output resolution
</span>
</div>
</div>
);
};