style: add auto scroll result analysis section
This commit is contained in:
parent
8204b15b4b
commit
bfc91e3a22
|
|
@ -26,6 +26,7 @@ export default function AnalysisClient() {
|
||||||
loading,
|
loading,
|
||||||
result,
|
result,
|
||||||
showField,
|
showField,
|
||||||
|
resultRef,
|
||||||
setShowField,
|
setShowField,
|
||||||
} = useAnalyseText();
|
} = useAnalyseText();
|
||||||
|
|
||||||
|
|
@ -151,50 +152,6 @@ export default function AnalysisClient() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* {showField ? (
|
|
||||||
<div className="w-1/2 mr-auto animate-in fade-in">
|
|
||||||
<label className="block mb-1 text-sm font-medium text-gray-700">
|
|
||||||
Tautan Produk 3
|
|
||||||
</label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<div className="w-full flex flex-col">
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Contoh: https://www.tokopedia.com/lenovo/thinkpad-x1-carbon"
|
|
||||||
className={`w-full ${errors.url3 ? "border-red-500 focus:ring-red-500" : "focus:ring-primary"}`}
|
|
||||||
{...register("url3")}
|
|
||||||
/>
|
|
||||||
{errors.url3 && (
|
|
||||||
<p className="text-red-500 text-xs mt-1">
|
|
||||||
{errors.url3.message}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
setShowField(false);
|
|
||||||
setValue("url3", "");
|
|
||||||
}}
|
|
||||||
className="text-red-500"
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="w-max mt-4">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowField(true)}
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
+ Tambah Tautan Produk Lainnya
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
{showField ? (
|
{showField ? (
|
||||||
<div className="w-1/2 animate-in fade-in slide-in-from-bottom-2 duration-300">
|
<div className="w-1/2 animate-in fade-in slide-in-from-bottom-2 duration-300">
|
||||||
<label className="block mb-1 text-sm font-medium text-gray-700">
|
<label className="block mb-1 text-sm font-medium text-gray-700">
|
||||||
|
|
@ -236,10 +193,6 @@ export default function AnalysisClient() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <div className="flex w-full gap-4 justify-center">
|
|
||||||
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -252,7 +205,9 @@ export default function AnalysisClient() {
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<ResultSection result={result} />
|
<div ref={resultRef} id="result-section" className="scroll-mt-20">
|
||||||
|
{result && <ResultSection result={result} />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,11 @@
|
||||||
import { ResultProps } from "@/src/types";
|
import { ResultProps } from "@/src/types";
|
||||||
|
import { getGridClass } from "@/src/utils/datas";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Trophy, ExternalLink, CheckCircle2, TrendingUp } from "lucide-react";
|
import { Trophy, ExternalLink, CheckCircle2, TrendingUp } from "lucide-react";
|
||||||
|
|
||||||
export default function ResultSection({ result }: ResultProps) {
|
export default function ResultSection({ result }: ResultProps) {
|
||||||
if (!result) return null;
|
if (!result) return null;
|
||||||
|
|
||||||
const getGridClass = (count: number) => {
|
|
||||||
if (count === 1) return "max-w-md mx-auto";
|
|
||||||
if (count === 2) return "grid-cols-1 md:grid-cols-2";
|
|
||||||
return "grid-cols-1 md:grid-cols-2 lg:grid-cols-3";
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="w-full mx-auto"
|
className="w-full mx-auto"
|
||||||
|
|
@ -39,7 +34,7 @@ export default function ResultSection({ result }: ResultProps) {
|
||||||
<div className="absolute top-0 right-0 -mt-10 -mr-10 w-64 h-64 bg-white/10 rounded-full blur-3xl"></div>
|
<div className="absolute top-0 right-0 -mt-10 -mr-10 w-64 h-64 bg-white/10 rounded-full blur-3xl"></div>
|
||||||
<div className="absolute bottom-0 left-0 -mb-10 -ml-10 w-40 h-40 bg-primary/20 rounded-full blur-3xl"></div>
|
<div className="absolute bottom-0 left-0 -mb-10 -ml-10 w-40 h-40 bg-primary/20 rounded-full blur-3xl"></div>
|
||||||
|
|
||||||
<div className="relative z-10">
|
<div className="relative">
|
||||||
<div className="inline-flex items-center gap-2 bg-white/10 backdrop-blur-md px-4 py-1.5 rounded-full border border-white/20 mb-6 shadow-sm">
|
<div className="inline-flex items-center gap-2 bg-white/10 backdrop-blur-md px-4 py-1.5 rounded-full border border-white/20 mb-6 shadow-sm">
|
||||||
<Trophy className="w-4 h-4 text-yellow-300" />
|
<Trophy className="w-4 h-4 text-yellow-300" />
|
||||||
<span className="text-sm font-semibold tracking-wide uppercase text-white">
|
<span className="text-sm font-semibold tracking-wide uppercase text-white">
|
||||||
|
|
@ -82,7 +77,7 @@ export default function ResultSection({ result }: ResultProps) {
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isWinner && (
|
{isWinner && (
|
||||||
<div className="absolute -top-4 left-1/2 transform -translate-x-1/2 bg-sentiment-positive text-white px-6 py-1.5 rounded-full shadow-lg flex items-center gap-2 z-20">
|
<div className="absolute -top-4 left-1/2 transform -translate-x-1/2 bg-sentiment-positive text-white px-6 py-1.5 rounded-full shadow-lg flex items-center gap-2">
|
||||||
<Trophy className="w-4 h-4 fill-yellow-400 text-yellow-400" />
|
<Trophy className="w-4 h-4 fill-yellow-400 text-yellow-400" />
|
||||||
<span className="text-xs font-bold uppercase tracking-wider">
|
<span className="text-xs font-bold uppercase tracking-wider">
|
||||||
Pemenang
|
Pemenang
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
@ -15,10 +15,10 @@ export type AnalyzeFormData = z.infer<typeof analyzeSchema>;
|
||||||
|
|
||||||
export const useAnalyseText = () => {
|
export const useAnalyseText = () => {
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [result, setResult] = useState<AnalysisResults | null>(null);
|
const [result, setResult] = useState<AnalysisResults | null>(null);
|
||||||
const [showField, setShowField] = useState(false);
|
const [showField, setShowField] = useState(false);
|
||||||
|
const resultRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|
@ -97,6 +97,19 @@ export const useAnalyseText = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading && result) {
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
resultRef.current?.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "start",
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
}, [loading, result]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
control,
|
control,
|
||||||
register,
|
register,
|
||||||
|
|
@ -108,6 +121,7 @@ export const useAnalyseText = () => {
|
||||||
loading,
|
loading,
|
||||||
result,
|
result,
|
||||||
showField,
|
showField,
|
||||||
|
resultRef,
|
||||||
setShowField,
|
setShowField,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -108,3 +108,9 @@ export const getVisiblePages = (data: VisiblePageProps) => {
|
||||||
data.totalPages,
|
data.totalPages,
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getGridClass = (count: number) => {
|
||||||
|
if (count === 1) return "max-w-md mx-auto";
|
||||||
|
if (count === 2) return "grid-cols-1 md:grid-cols-2";
|
||||||
|
return "grid-cols-1 md:grid-cols-2 lg:grid-cols-3";
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue