feat: add profile edit modal form
This commit is contained in:
parent
6433622470
commit
06fae9ca91
|
|
@ -12,7 +12,7 @@ import {
|
||||||
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 { professions } from "@/src/utils/const";
|
import { professionItems } from "@/src/utils/const";
|
||||||
|
|
||||||
export default function AnalysisClient() {
|
export default function AnalysisClient() {
|
||||||
const {
|
const {
|
||||||
|
|
@ -56,7 +56,7 @@ export default function AnalysisClient() {
|
||||||
className="bg-card border-border shadow-lg"
|
className="bg-card border-border shadow-lg"
|
||||||
position="popper"
|
position="popper"
|
||||||
>
|
>
|
||||||
{professions.map((item) => {
|
{professionItems.map((item) => {
|
||||||
const PIcon = item.icon;
|
const PIcon = item.icon;
|
||||||
return (
|
return (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
|
|
@ -85,7 +85,7 @@ export default function AnalysisClient() {
|
||||||
placeholder="Contoh: https://www.tokopedia.com/lenovo/thinkpad-x1-carbon"
|
placeholder="Contoh: https://www.tokopedia.com/lenovo/thinkpad-x1-carbon"
|
||||||
value={url1}
|
value={url1}
|
||||||
onChange={(e) => setUrl1(e.target.value)}
|
onChange={(e) => setUrl1(e.target.value)}
|
||||||
className="border rounded-md focus:ring-2 focus:ring-green-500"
|
className="border rounded-md focus:ring-2 focus:ring-primary"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -101,7 +101,7 @@ export default function AnalysisClient() {
|
||||||
placeholder="Contoh: https://www.tokopedia.com/..."
|
placeholder="Contoh: https://www.tokopedia.com/..."
|
||||||
value={url2}
|
value={url2}
|
||||||
onChange={(e) => setUrl2(e.target.value)}
|
onChange={(e) => setUrl2(e.target.value)}
|
||||||
className="border rounded-md focus:ring-2 focus:ring-green-500 w-full"
|
className="border rounded-md focus:ring-2 focus:ring-primary w-full"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -117,7 +117,7 @@ export default function AnalysisClient() {
|
||||||
placeholder="Contoh: https://www.tokopedia.com/..."
|
placeholder="Contoh: https://www.tokopedia.com/..."
|
||||||
value={url3}
|
value={url3}
|
||||||
onChange={(e) => setUrl3(e.target.value)}
|
onChange={(e) => setUrl3(e.target.value)}
|
||||||
className="border p-2 rounded-md focus:ring-2 focus:ring-green-500 w-full"
|
className="border p-2 rounded-md focus:ring-2 focus:ring-primary w-full"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,34 @@
|
||||||
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Pencil, Wallet, Laptop, User, Monitor, Fan } from "lucide-react";
|
import {
|
||||||
|
Pencil,
|
||||||
|
Wallet,
|
||||||
|
Laptop,
|
||||||
|
User,
|
||||||
|
Monitor,
|
||||||
|
Fan,
|
||||||
|
X,
|
||||||
|
Pickaxe,
|
||||||
|
Shell,
|
||||||
|
Save,
|
||||||
|
} from "lucide-react";
|
||||||
import { ProfileClientProps } from "@/src/types";
|
import { ProfileClientProps } from "@/src/types";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
import { brandFormat, formatRupiah } from "@/src/utils/datas";
|
import { brandFormat, formatRupiah } from "@/src/utils/datas";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "../ui/select";
|
||||||
|
import { brandItems, OSItems, professionItems } from "@/src/utils/const";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Label } from "../ui/label";
|
||||||
|
|
||||||
export default function ProfileCard({
|
export default function ProfileCard({
|
||||||
bio,
|
bio,
|
||||||
|
|
@ -15,10 +37,13 @@ export default function ProfileCard({
|
||||||
preferenceOS,
|
preferenceOS,
|
||||||
budgetMax,
|
budgetMax,
|
||||||
budgetMin,
|
budgetMin,
|
||||||
profession,
|
|
||||||
}: ProfileClientProps) {
|
}: ProfileClientProps) {
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const { brands } = brandFormat({ preferenceBrand });
|
const { brands } = brandFormat({ preferenceBrand });
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [profession, setProfession] = useState("");
|
||||||
|
const [brand, setBrand] = useState("");
|
||||||
|
const [OS, setOS] = useState("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -61,6 +86,7 @@ export default function ProfileCard({
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
className="w-full sm:w-auto gap-2 rounded-full shadow-sm"
|
className="w-full sm:w-auto gap-2 rounded-full shadow-sm"
|
||||||
|
onClick={() => setShowModal(true)}
|
||||||
>
|
>
|
||||||
<Pencil className="h-4 w-4" />
|
<Pencil className="h-4 w-4" />
|
||||||
Edit Profile
|
Edit Profile
|
||||||
|
|
@ -140,12 +166,14 @@ export default function ProfileCard({
|
||||||
{budgetMin || budgetMax ? (
|
{budgetMin || budgetMax ? (
|
||||||
<div className="flex flex-col justify-center h-[calc(100%-2rem)]">
|
<div className="flex flex-col justify-center h-[calc(100%-2rem)]">
|
||||||
<p className="text-sm text-muted-foreground mb-1">Dari</p>
|
<p className="text-sm text-muted-foreground mb-1">Dari</p>
|
||||||
<p className="text-xl font-bold">{formatRupiah(budgetMin)}</p>
|
<p className="text-xl font-semibold">
|
||||||
|
{formatRupiah(budgetMin)}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="my-2 h-px w-full bg-border"></div>
|
<div className="my-2 h-px w-full bg-border"></div>
|
||||||
|
|
||||||
<p className="text-sm text-muted-foreground mb-1">Hingga</p>
|
<p className="text-sm text-muted-foreground mb-1">Hingga</p>
|
||||||
<p className="text-xl font-bold text-sentiment-positive">
|
<p className="text-xl font-semibold text-sentiment-positive">
|
||||||
{formatRupiah(budgetMax)}
|
{formatRupiah(budgetMax)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -159,6 +187,209 @@ export default function ProfileCard({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{showModal && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.2, ease: "circOut" }}
|
||||||
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
action=""
|
||||||
|
className=" flex flex-col bg-card w-1/3 p-6 rounded-2xl border relative"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-1 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<User className="w-4 h-4" />
|
||||||
|
<Label htmlFor="username" className="font-semibold">
|
||||||
|
Nama Lengkap
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
id="username"
|
||||||
|
type="text"
|
||||||
|
placeholder="Masukkan nama lengkap Anda"
|
||||||
|
className="border rounded-md focus:ring-2 focus:ring-primary"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Pickaxe className="w-4 h-4" />
|
||||||
|
<Label htmlFor="profession" className="font-semibold">
|
||||||
|
Profesi
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
name="profession"
|
||||||
|
value={profession}
|
||||||
|
onValueChange={setProfession}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<SelectTrigger
|
||||||
|
className={`w-full ${!profession ? "text-gray-500" : "text-black"}`}
|
||||||
|
>
|
||||||
|
<SelectValue placeholder="Pilih Profesi/Kebutuhan" />
|
||||||
|
</SelectTrigger>
|
||||||
|
|
||||||
|
<SelectContent
|
||||||
|
className="bg-card border-border shadow-lg"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
{professionItems.map((item) => {
|
||||||
|
const PIcon = item.icon;
|
||||||
|
return (
|
||||||
|
<SelectItem
|
||||||
|
key={item.value}
|
||||||
|
value={item.value}
|
||||||
|
className="cursor-pointer hover:bg-primary hover:text-card focus:bg-primary focus:text-card"
|
||||||
|
>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<span>
|
||||||
|
<PIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</span>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Laptop className="w-4 h-4" />
|
||||||
|
<Label htmlFor="brand" className="font-semibold">
|
||||||
|
Merek Laptop
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
name="brand"
|
||||||
|
value={brand}
|
||||||
|
onValueChange={setBrand}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<SelectTrigger
|
||||||
|
className={`w-full ${!brand ? "text-gray-500" : "text-black"}`}
|
||||||
|
>
|
||||||
|
<SelectValue placeholder="Pilih Merek Laptop" />
|
||||||
|
</SelectTrigger>
|
||||||
|
|
||||||
|
<SelectContent
|
||||||
|
className="bg-card border-border shadow-lg"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
{brandItems.map((item) => {
|
||||||
|
const PIcon = item.icon;
|
||||||
|
return (
|
||||||
|
<SelectItem
|
||||||
|
key={item.value}
|
||||||
|
value={item.value}
|
||||||
|
className="cursor-pointer hover:bg-primary hover:text-card focus:bg-primary focus:text-card"
|
||||||
|
>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<span>
|
||||||
|
<PIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</span>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Shell className="w-4 h-4" />
|
||||||
|
<Label htmlFor="OS" className="font-semibold">
|
||||||
|
Sistem Operasi
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<Select name="OS" value={OS} onValueChange={setOS} required>
|
||||||
|
<SelectTrigger
|
||||||
|
className={`w-full -mt-1 ${!profession ? "text-gray-500" : "text-black"}`}
|
||||||
|
>
|
||||||
|
<SelectValue placeholder="Pilih Sistem Operasi" />
|
||||||
|
</SelectTrigger>
|
||||||
|
|
||||||
|
<SelectContent
|
||||||
|
className="bg-card border-border shadow-lg"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
{OSItems.map((item) => {
|
||||||
|
const PIcon = item.icon;
|
||||||
|
return (
|
||||||
|
<SelectItem
|
||||||
|
key={item.value}
|
||||||
|
value={item.value}
|
||||||
|
className="cursor-pointer hover:bg-primary hover:text-card focus:bg-primary focus:text-card"
|
||||||
|
>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<span>
|
||||||
|
<PIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</span>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Wallet className="w-4 h-4" />
|
||||||
|
<Label htmlFor="budget" className="font-semibold">
|
||||||
|
Rentang Anggaran
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<div className="flex-col w-1/2">
|
||||||
|
<Label htmlFor="budget" className="text-xs mt-2">
|
||||||
|
Rp (Minimal)
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="budget"
|
||||||
|
type="text"
|
||||||
|
placeholder="Rp 0"
|
||||||
|
className="border rounded-md focus:ring-2 focus:ring-primary mt-1"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-col w-1/2">
|
||||||
|
<Label htmlFor="budget" className="text-xs mt-2">
|
||||||
|
Rp (Maksimal)
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="budget"
|
||||||
|
type="text"
|
||||||
|
placeholder="Rp 0"
|
||||||
|
className="border rounded-md focus:ring-2 focus:ring-primary mt-1"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 flex justify-start gap-4">
|
||||||
|
<Button onClick={() => setShowModal(false)} variant={"outline"}>
|
||||||
|
<X />
|
||||||
|
<span>Cancel</span>
|
||||||
|
</Button>
|
||||||
|
<Button type="submit">
|
||||||
|
<Save />
|
||||||
|
<span>Simpan</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
import { Book, Briefcase, Code, GamepadDirectional, Laptop, Palette } from "lucide-react";
|
import {
|
||||||
|
Book,
|
||||||
|
Code,
|
||||||
|
GamepadDirectional,
|
||||||
|
LucideCircleEllipsis,
|
||||||
|
Palette,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { SiAcer, SiAsus, SiLenovo, SiLinux, SiMacos } from "react-icons/si";
|
||||||
|
import { FaWindows } from "react-icons/fa";
|
||||||
|
|
||||||
export const MODEL_OPTIONS = [
|
export const MODEL_OPTIONS = [
|
||||||
{
|
{
|
||||||
|
|
@ -20,9 +28,23 @@ export const MODEL_OPTIONS = [
|
||||||
|
|
||||||
export const WORD_LIMIT = 15;
|
export const WORD_LIMIT = 15;
|
||||||
|
|
||||||
export const professions = [
|
export const professionItems = [
|
||||||
{ value: "programmer", label: "Programmer", icon: Code },
|
{ value: "programmer", label: "Programmer", icon: Code },
|
||||||
{ value: "designer", label: "Designer", icon: Palette },
|
{ value: "designer", label: "Designer", icon: Palette },
|
||||||
{ value: "student", label: "Student", icon: Book },
|
{ value: "student", label: "Student", icon: Book },
|
||||||
{ value: "gamer", label: "Gamer", icon: GamepadDirectional },
|
{ value: "gamer", label: "Gamer", icon: GamepadDirectional },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const brandItems = [
|
||||||
|
{ value: "asus", label: "Asus", icon: SiAsus },
|
||||||
|
{ value: "acer", label: "Acer", icon: SiAcer },
|
||||||
|
{ value: "lenovo", label: "Lenovo", icon: SiLenovo },
|
||||||
|
{ value: "other", label: "Other", icon: LucideCircleEllipsis },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const OSItems = [
|
||||||
|
{ value: "windows", label: "Windows", icon: FaWindows },
|
||||||
|
{ value: "macos", label: "Macos", icon: SiMacos },
|
||||||
|
{ value: "linux", label: "Linux", icon: SiLinux },
|
||||||
|
{ value: "other", label: "Other", icon: LucideCircleEllipsis },
|
||||||
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue