refactor: separate url input components
This commit is contained in:
parent
a106c5d6c1
commit
1593fd75bd
|
|
@ -5,6 +5,7 @@ import { Sparkles, X } from "lucide-react";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import ResultSection from "./ResultSection";
|
import ResultSection from "./ResultSection";
|
||||||
|
import UrlInputList from "./UrlInputList";
|
||||||
|
|
||||||
export default function AnalysisClient() {
|
export default function AnalysisClient() {
|
||||||
const {
|
const {
|
||||||
|
|
@ -23,39 +24,6 @@ export default function AnalysisClient() {
|
||||||
setVisibleFields,
|
setVisibleFields,
|
||||||
} = useAnalyseText();
|
} = useAnalyseText();
|
||||||
|
|
||||||
const urlInput = () => {
|
|
||||||
return urlDatas.slice(0, visibleFields).map((item, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="animate-in fade-in slide-in-from-bottom-2 duration-300"
|
|
||||||
>
|
|
||||||
<label className="block mb-1 text-sm font-medium text-gray-700">
|
|
||||||
{item.labels}
|
|
||||||
</label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<div className="flex-1">
|
|
||||||
<Input
|
|
||||||
type="url"
|
|
||||||
placeholder="Contoh: https://tokopedia.com/..."
|
|
||||||
className={`${item.errors ? "border-sentiment-negative" : "focus:ring-primary"}`}
|
|
||||||
{...item.title}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{index === visibleFields - 1 && (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => setVisibleFields((prev) => prev - 1)}
|
|
||||||
className="text-sentiment-negative hover:text-sentiment-negative hover:bg-sentiment-negative-light shrink-0"
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full mx-auto">
|
<div className="w-full mx-auto">
|
||||||
<form
|
<form
|
||||||
|
|
@ -105,7 +73,11 @@ export default function AnalysisClient() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-end transition-all duration-500">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-end transition-all duration-500">
|
||||||
{urlInput()}
|
<UrlInputList
|
||||||
|
urlDatas={urlDatas}
|
||||||
|
visibleFields={visibleFields}
|
||||||
|
setVisibleFields={setVisibleFields}
|
||||||
|
/>
|
||||||
|
|
||||||
{visibleFields < 2 && (
|
{visibleFields < 2 && (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { UrlInputItemProps } from "@/src/types";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
|
||||||
|
const UrlInputItem = ({
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
visibleFields,
|
||||||
|
onRemove,
|
||||||
|
}: UrlInputItemProps) => {
|
||||||
|
return (
|
||||||
|
<div className="animate-in fade-in slide-in-from-bottom-2 duration-300">
|
||||||
|
<label className="block mb-1 text-sm font-medium text-gray-700">
|
||||||
|
{item.labels}
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="flex-1">
|
||||||
|
<Input
|
||||||
|
type="url"
|
||||||
|
placeholder="Contoh: https://tokopedia.com/..."
|
||||||
|
className={`${item.errors ? "border-sentiment-negative" : "focus:ring-primary"}`}
|
||||||
|
{...item.title}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{index === visibleFields - 1 && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={onRemove}
|
||||||
|
className="text-sentiment-negative hover:text-sentiment-negative hover:bg-sentiment-negative-light shrink-0"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UrlInputItem;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { UrlInputListProps } from "@/src/types";
|
||||||
|
import UrlInputItem from "./UrlInputItem";
|
||||||
|
|
||||||
|
const UrlInputList = ({
|
||||||
|
urlDatas,
|
||||||
|
visibleFields,
|
||||||
|
setVisibleFields,
|
||||||
|
}: UrlInputListProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{urlDatas.slice(0, visibleFields).map((item, index) => (
|
||||||
|
<UrlInputItem
|
||||||
|
key={index}
|
||||||
|
item={item}
|
||||||
|
index={index}
|
||||||
|
visibleFields={visibleFields}
|
||||||
|
onRemove={() => setVisibleFields((prev) => prev - 1)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UrlInputList;
|
||||||
|
|
@ -5,6 +5,7 @@ import { profileSchema } from "../app/validation/profile.schema";
|
||||||
import { Session } from "next-auth";
|
import { Session } from "next-auth";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { analyzeSchema } from "../app/validation/analyze.schema";
|
import { analyzeSchema } from "../app/validation/analyze.schema";
|
||||||
|
import { FieldError, UseFormRegisterReturn } from "react-hook-form";
|
||||||
|
|
||||||
export interface ModelDB {
|
export interface ModelDB {
|
||||||
modelName: string;
|
modelName: string;
|
||||||
|
|
@ -19,7 +20,7 @@ export interface ModelDB {
|
||||||
export interface ProfileClientProps {
|
export interface ProfileClientProps {
|
||||||
name: string;
|
name: string;
|
||||||
bio?: string;
|
bio?: string;
|
||||||
preferenceBrand: string
|
preferenceBrand: string;
|
||||||
preferenceOS: string;
|
preferenceOS: string;
|
||||||
budgetMin: number;
|
budgetMin: number;
|
||||||
budgetMax: number;
|
budgetMax: number;
|
||||||
|
|
@ -371,4 +372,23 @@ export interface AnalysisWithMetric {
|
||||||
metricId: number;
|
metricId: number;
|
||||||
name: string;
|
name: string;
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UrlData = {
|
||||||
|
labels: string;
|
||||||
|
errors: FieldError | undefined;
|
||||||
|
title: UseFormRegisterReturn;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UrlInputItemProps = {
|
||||||
|
item: UrlData;
|
||||||
|
index: number;
|
||||||
|
visibleFields: number;
|
||||||
|
onRemove: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UrlInputListProps = {
|
||||||
|
urlDatas: UrlData[];
|
||||||
|
visibleFields: number;
|
||||||
|
setVisibleFields: React.Dispatch<React.SetStateAction<number>>;
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue