This commit is contained in:
Raja Oktafrianto
2025-05-11 13:27:28 +07:00
parent 2a1c715285
commit d21d43ba56
4 changed files with 120 additions and 57 deletions

View File

@ -129,6 +129,7 @@ const AddDialog = () => {
<Input <Input
className={`input col-span-6 ${errors.name ? 'border-red-500' : ''}`} className={`input col-span-6 ${errors.name ? 'border-red-500' : ''}`}
type="text" type="text"
placeholder="Enter Name"
autoComplete="off" autoComplete="off"
value={formField.name} value={formField.name}
onChange={({ target }) => { onChange={({ target }) => {
@ -149,15 +150,15 @@ const AddDialog = () => {
Description <span className="text-red-500">*</span> Description <span className="text-red-500">*</span>
</label> </label>
<Textarea <Textarea
className="input col-span-6" className={`input col-span-6 ${errors.name ? 'border-red-500' : ''}`}
name="description" name="description"
placeholder="Enter Description" placeholder="Enter Description"
value={formField.description} value={formField.description}
onChange={handleChange} onChange={handleChange}
/> />
{errors.type && ( {errors.description && (
<span className="text-red-500 text-xs mt-1 col-span-8 ml-[calc(25%+0.5rem)]"> <span className="text-red-500 text-xs mt-1 col-span-8 ml-[calc(25%+0.5rem)]">
{errors.type} {errors.description}
</span> </span>
)} )}
</div> </div>

View File

@ -27,6 +27,7 @@ import { useManageNotificationContext } from '../hooks/useManageNotificationCont
import { doSaveLogActivity } from '@/actions/GlobalActions'; import { doSaveLogActivity } from '@/actions/GlobalActions';
import { CustomerProps } from '@/pages/master/provider/blocks/AddDialog'; import { CustomerProps } from '@/pages/master/provider/blocks/AddDialog';
import { Check, ChevronDown } from 'lucide-react'; import { Check, ChevronDown } from 'lucide-react';
import { initialStateNotification, selectedNotification, validateFormNotification } from './Types';
const API_URL_CUSTOMER = apiConfig.service_customer; const API_URL_CUSTOMER = apiConfig.service_customer;
const API_URL_NOTIFICATION = apiConfig.service_notification; const API_URL_NOTIFICATION = apiConfig.service_notification;
@ -37,32 +38,34 @@ const AddDialog = () => {
const { GetData, PostData } = useCallApi(); const { GetData, PostData } = useCallApi();
const [alert, setAlert] = useState({ show: false, message: '' }); const [alert, setAlert] = useState({ show: false, message: '' });
const isSubmittingRef = useRef(false); const isSubmittingRef = useRef(false);
const [errors, setErrors] = useState<Record<string, string>>({});
const initialState = { const [formField, setFormField] = useState<selectedNotification>(initialStateNotification);
customers: [] as string[],
all_customer: '',
type: '',
via: '',
subject: '',
content: ''
};
const [formField, setFormField] = useState(initialState);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [customers, setCustomers] = useState<CustomerProps[]>([]); const [customers, setCustomers] = useState<CustomerProps[]>([]);
const resetForm = () => { const resetForm = () => {
setFormField({ ...initialState }); setFormField({ ...initialStateNotification });
setIsSubmitting(false); setIsSubmitting(false);
setAlert({ show: false, message: '' }); setAlert({ show: false, message: '' });
setErrors({});
}; };
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormField({ ...formField, [e.target.name]: e.target.value }); const { name, value } = e.target;
setFormField({ ...formField, [name]: value });
if (e.target.name === 'all_customer' && e.target.value === 'true') { if (name === 'all_customer' && value === 'true') {
setFormField((prev) => ({ ...prev, customers: [] })); setFormField((prev) => ({ ...prev, customers: [] }));
} }
if (errors[name]) {
setErrors((prevErrors) => {
const updatedErrors = { ...prevErrors };
delete updatedErrors[name];
return updatedErrors;
});
}
}; };
const doCreateNotification = useCallback( const doCreateNotification = useCallback(
@ -120,32 +123,8 @@ const AddDialog = () => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
if (!validateFormNotification(formField, setErrors)) return;
if (isSubmittingRef.current) return;
if (
formField.all_customer.trim() === '' ||
formField.via.trim() === '' ||
formField.content.trim() === ''
) {
setAlert({ show: true, message: 'Please fill all required fields.' });
return;
}
if (formField.via === 'email' && formField.subject.trim() === '') {
setAlert({ show: true, message: 'Subject is required for E-Mail' });
return;
}
if (formField.via === 'fcm' && formField.subject.trim() === '') {
setAlert({ show: true, message: 'Subject is required form FCM' });
return;
}
setIsSubmitting(true);
doCreateNotification(e); doCreateNotification(e);
// console.log(formField);
setAlert({ show: false, message: '' });
}; };
useEffect(() => { useEffect(() => {
@ -179,7 +158,11 @@ const AddDialog = () => {
<label className="form-label flex items-center gap-1 max-w-56"> <label className="form-label flex items-center gap-1 max-w-56">
Send To<span className="text-red-500">*</span> Send To<span className="text-red-500">*</span>
</label> </label>
<div className="flex gap-6 items-center"> <div
className={`flex gap-6 items-center border rounded-md px-3 py-1 ${
errors.all_customer ? 'border-red-500' : 'border-gray-300'
}`}
>
<label className="flex items-center space-x-2"> <label className="flex items-center space-x-2">
<input <input
type="radio" type="radio"
@ -204,6 +187,13 @@ const AddDialog = () => {
</label> </label>
</div> </div>
</div> </div>
{errors.all_customer && (
<div className="w-full">
<span className="text-red-500 text-xs mt-3 ml-[calc(30%+2rem)] block">
{errors.all_customer}
</span>
</div>
)}
</div> </div>
<div className="w-full"> <div className="w-full">
@ -277,7 +267,11 @@ const AddDialog = () => {
<label className="form-label flex items-center gap-1 max-w-56"> <label className="form-label flex items-center gap-1 max-w-56">
Type<span className="text-red-500">*</span> Type<span className="text-red-500">*</span>
</label> </label>
<div className="flex gap-6 items-center"> <div
className={`flex gap-6 items-center border rounded-md px-3 py-1 ${
errors.type ? 'border-red-500' : 'border-gray-300'
}`}
>
{['info', 'promo'].map((type) => ( {['info', 'promo'].map((type) => (
<label key={type} className="flex items-center space-x-2"> <label key={type} className="flex items-center space-x-2">
<input <input
@ -293,6 +287,13 @@ const AddDialog = () => {
))} ))}
</div> </div>
</div> </div>
{errors.type && (
<div className="w-full">
<span className="text-red-500 text-xs mt-3 ml-[calc(30%+2rem)] block">
{errors.type}
</span>
</div>
)}
</div> </div>
<div className="w-full"> <div className="w-full">
@ -300,7 +301,11 @@ const AddDialog = () => {
<label className="form-label flex items-center gap-1 max-w-56"> <label className="form-label flex items-center gap-1 max-w-56">
Send Via<span className="text-red-500">*</span> Send Via<span className="text-red-500">*</span>
</label> </label>
<div className="flex gap-6 items-center"> <div
className={`flex gap-6 items-center border rounded-md px-3 py-1 ${
errors.via ? 'border-red-500' : 'border-gray-300'
}`}
>
<label className="flex items-center space-x-2"> <label className="flex items-center space-x-2">
<input <input
type="radio" type="radio"
@ -336,6 +341,13 @@ const AddDialog = () => {
</label> </label>
</div> </div>
</div> </div>
{errors.via && (
<div className="w-full">
<span className="text-red-500 text-xs mt-3 ml-[calc(30%+2rem)] block">
{errors.via}
</span>
</div>
)}
</div> </div>
<div className="w-full"> <div className="w-full">
@ -358,13 +370,20 @@ const AddDialog = () => {
Content<span className="text-red-500">*</span> Content<span className="text-red-500">*</span>
</label> </label>
<Textarea <Textarea
className="input col-span-6" className={`input col-span-6 ${errors.content ? 'border-red-500' : ''}`}
name="content" name="content"
placeholder="Enter Content Notification" placeholder="Enter Content Notification"
value={formField.content} value={formField.content}
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>
{errors.content && (
<div className="w-full">
<span className="text-red-500 text-xs mt-3 col-span-8 ml-[calc(30%+2rem)] block">
{errors.content}
</span>
</div>
)}
</div> </div>
<div className="flex justify-end gap-4"> <div className="flex justify-end gap-4">

View File

@ -0,0 +1,55 @@
import { toast } from 'sonner';
export interface selectedNotification {
customers: string[];
all_customer: string;
type: string;
via: string;
subject: string;
content: string;
id: string;
created_by: string;
created_at: string;
}
export const initialStateNotification: selectedNotification = {
customers: [],
all_customer: '',
type: '',
via: '',
subject: '',
content: '',
id: '',
created_by: '',
created_at: ''
};
export const validateFormNotification = (
formField: typeof initialStateNotification,
setErrors: React.Dispatch<React.SetStateAction<Record<string, string>>>
) => {
const requiredFields = [
{ key: 'all_customer', label: 'Send To' },
{ key: 'type', label: 'Type' },
{ key: 'via', label: 'Via' },
{ key: 'content', label: 'Content' }
];
const newErrors: Record<string, string> = {};
let isValid = true;
requiredFields.forEach(({ key, label }) => {
if (
formField[key as keyof typeof formField] === '' ||
formField[key as keyof typeof formField] === null ||
formField[key as keyof typeof formField] === undefined
) {
newErrors[key] = `${label} is required`;
toast.error(`${label} is required`);
isValid = false;
}
});
setErrors(newErrors);
return isValid;
};

View File

@ -7,17 +7,6 @@ import { ListToolBar } from '../blocks/ListToolbar';
import { useCallApi } from '@/hooks'; import { useCallApi } from '@/hooks';
import moment from 'moment'; import moment from 'moment';
interface SelectedNotification {
all_customers: string;
customer_name: string;
id: string;
content: string;
subject: string;
type: string;
via: string;
created_at: string;
}
interface ContextProps { interface ContextProps {
showAddDialog: boolean; showAddDialog: boolean;
handleAddDialog: (show: boolean) => void; handleAddDialog: (show: boolean) => void;
@ -197,4 +186,3 @@ const ManageNotifContextProvider = ({ children }: { children: React.ReactNode })
}; };
export { ManageNotifContext, ManageNotifContextProvider }; export { ManageNotifContext, ManageNotifContextProvider };
export type { SelectedNotification };