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

View File

@ -27,6 +27,7 @@ import { useManageNotificationContext } from '../hooks/useManageNotificationCont
import { doSaveLogActivity } from '@/actions/GlobalActions';
import { CustomerProps } from '@/pages/master/provider/blocks/AddDialog';
import { Check, ChevronDown } from 'lucide-react';
import { initialStateNotification, selectedNotification, validateFormNotification } from './Types';
const API_URL_CUSTOMER = apiConfig.service_customer;
const API_URL_NOTIFICATION = apiConfig.service_notification;
@ -37,32 +38,34 @@ const AddDialog = () => {
const { GetData, PostData } = useCallApi();
const [alert, setAlert] = useState({ show: false, message: '' });
const isSubmittingRef = useRef(false);
const [errors, setErrors] = useState<Record<string, string>>({});
const initialState = {
customers: [] as string[],
all_customer: '',
type: '',
via: '',
subject: '',
content: ''
};
const [formField, setFormField] = useState(initialState);
const [formField, setFormField] = useState<selectedNotification>(initialStateNotification);
const [open, setOpen] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [customers, setCustomers] = useState<CustomerProps[]>([]);
const resetForm = () => {
setFormField({ ...initialState });
setFormField({ ...initialStateNotification });
setIsSubmitting(false);
setAlert({ show: false, message: '' });
setErrors({});
};
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: [] }));
}
if (errors[name]) {
setErrors((prevErrors) => {
const updatedErrors = { ...prevErrors };
delete updatedErrors[name];
return updatedErrors;
});
}
};
const doCreateNotification = useCallback(
@ -120,32 +123,8 @@ const AddDialog = () => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
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);
if (!validateFormNotification(formField, setErrors)) return;
doCreateNotification(e);
// console.log(formField);
setAlert({ show: false, message: '' });
};
useEffect(() => {
@ -179,7 +158,11 @@ const AddDialog = () => {
<label className="form-label flex items-center gap-1 max-w-56">
Send To<span className="text-red-500">*</span>
</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">
<input
type="radio"
@ -204,6 +187,13 @@ const AddDialog = () => {
</label>
</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 className="w-full">
@ -277,7 +267,11 @@ const AddDialog = () => {
<label className="form-label flex items-center gap-1 max-w-56">
Type<span className="text-red-500">*</span>
</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) => (
<label key={type} className="flex items-center space-x-2">
<input
@ -293,6 +287,13 @@ const AddDialog = () => {
))}
</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 className="w-full">
@ -300,7 +301,11 @@ const AddDialog = () => {
<label className="form-label flex items-center gap-1 max-w-56">
Send Via<span className="text-red-500">*</span>
</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">
<input
type="radio"
@ -336,6 +341,13 @@ const AddDialog = () => {
</label>
</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 className="w-full">
@ -358,13 +370,20 @@ const AddDialog = () => {
Content<span className="text-red-500">*</span>
</label>
<Textarea
className="input col-span-6"
className={`input col-span-6 ${errors.content ? 'border-red-500' : ''}`}
name="content"
placeholder="Enter Content Notification"
value={formField.content}
onChange={handleChange}
/>
</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 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 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 {
showAddDialog: boolean;
handleAddDialog: (show: boolean) => void;
@ -197,4 +186,3 @@ const ManageNotifContextProvider = ({ children }: { children: React.ReactNode })
};
export { ManageNotifContext, ManageNotifContextProvider };
export type { SelectedNotification };