Fixed creator bug when adding new user
This commit is contained in:
parent
483f2656da
commit
eff7f62299
@ -30,6 +30,7 @@ interface FrontendRegisterForm {
|
||||
groups?: string[];
|
||||
merchants?: string[];
|
||||
creator?: string;
|
||||
countryCode: string;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
min-width: 320px;
|
||||
max-width: 60vw;
|
||||
max-width: 65vw;
|
||||
max-height: 90vh;
|
||||
overflow: auto;
|
||||
padding: 2rem 1.5rem 1.5rem 1.5rem;
|
||||
|
||||
48
app/components/Spinner/Spinner.scss
Normal file
48
app/components/Spinner/Spinner.scss
Normal file
@ -0,0 +1,48 @@
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid #1976d2;
|
||||
animation: spin 1s linear infinite;
|
||||
|
||||
&--small {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
&--medium {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
&--large {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
&__inner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 60%;
|
||||
height: 60%;
|
||||
border-radius: 50%;
|
||||
border: 1px solid transparent;
|
||||
border-top: 1px solid currentColor;
|
||||
animation: spin 0.8s linear infinite reverse;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
23
app/components/Spinner/Spinner.tsx
Normal file
23
app/components/Spinner/Spinner.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import "./Spinner.scss";
|
||||
|
||||
interface SpinnerProps {
|
||||
size?: "small" | "medium" | "large";
|
||||
color?: string;
|
||||
}
|
||||
|
||||
const Spinner: React.FC<SpinnerProps> = ({
|
||||
size = "medium",
|
||||
color = "#1976d2",
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={`spinner spinner--${size}`}
|
||||
style={{ borderTopColor: color }}
|
||||
>
|
||||
<div className="spinner__inner"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Spinner;
|
||||
@ -67,6 +67,15 @@
|
||||
border-radius: 4px;
|
||||
width: 100px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
button:first-child {
|
||||
|
||||
@ -2,11 +2,15 @@
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppDispatch } from "@/app/redux/store";
|
||||
import "./AddUser.scss";
|
||||
import { addUser } from "@/services/roles.services";
|
||||
import { addUser } from "@/app/redux/auth/authSlice";
|
||||
import { IEditUserForm } from "../User.interfaces";
|
||||
import { COUNTRY_CODES } from "../constants";
|
||||
import { formatPhoneDisplay, validatePhone } from "../utils";
|
||||
import Spinner from "../../../components/Spinner/Spinner";
|
||||
import { RootState } from "@/app/redux/store";
|
||||
|
||||
interface AddUserFormProps {
|
||||
onSuccess?: () => void;
|
||||
@ -14,6 +18,11 @@ interface AddUserFormProps {
|
||||
|
||||
const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
|
||||
const router = useRouter();
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const { status, error: authError } = useSelector(
|
||||
(state: RootState) => state.auth
|
||||
);
|
||||
|
||||
const [form, setForm] = useState<IEditUserForm>({
|
||||
username: "",
|
||||
firstName: "",
|
||||
@ -26,11 +35,11 @@ const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
|
||||
jobTitle: "",
|
||||
});
|
||||
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [phoneError, setPhoneError] = useState("");
|
||||
const [countryCode, setCountryCode] = useState("+1");
|
||||
|
||||
const loading = status === "loading";
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
@ -78,7 +87,6 @@ const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
|
||||
|
||||
// Validate phone number if provided
|
||||
if (form.phone && phoneError) {
|
||||
setError("Please fix phone number errors before submitting.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -90,28 +98,22 @@ const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
|
||||
form.groups.length === 0 ||
|
||||
!form.jobTitle
|
||||
) {
|
||||
setError("Please fill in all required fields.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
|
||||
// Format phone number with country code before submission
|
||||
const formattedForm = {
|
||||
...form,
|
||||
phone: form.phone ? formatPhoneDisplay(form.phone, countryCode) : "",
|
||||
};
|
||||
|
||||
await addUser(formattedForm);
|
||||
try {
|
||||
await dispatch(addUser(formattedForm));
|
||||
if (onSuccess) onSuccess();
|
||||
router.refresh(); // <- refreshes the page (SSR re-runs)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Something went wrong.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
// Error is handled by Redux state
|
||||
console.error("Failed to add user:", err);
|
||||
}
|
||||
};
|
||||
|
||||
@ -255,7 +257,9 @@ const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && <span style={{ color: "red", width: "100%" }}>{error}</span>}
|
||||
{authError && (
|
||||
<span style={{ color: "red", width: "100%" }}>{authError}</span>
|
||||
)}
|
||||
<span
|
||||
className="phone-error-message"
|
||||
style={{
|
||||
@ -269,7 +273,14 @@ const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
|
||||
|
||||
<div className="add-user__button-container">
|
||||
<button type="submit" disabled={loading}>
|
||||
{loading ? "Adding..." : "Add User"}
|
||||
{loading ? (
|
||||
<>
|
||||
<Spinner size="small" color="#fff" />
|
||||
Adding...
|
||||
</>
|
||||
) : (
|
||||
"Add User"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { ThunkSuccess, ThunkError, IUserResponse } from "../types";
|
||||
import { IEditUserForm } from "../../features/UserRoles/User.interfaces";
|
||||
import { RootState } from "../store";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
interface TokenInfo {
|
||||
@ -175,6 +177,39 @@ export const validateAuth = createAsyncThunk<
|
||||
|
||||
// TODO - Creaye a new thunk to update the user stuff
|
||||
|
||||
// ---------------- Add User ----------------
|
||||
export const addUser = createAsyncThunk<
|
||||
ThunkSuccess<{ user: IUserResponse }>,
|
||||
IEditUserForm,
|
||||
{ rejectValue: ThunkError; state: RootState }
|
||||
>("auth/addUser", async (userData, { rejectWithValue, getState }) => {
|
||||
try {
|
||||
const state = getState();
|
||||
const currentUserId = state.auth.user?.id;
|
||||
console.log("[DEBUG] [ADD-USER] [currentUserId]: ", currentUserId);
|
||||
const res = await fetch("/api/auth/register", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ ...userData, creator: currentUserId || "" }),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
return rejectWithValue(data.message || "Failed to create user");
|
||||
}
|
||||
|
||||
return {
|
||||
message: data.message || "User created successfully",
|
||||
user: data.user,
|
||||
};
|
||||
} catch (err: unknown) {
|
||||
return rejectWithValue(
|
||||
(err as Error).message || "Network error during user creation"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------- Update User Details ----------------
|
||||
export const updateUserDetails = createAsyncThunk<
|
||||
ThunkSuccess<{ user: IUserResponse }>,
|
||||
@ -297,6 +332,20 @@ const authSlice = createSlice({
|
||||
state.tokenInfo = null;
|
||||
state.error = action.payload as string;
|
||||
})
|
||||
// Add User
|
||||
.addCase(addUser.pending, state => {
|
||||
state.status = "loading";
|
||||
state.authMessage = "Creating user...";
|
||||
})
|
||||
.addCase(addUser.fulfilled, (state, action) => {
|
||||
state.status = "succeeded";
|
||||
state.authMessage = action.payload.message;
|
||||
})
|
||||
.addCase(addUser.rejected, (state, action) => {
|
||||
state.status = "failed";
|
||||
state.error = action.payload as string;
|
||||
state.authMessage = action.payload as string;
|
||||
})
|
||||
// Update User Details
|
||||
.addCase(updateUserDetails.pending, state => {
|
||||
state.status = "loading";
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { IEditUserForm } from "@/app/features/UserRoles/User.interfaces";
|
||||
|
||||
export async function addUser(data: IEditUserForm) {
|
||||
const res = await fetch("/api/auth/register", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
// export async function addUser(data: IEditUserForm) {
|
||||
// const res = await fetch("/api/auth/register", {
|
||||
// method: "POST",
|
||||
// headers: { "Content-Type": "application/json" },
|
||||
// body: JSON.stringify(data),
|
||||
// });
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Failed to create role");
|
||||
}
|
||||
// if (!res.ok) {
|
||||
// throw new Error("Failed to create role");
|
||||
// }
|
||||
|
||||
return res.json(); // or return type depending on your backend
|
||||
}
|
||||
// return res.json(); // or return type depending on your backend
|
||||
// }
|
||||
export async function editUser(id: string, data: IEditUserForm) {
|
||||
console.log("[editUser] - id", id, data);
|
||||
const res = await fetch(`/api/dashboard/admin/users/${id}`, {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user