ema-frontend-demo / src /components /admin /SettingsModal.tsx
ishaq101's picture
Refactor API calls to direct backend integration, add chatbot configuration system, and improve UI components
db983c6
"use client";
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { X, Save, RotateCcw, Check } from "lucide-react";
import { Button } from "@/components/ui/Button";
import { CHATBOT_CONFIG, type ChatbotConfig, getChatbotConfig, saveChatbotConfig, resetChatbotConfig } from "@/lib/chatbotConfig";
import { cn } from "@/lib/utils";
interface SettingsModalProps {
isOpen: boolean;
onClose: () => void;
}
export function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
const [config, setConfig] = useState<ChatbotConfig>(CHATBOT_CONFIG);
const [saved, setSaved] = useState(false);
// Load current config when modal opens
useEffect(() => {
if (isOpen) {
setConfig(getChatbotConfig());
setSaved(false);
}
}, [isOpen]);
const handleChange = (field: keyof ChatbotConfig, value: string) => {
setConfig((prev) => ({ ...prev, [field]: value }));
};
const handleSave = () => {
saveChatbotConfig(config);
setSaved(true);
// Close after showing success
setTimeout(() => {
onClose();
}, 800);
};
const handleReset = () => {
setConfig(CHATBOT_CONFIG);
resetChatbotConfig();
};
return (
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className="fixed inset-0 bg-black/40 backdrop-blur-sm z-50"
onClick={onClose}
/>
{/* Modal */}
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 8 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 8 }}
transition={{ duration: 0.2, ease: "easeOut" }}
className="w-full max-w-md bg-white rounded-2xl shadow-2xl overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-neutral-100">
<h2 className="text-lg font-semibold text-neutral-900">
Pengaturan Chatbot
</h2>
<button
onClick={onClose}
className="p-1.5 rounded-lg text-neutral-400 hover:bg-neutral-100 hover:text-neutral-700 transition-colors"
>
<X className="h-4 w-4" />
</button>
</div>
{/* Body */}
<div className="px-6 py-5 space-y-5">
{/* Name */}
<div className="flex flex-col gap-2">
<label className="text-sm font-medium text-neutral-700">
Nama Chatbot
</label>
<input
type="text"
value={config.name}
onChange={(e) => handleChange("name", e.target.value)}
maxLength={30}
className="w-full rounded-xl border border-neutral-200 bg-white px-4 py-2.5 text-sm text-neutral-900 placeholder:text-neutral-400 transition-all duration-200 outline-none focus:border-brand-green focus:ring-3 focus:ring-brand-green/10"
placeholder="Contoh: EMA"
/>
<p className="text-xs text-neutral-400">{config.name.length}/30 karakter</p>
</div>
{/* Caption */}
<div className="flex flex-col gap-2">
<label className="text-sm font-medium text-neutral-700">
Caption / Tagline
</label>
<input
type="text"
value={config.caption}
onChange={(e) => handleChange("caption", e.target.value)}
maxLength={50}
className="w-full rounded-xl border border-neutral-200 bg-white px-4 py-2.5 text-sm text-neutral-900 placeholder:text-neutral-400 transition-all duration-200 outline-none focus:border-brand-green focus:ring-3 focus:ring-brand-green/10"
placeholder="Contoh: Expert Mining Assistant"
/>
<p className="text-xs text-neutral-400">{config.caption.length}/50 karakter</p>
</div>
{/* Description */}
<div className="flex flex-col gap-2">
<label className="text-sm font-medium text-neutral-700">
Deskripsi
</label>
<textarea
value={config.description}
onChange={(e) => handleChange("description", e.target.value)}
maxLength={200}
rows={3}
className="w-full rounded-xl border border-neutral-200 bg-white px-4 py-2.5 text-sm text-neutral-900 placeholder:text-neutral-400 transition-all duration-200 outline-none focus:border-brand-green focus:ring-3 focus:ring-brand-green/10 resize-none"
placeholder="Deskripsi singkat tentang chatbot"
/>
<p className="text-xs text-neutral-400">{config.description.length}/200 karakter</p>
</div>
{/* Preview */}
<div className="p-3 rounded-xl bg-neutral-50 border border-neutral-100">
<p className="text-xs font-medium text-neutral-400 mb-2">Preview</p>
<p className="text-sm font-semibold text-neutral-900">{config.name}</p>
<p className="text-xs text-neutral-500">{config.caption}</p>
<p className="text-xs text-neutral-400 mt-1 leading-relaxed line-clamp-2">{config.description}</p>
</div>
</div>
{/* Footer */}
<div className="flex items-center justify-between px-6 py-4 border-t border-neutral-100 bg-neutral-50/50">
<Button
variant="ghost"
size="sm"
onClick={handleReset}
className="text-neutral-500"
>
<RotateCcw className="h-3.5 w-3.5" />
Reset
</Button>
<div className="flex items-center gap-2">
<Button
variant="secondary"
size="sm"
onClick={onClose}
>
Batal
</Button>
<AnimatePresence mode="wait">
{saved ? (
<motion.div
key="saved"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl bg-green-500 text-white text-sm font-semibold"
>
<Check className="h-3.5 w-3.5" />
Tersimpan
</motion.div>
) : (
<motion.div
key="save"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
>
<Button
variant="primary"
size="sm"
onClick={handleSave}
>
<Save className="h-3.5 w-3.5" />
Simpan
</Button>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</motion.div>
</div>
</>
)}
</AnimatePresence>
);
}