* fix: correct link for Roadmap navigation item * feat: add feature request and projects voting components * fix: correct spelling of 'Certificate' in ProjectsVote component * feat: add Request Feature popup with responsive dialog and drawer components * feat: enhance Header component with mobile menu animations and improved navigation styling * refactor: simplify Header component by removing unused scroll handling and improving mobile menu structure * fix: replace motion span with static span for header underline * fix: update FeatureRequestCTA text for clarity and adjust page layout margins * fix: remove DialogDescription from RequestFeaturePopup and update textarea placeholder for clarity * fix: remove DrawerDescription from RequestFeaturePopup for cleaner UI * fix: remove unused icon exports from icons * fix: replace XIcon with LuX in DialogClose for consistency * fix: remove unused icon export from index for cleaner module structure
121 lines
3.1 KiB
TypeScript
121 lines
3.1 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
|
|
import { useMediaQuery } from '@/hooks/use-media-query';
|
|
import {
|
|
Button,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
Drawer,
|
|
DrawerClose,
|
|
DrawerContent,
|
|
DrawerFooter,
|
|
DrawerHeader,
|
|
DrawerTitle,
|
|
DrawerTrigger,
|
|
Input,
|
|
Label,
|
|
Textarea,
|
|
} from '@components';
|
|
import { cn } from '@utils';
|
|
|
|
export function RequestFeaturePopup() {
|
|
const [open, setOpen] = React.useState(false);
|
|
const isDesktop = useMediaQuery('(min-width: 768px)');
|
|
|
|
if (isDesktop) {
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button variant="secondary">Request Fitur</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="sm:max-w-[600px]">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-2xl">Request Fitur</DialogTitle>
|
|
</DialogHeader>
|
|
<ProfileForm />
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Drawer open={open} onOpenChange={setOpen}>
|
|
<DrawerTrigger asChild>
|
|
<Button variant="secondary">Request Fitur</Button>
|
|
</DrawerTrigger>
|
|
<DrawerContent>
|
|
<DrawerHeader className="text-left">
|
|
<DrawerTitle>Request Fitur</DrawerTitle>
|
|
</DrawerHeader>
|
|
<ProfileForm className="px-4" />
|
|
<DrawerFooter className="pt-2">
|
|
<DrawerClose asChild>
|
|
<Button variant="bordered">Cancel</Button>
|
|
</DrawerClose>
|
|
</DrawerFooter>
|
|
</DrawerContent>
|
|
</Drawer>
|
|
);
|
|
}
|
|
|
|
interface ProfileFormProps extends React.ComponentProps<'form'> {
|
|
showFooterButton?: boolean;
|
|
}
|
|
|
|
function ProfileForm({
|
|
className,
|
|
showFooterButton = false,
|
|
}: ProfileFormProps) {
|
|
const [description, setDescription] = React.useState('');
|
|
const maxDescriptionLength = 10000;
|
|
|
|
return (
|
|
<form className={cn('grid items-start gap-8', className)}>
|
|
<div className="grid gap-4">
|
|
<Label htmlFor="namaFitur" className="font-medium">
|
|
Nama Fitur *
|
|
</Label>
|
|
<Input
|
|
id="namaFitur"
|
|
placeholder="Masukkan nama fitur yang diinginkan"
|
|
className="h-11"
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid gap-4">
|
|
<div className="flex justify-between items-center">
|
|
<Label htmlFor="deskripsiFitur" className="font-medium">
|
|
Deskripsi Fitur *
|
|
</Label>
|
|
<span className="text-sm text-muted-foreground">
|
|
{description.length}/{maxDescriptionLength}
|
|
</span>
|
|
</div>
|
|
<Textarea
|
|
id="deskripsiFitur"
|
|
placeholder="Jelaskan fitur yang Anda inginkan secara detail"
|
|
maxLength={maxDescriptionLength}
|
|
rows={6}
|
|
className="min-h-[150px]"
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
/>
|
|
<p className="text-sm text-muted-foreground -mt-2">
|
|
Jelaskan manfaat dan cara kerja fitur yang Anda inginkan
|
|
</p>
|
|
</div>
|
|
|
|
{!showFooterButton && (
|
|
<Button type="submit" size="lg" className="mt-2">
|
|
Kirim Permintaan
|
|
</Button>
|
|
)}
|
|
</form>
|
|
);
|
|
}
|