2025-11-25 10:50:27 +01:00

312 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useState } from "react";
import { RootState } from "@/app/redux/store";
import toast from "react-hot-toast";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "@/app/redux/store";
import { addUser } from "@/app/redux/auth/authSlice";
import { IEditUserForm } from "../User.interfaces";
import { formatPhoneDisplay, validatePhone } from "../utils";
import Spinner from "../../../components/Spinner/Spinner";
import Modal from "@/app/components/Modal/Modal";
import {
selectAppMetadata,
selectPhoneNumberCountries,
} from "@/app/redux/metadata/selectors";
import "./AddUser.scss";
interface AddUserProps {
open: boolean;
onClose: () => void;
}
const AddUser: React.FC<AddUserProps> = ({ open, onClose }) => {
const dispatch = useDispatch<AppDispatch>();
const { status, error: authError } = useSelector(
(state: RootState) => state.auth
);
const [form, setForm] = useState<IEditUserForm>({
username: "",
first_name: "",
last_name: "",
email: "",
phone: "",
merchants: [],
groups: [],
job_title: "",
});
const [phoneError, setPhoneError] = useState("");
const [countryCode, setCountryCode] = useState("+1");
const data = useSelector(selectAppMetadata);
const { merchants = [], groups = [], job_titles = [] } = data || {};
const loading = status === "loading";
const COUNTRY_CODES = useSelector(selectPhoneNumberCountries);
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
) => {
const { name, value } = e.target;
if (name === "countryCode") {
setCountryCode(value);
return;
}
// Handle array fields (merchants and groups)
if (name === "merchants" || name === "groups") {
if (value === "") {
// If empty selection, set empty array
setForm(prev => ({ ...prev, [name]: [] }));
} else {
// Add the selected value to the array if not already present
setForm(prev => {
const currentArray = prev[name as keyof IEditUserForm] as string[];
if (!currentArray.includes(value)) {
return { ...prev, [name]: [...currentArray, value] };
}
return prev;
});
}
} else {
// Handle single value fields
setForm(prev => ({ ...prev, [name]: value }));
}
};
const handleCountryCodeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const newCountryCode = e.target.value;
setCountryCode(newCountryCode);
// Re-validate phone if it exists
if (form.phone) {
const phoneError = validatePhone(form.phone, newCountryCode);
setPhoneError(phoneError);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Validate phone number if provided
if (form.phone && phoneError) {
return;
}
if (
!form.first_name ||
!form.last_name ||
!form.email ||
form.merchants.length === 0 ||
form.groups.length === 0 ||
!form.job_title
) {
return;
}
// Format phone number with country code before submission
const formattedForm = {
...form,
phone: form.phone ? formatPhoneDisplay(form.phone, countryCode) : "",
};
try {
const resultAction = await dispatch(addUser(formattedForm));
const result = addUser.fulfilled.match(resultAction);
if (result && resultAction.payload.success) {
toast.success(resultAction.payload.message);
onClose();
}
} catch (err) {
// Error is handled by Redux state
console.error("Failed to add user:", err);
}
};
return (
<Modal open={open} onClose={onClose} title="Add User">
<form className="add-user" onSubmit={handleSubmit}>
<input
name="username"
placeholder="Username"
value={form.username}
onChange={handleChange}
required
/>
<input
name="first_name"
placeholder="First Name"
value={form.first_name}
onChange={handleChange}
required
/>
<input
name="last_name"
placeholder="Last Name"
value={form.last_name}
onChange={handleChange}
required
/>
<input
name="email"
type="email"
placeholder="Email"
value={form.email}
onChange={handleChange}
required
/>
<div className="array-field-container">
<label>Merchants:</label>
<select
name="merchants"
value=""
onChange={handleChange}
className="add-user__select"
>
<option value="">Select Merchant</option>
{merchants.map((merchant: string) => (
<option key={merchant} value={merchant}>
{merchant}
</option>
))}
</select>
{form.merchants.length > 0 && (
<div className="selected-items">
{form.merchants.map((merchant, index) => (
<span key={index} className="selected-item">
{merchant}
<button
type="button"
onClick={() => {
setForm(prev => ({
...prev,
merchants: prev.merchants.filter((_, i) => i !== index),
}));
}}
className="remove-item"
>
×
</button>
</span>
))}
</div>
)}
</div>
<div className="array-field-container">
<label>Groups:</label>
<select
name="groups"
value=""
onChange={handleChange}
className="add-user__select"
>
<option value="">Select Group</option>
{groups.map((group: string) => (
<option key={group} value={group}>
{group}
</option>
))}
</select>
{form.groups.length > 0 && (
<div className="selected-items">
{form.groups.map((group, index) => (
<span key={index} className="selected-item">
{group}
<button
type="button"
onClick={() => {
setForm(prev => ({
...prev,
groups: prev.groups.filter((_, i) => i !== index),
}));
}}
className="remove-item"
>
×
</button>
</span>
))}
</div>
)}
</div>
<div className="array-field-container">
<label>Job Title:</label>
<select
name="job_title"
value={form.job_title}
onChange={handleChange}
required
className="add-user__select"
>
<option value="">Select Job Title</option>
{job_titles?.map((job_title: string) => (
<option key={job_title} value={job_title}>
{job_title}
</option>
))}
</select>
</div>
<div className="phone-input-container">
<select
name="countryCode"
value={countryCode}
onChange={handleCountryCodeChange}
className="country-code-select"
>
{COUNTRY_CODES.map((country, i) => (
<option
key={`${country.code}-${country.name} ${i}`}
value={country.code}
>
{country.flag} {country.code} {country.name}
</option>
))}
</select>
<input
name="phone"
type="tel"
placeholder="Phone number (optional)"
value={form.phone}
onChange={handleChange}
className={phoneError ? "phone-input-error" : ""}
/>
</div>
{authError && (
<span style={{ color: "red", width: "100%" }}>{authError}</span>
)}
<span
className="phone-error-message"
style={{
visibility: phoneError ? "visible" : "hidden",
color: "red",
width: "100%",
}}
>
{phoneError}
</span>
<div className="add-user__button-container">
<button type="submit" disabled={loading}>
{loading ? (
<>
<Spinner size="small" color="#fff" />
Adding...
</>
) : (
"Add User"
)}
</button>
</div>
</form>
</Modal>
);
};
export default AddUser;