Added New User Registration Detail Modal

This commit is contained in:
Mitchell Magro 2025-10-28 16:42:25 +01:00
parent fe6ed86a76
commit be1df88e75
15 changed files with 385 additions and 210 deletions

View File

@ -1,9 +1,9 @@
import Users from "@/app/features/Pages/Admin/Users/users"; import Users from "@/app/features/Pages/Admin/Users/users";
export default async function BackOfficeUsersPage() { export default async function BackOfficeUsersPage() {
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL // const baseUrl = process.env.NEXT_PUBLIC_BASE_URL
? `${process.env.NEXT_PUBLIC_BASE_URL}` // ? `${process.env.NEXT_PUBLIC_BASE_URL}`
: "http://localhost:4000"; // : "http://localhost:4000";
// const res = await fetch(`${baseUrl}/api/dashboard/admin/users`, { // const res = await fetch(`${baseUrl}/api/dashboard/admin/users`, {
// cache: "no-store", // 👈 disables caching for SSR freshness // cache: "no-store", // 👈 disables caching for SSR freshness
// }); // });

View File

@ -3,9 +3,11 @@ import React, { useState } from "react";
import { Card, CardContent, Typography, Stack } from "@mui/material"; import { Card, CardContent, Typography, Stack } from "@mui/material";
import { IUser } from "./interfaces"; import { IUser } from "./interfaces";
import UserTopBar from "@/app/features/UserRoles/AddUser/AddUserButton"; import UserTopBar from "@/app/features/UserRoles/AddUser/AddUserButton";
import Modal from "@/app/components/Modal/Modal";
import UserRoleCard from "@/app/features/UserRoles/userRoleCard"; import UserRoleCard from "@/app/features/UserRoles/userRoleCard";
import AddUser from "@/app/features/UserRoles/AddUser/AddUser"; import AddUser from "@/app/features/UserRoles/AddUser/AddUser";
import { setSidebarOpen } from "@/app/redux/ui/uiSlice";
import { AppDispatch } from "@/app/redux/types";
import { useDispatch } from "react-redux";
interface UsersProps { interface UsersProps {
users: IUser[]; users: IUser[];
@ -13,37 +15,38 @@ interface UsersProps {
const Users: React.FC<UsersProps> = ({ users }) => { const Users: React.FC<UsersProps> = ({ users }) => {
const [showAddUser, setShowAddUser] = useState(false); const [showAddUser, setShowAddUser] = useState(false);
const dispatch = useDispatch<AppDispatch>();
return ( return (
<div> <div>
<UserTopBar onAddUser={() => setShowAddUser(true)} /> <UserTopBar
{users.map((user: IUser) => ( onAddUser={() => {
<Card key={user.id} sx={{ mb: 2 }}> setShowAddUser(true);
<CardContent> dispatch(setSidebarOpen(false));
<Typography variant="h6">{user.username}</Typography> }}
<Typography variant="body2"> />
Merchant ID: {user.merchantId} {users?.length > 0 &&
</Typography> users.map((user: IUser) => (
<Card key={user.id} sx={{ mb: 2 }}>
<CardContent>
<Typography variant="h6">{user.username}</Typography>
<Typography variant="body2">
Merchant ID: {user.merchantId}
</Typography>
<Stack direction="row" spacing={1} mt={1}> <Stack direction="row" spacing={1} mt={1}>
<UserRoleCard <UserRoleCard
user={user} user={user}
isAdmin={true} isAdmin={true}
lastLogin="small" lastLogin="small"
merchants={[]} // merchants={Numberuser.allowedMerchantIds} merchants={[]} // merchants={Numberuser.allowedMerchantIds}
/> />
</Stack> </Stack>
</CardContent> </CardContent>
</Card> </Card>
))} ))}
<Modal <AddUser open={showAddUser} onClose={() => setShowAddUser(false)} />
open={showAddUser}
onClose={() => setShowAddUser(false)}
title="Add User"
>
<AddUser />
</Modal>
</div> </div>
); );
}; };

View File

@ -11,18 +11,20 @@ import { COUNTRY_CODES } from "../constants";
import { formatPhoneDisplay, validatePhone } from "../utils"; import { formatPhoneDisplay, validatePhone } from "../utils";
import Spinner from "../../../components/Spinner/Spinner"; import Spinner from "../../../components/Spinner/Spinner";
import { RootState } from "@/app/redux/store"; import { RootState } from "@/app/redux/store";
import toast from "react-hot-toast";
import Modal from "@/app/components/Modal/Modal";
interface AddUserFormProps { interface AddUserProps {
onSuccess?: () => void; open: boolean;
onClose: () => void;
} }
const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => { const AddUser: React.FC<AddUserProps> = ({ open, onClose }) => {
const router = useRouter(); // const router = useRouter();
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const { status, error: authError } = useSelector( const { status, error: authError } = useSelector(
(state: RootState) => state.auth (state: RootState) => state.auth
); );
const [form, setForm] = useState<IEditUserForm>({ const [form, setForm] = useState<IEditUserForm>({
username: "", username: "",
firstName: "", firstName: "",
@ -108,9 +110,14 @@ const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
}; };
try { try {
await dispatch(addUser(formattedForm)); const resultAction = await dispatch(addUser(formattedForm));
if (onSuccess) onSuccess(); const result = addUser.fulfilled.match(resultAction);
router.refresh(); // <- refreshes the page (SSR re-runs)
if (result && resultAction.payload.success) {
toast.success(resultAction.payload.message);
// router.refresh();
onClose();
}
} catch (err) { } catch (err) {
// Error is handled by Redux state // Error is handled by Redux state
console.error("Failed to add user:", err); console.error("Failed to add user:", err);
@ -118,173 +125,175 @@ const AddUserForm: React.FC<AddUserFormProps> = ({ onSuccess }) => {
}; };
return ( return (
<form className="add-user" onSubmit={handleSubmit}> <Modal open={open} onClose={onClose} title="Add User">
<input <form className="add-user" onSubmit={handleSubmit}>
name="username" <input
placeholder="Username" name="username"
value={form.username} placeholder="Username"
onChange={handleChange} value={form.username}
required
/>
<input
name="firstName"
placeholder="First Name"
value={form.firstName}
onChange={handleChange}
required
/>
<input
name="lastName"
placeholder="Last Name"
value={form.lastName}
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} onChange={handleChange}
className="add-user__select" required
> />
<option value="">Select Merchant</option> <input
<option value="Win Bot">Win Bot</option> name="firstName"
<option value="Data Spin">Data Spin</option> placeholder="First Name"
</select> value={form.firstName}
{form.merchants.length > 0 && ( onChange={handleChange}
<div className="selected-items"> required
{form.merchants.map((merchant, index) => ( />
<span key={index} className="selected-item"> <input
{merchant} name="lastName"
<button placeholder="Last Name"
type="button" value={form.lastName}
onClick={() => { onChange={handleChange}
setForm(prev => ({ required
...prev, />
merchants: prev.merchants.filter((_, i) => i !== index), <input
})); name="email"
}} type="email"
className="remove-item" placeholder="Email"
> value={form.email}
× onChange={handleChange}
</button> required
</span> />
))} <div className="array-field-container">
</div> <label>Merchants:</label>
)} <select
</div> name="merchants"
value=""
onChange={handleChange}
className="add-user__select"
>
<option value="">Select Merchant</option>
<option value="Win Bot">Win Bot</option>
<option value="Data Spin">Data Spin</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"> <div className="array-field-container">
<label>Groups:</label> <label>Groups:</label>
<select
name="groups"
value=""
onChange={handleChange}
className="add-user__select"
>
<option value="">Select Group</option>
<option value="Admin">Admin</option>
<option value="Reader">Reader</option>
<option value="Manager">Manager</option>
<option value="User">User</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>
<select <select
name="groups" name="jobTitle"
value="" value={form.jobTitle}
onChange={handleChange} onChange={handleChange}
required
className="add-user__select" className="add-user__select"
> >
<option value="">Select Group</option> <option value="">Select Job Title</option>
<option value="Admin">Admin</option> <option value="Admin">Admin</option>
<option value="Reader">Reader</option> <option value="Reader">Reader</option>
<option value="Manager">Manager</option>
<option value="User">User</option> <option value="User">User</option>
<option value="Manager">Manager</option>
<option value="Supervisor">Supervisor</option>
<option value="Director">Director</option>
</select> </select>
{form.groups.length > 0 && ( <div className="phone-input-container">
<div className="selected-items"> <select
{form.groups.map((group, index) => ( name="countryCode"
<span key={index} className="selected-item"> value={countryCode}
{group} onChange={handleCountryCodeChange}
<button className="country-code-select"
type="button" >
onClick={() => { {COUNTRY_CODES.map(country => (
setForm(prev => ({ <option key={country.code} value={country.code}>
...prev, {country.flag} {country.code} {country.country}
groups: prev.groups.filter((_, i) => i !== index), </option>
}));
}}
className="remove-item"
>
×
</button>
</span>
))} ))}
</div> </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>
)} )}
</div> <span
<select className="phone-error-message"
name="jobTitle" style={{
value={form.jobTitle} visibility: phoneError ? "visible" : "hidden",
onChange={handleChange} color: "red",
required width: "100%",
className="add-user__select" }}
>
<option value="">Select Job Title</option>
<option value="Admin">Admin</option>
<option value="Reader">Reader</option>
<option value="User">User</option>
<option value="Manager">Manager</option>
<option value="Supervisor">Supervisor</option>
<option value="Director">Director</option>
</select>
<div className="phone-input-container">
<select
name="countryCode"
value={countryCode}
onChange={handleCountryCodeChange}
className="country-code-select"
> >
{COUNTRY_CODES.map(country => ( {phoneError}
<option key={country.code} value={country.code}> </span>
{country.flag} {country.code} {country.country}
</option>
))}
</select>
<input
name="phone"
type="tel"
placeholder="Phone number (optional)"
value={form.phone}
onChange={handleChange}
className={phoneError ? "phone-input-error" : ""}
/>
</div>
{authError && ( <div className="add-user__button-container">
<span style={{ color: "red", width: "100%" }}>{authError}</span> <button type="submit" disabled={loading}>
)} {loading ? (
<span <>
className="phone-error-message" <Spinner size="small" color="#fff" />
style={{ Adding...
visibility: phoneError ? "visible" : "hidden", </>
color: "red", ) : (
width: "100%", "Add User"
}} )}
> </button>
{phoneError} </div>
</span> </form>
</Modal>
<div className="add-user__button-container">
<button type="submit" disabled={loading}>
{loading ? (
<>
<Spinner size="small" color="#fff" />
Adding...
</>
) : (
"Add User"
)}
</button>
</div>
</form>
); );
}; };
export default AddUserForm; export default AddUser;

View File

@ -0,0 +1,63 @@
.new-user__content {
display: flex;
flex-direction: column;
gap: 1.5rem;
padding: 1rem 0;
.new-user__row {
display: flex;
flex-direction: column;
gap: 0.4rem;
.new-user__label {
font-weight: 600;
color: #444;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.new-user__value {
background: #f7f7f8;
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 0.75rem 1rem;
font-size: 0.95rem;
color: #222;
word-break: break-word;
code {
background: #272822;
color: #f8f8f2;
padding: 0.2rem 0.4rem;
border-radius: 4px;
font-family: "Source Code Pro", monospace;
font-size: 0.9rem;
}
}
}
}
/* Optional: Add a subtle hover highlight for readability */
.new-user__row:hover .new-user__value {
border-color: #bdbdbd;
background: #fafafa;
}
/* Responsive tweak for smaller screens */
@media (max-width: 600px) {
.new-user__content {
gap: 1rem;
}
.new-user__row {
.new-user__label {
font-size: 0.85rem;
}
.new-user__value {
font-size: 0.9rem;
padding: 0.6rem 0.8rem;
}
}
}

View File

@ -0,0 +1,43 @@
"use client";
import React from "react";
import Modal from "@/app/components/Modal/Modal";
import { useDispatch, useSelector } from "react-redux";
import { clearAddedUser } from "@/app/redux/auth/authSlice";
import { AppDispatch } from "@/app/redux/types";
import { useRouter } from "next/navigation";
import { selectAddedUser } from "@/app/redux/auth/selectors";
import { setShowNewUserModal } from "@/app/redux/ui/uiSlice";
import "./NewUser.scss";
const NewUser: React.FC = () => {
const dispatch = useDispatch<AppDispatch>();
const router = useRouter();
const user = useSelector(selectAddedUser);
const handleClose = () => {
dispatch(clearAddedUser());
dispatch(setShowNewUserModal(false));
// Refresh data after closing to update lists without racing rehydrate
router.refresh();
};
return (
<Modal open={true} onClose={handleClose} title="New user Details">
<div className="new-user__content">
<div className="new-user__row">
<label className="new-user__label">Email</label>
<div className="new-user__value">{user?.email}</div>
</div>
<div className="new-user__row">
<label className="new-user__label">Temporary Password</label>
<div className="new-user__value">
<code>{user?.password}</code>
</div>
</div>
</div>
</Modal>
);
};
export default NewUser;

View File

@ -1,6 +1,6 @@
import { RootState } from "@/app/redux/types";
import React from "react"; import React from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { RootState } from "../../redux/types";
interface MainContentProps { interface MainContentProps {
children: React.ReactNode; children: React.ReactNode;

View File

@ -5,6 +5,7 @@ import ReduxProvider from "./ReduxProvider"; // moved into app/
import { AuthBootstrap } from "./AuthBootstrap"; // moved into app/ import { AuthBootstrap } from "./AuthBootstrap"; // moved into app/
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
import "../styles/globals.scss"; import "../styles/globals.scss";
import Modals from "./modals";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Your App", title: "Your App",
@ -21,6 +22,7 @@ export default function RootLayout({
<body> <body>
<ReduxProvider> <ReduxProvider>
{/* Bootstraps session validation + redirect if invalid */} {/* Bootstraps session validation + redirect if invalid */}
<Modals />
<AuthBootstrap /> <AuthBootstrap />
<ThemeRegistry>{children}</ThemeRegistry> <ThemeRegistry>{children}</ThemeRegistry>
<Toaster position="top-right" reverseOrder={false} /> <Toaster position="top-right" reverseOrder={false} />

14
app/modals.tsx Normal file
View File

@ -0,0 +1,14 @@
"use client";
import React from "react";
import NewUser from "./features/UserRoles/NewUser/NewUser";
import { useSelector } from "react-redux";
// import { selectIsLoggedIn } from "./redux/auth/selectors";
import { selectShowNewUserModal } from "./redux/ui/selectors";
const Modals: React.FC = () => {
// const isLoggedIn = useSelector(selectIsLoggedIn);
const showNewUserModal = useSelector(selectShowNewUserModal);
return <>{showNewUserModal && <NewUser />}</>;
};
export default Modals;

View File

@ -18,6 +18,7 @@ interface AuthState {
user: IUserResponse | null; user: IUserResponse | null;
tokenInfo: TokenInfo | null; tokenInfo: TokenInfo | null;
mustChangePassword: boolean; mustChangePassword: boolean;
addedUser: IUserResponse | null;
} }
const initialState: AuthState = { const initialState: AuthState = {
@ -28,6 +29,7 @@ const initialState: AuthState = {
user: null, user: null,
tokenInfo: null, tokenInfo: null,
mustChangePassword: false, mustChangePassword: false,
addedUser: null,
}; };
// ---------------- Login ---------------- // ---------------- Login ----------------
@ -179,7 +181,7 @@ export const validateAuth = createAsyncThunk<
// ---------------- Add User ---------------- // ---------------- Add User ----------------
export const addUser = createAsyncThunk< export const addUser = createAsyncThunk<
ThunkSuccess<{ user: IUserResponse }>, ThunkSuccess<{ user: IUserResponse; success: boolean }>,
IEditUserForm, IEditUserForm,
{ rejectValue: ThunkError; state: RootState } { rejectValue: ThunkError; state: RootState }
>("auth/addUser", async (userData, { rejectWithValue, getState }) => { >("auth/addUser", async (userData, { rejectWithValue, getState }) => {
@ -195,14 +197,17 @@ export const addUser = createAsyncThunk<
const data = await res.json(); const data = await res.json();
console.log("[DEBUG] [ADD-USER] [data]: ", data);
if (!res.ok) { if (!res.ok) {
return rejectWithValue(data.message || "Failed to create user"); return rejectWithValue(data.message || "Failed to create user");
} }
return { return {
success: data.success,
message: data.message || "User created successfully", message: data.message || "User created successfully",
user: data.user, user: data.user,
}; } as ThunkSuccess<{ user: IUserResponse; success: boolean }>;
} catch (err: unknown) { } catch (err: unknown) {
return rejectWithValue( return rejectWithValue(
(err as Error).message || "Network error during user creation" (err as Error).message || "Network error during user creation"
@ -253,6 +258,9 @@ const authSlice = createSlice({
clearAuthMessage: state => { clearAuthMessage: state => {
state.authMessage = ""; state.authMessage = "";
}, },
clearAddedUser: state => {
state.addedUser = null;
},
}, },
extraReducers: builder => { extraReducers: builder => {
builder builder
@ -277,12 +285,11 @@ const authSlice = createSlice({
}) })
// Logout // Logout
.addCase(logout.fulfilled, (state, action) => { .addCase(logout.fulfilled, (state, action) => {
state.status = "succeeded"; return {
state.isLoggedIn = false; ...initialState,
state.authMessage = action.payload.message; status: "succeeded",
state.user = null; authMessage: action.payload.message,
state.tokenInfo = null; };
state.mustChangePassword = false;
}) })
.addCase(logout.rejected, (state, action) => { .addCase(logout.rejected, (state, action) => {
state.status = "failed"; state.status = "failed";
@ -291,12 +298,12 @@ const authSlice = createSlice({
}) })
// Auto Logout // Auto Logout
.addCase(autoLogout.fulfilled, (state, action) => { .addCase(autoLogout.fulfilled, (state, action) => {
state.status = "succeeded"; return {
state.isLoggedIn = false; ...initialState,
state.authMessage = action.payload.message; status: "succeeded",
state.user = null; isLoggedIn: false,
state.tokenInfo = null; authMessage: action.payload.message,
state.mustChangePassword = false; };
}) })
.addCase(autoLogout.rejected, (state, action) => { .addCase(autoLogout.rejected, (state, action) => {
state.status = "failed"; state.status = "failed";
@ -336,15 +343,18 @@ const authSlice = createSlice({
.addCase(addUser.pending, state => { .addCase(addUser.pending, state => {
state.status = "loading"; state.status = "loading";
state.authMessage = "Creating user..."; state.authMessage = "Creating user...";
state.addedUser = null;
}) })
.addCase(addUser.fulfilled, (state, action) => { .addCase(addUser.fulfilled, (state, action) => {
state.status = "succeeded"; state.status = "succeeded";
state.authMessage = action.payload.message; state.authMessage = action.payload.message;
state.addedUser = action.payload.user;
}) })
.addCase(addUser.rejected, (state, action) => { .addCase(addUser.rejected, (state, action) => {
state.status = "failed"; state.status = "failed";
state.error = action.payload as string; state.error = action.payload as string;
state.authMessage = action.payload as string; state.authMessage = action.payload as string;
state.addedUser = null;
}) })
// Update User Details // Update User Details
.addCase(updateUserDetails.pending, state => { .addCase(updateUserDetails.pending, state => {
@ -364,5 +374,6 @@ const authSlice = createSlice({
}, },
}); });
export const { setAuthMessage, clearAuthMessage } = authSlice.actions; export const { setAuthMessage, clearAuthMessage, clearAddedUser } =
authSlice.actions;
export default authSlice.reducer; export default authSlice.reducer;

View File

@ -12,3 +12,4 @@ export const selectTimeUntilExpiration = (state: RootState) =>
state.auth?.tokenInfo?.timeUntilExpiration || 0; state.auth?.tokenInfo?.timeUntilExpiration || 0;
export const selectExpiresInHours = (state: RootState) => export const selectExpiresInHours = (state: RootState) =>
state.auth?.tokenInfo?.expiresInHours || 0; state.auth?.tokenInfo?.expiresInHours || 0;
export const selectAddedUser = (state: RootState) => state.auth?.addedUser;

View File

@ -11,6 +11,7 @@ import authReducer from "./auth/authSlice";
import uiReducer from "./ui/uiSlice"; import uiReducer from "./ui/uiSlice";
import userEpics from "./user/epic"; import userEpics from "./user/epic";
import authEpics from "./auth/epic"; import authEpics from "./auth/epic";
import uiEpics from "./ui/epic";
type PersistedAuth = { user: unknown | null }; type PersistedAuth = { user: unknown | null };
@ -40,7 +41,7 @@ const rootReducer = combineReducers({
ui: uiReducer, ui: uiReducer,
}); });
const rootEpic = combineEpics(...userEpics, ...authEpics); const rootEpic = combineEpics(...userEpics, ...authEpics, ...uiEpics);
const persistedReducer = persistReducer(persistConfig, rootReducer as Reducer); const persistedReducer = persistReducer(persistConfig, rootReducer as Reducer);

View File

@ -8,6 +8,7 @@ export type ThunkSuccess<T extends object = object> = { message: string } & T;
export type ThunkError = string; export type ThunkError = string;
export interface IUserResponse { export interface IUserResponse {
success: boolean;
token: string; token: string;
id: string; id: string;
name: string | null; name: string | null;
@ -19,6 +20,7 @@ export interface IUserResponse {
phone?: string | null; phone?: string | null;
jobTitle?: string | null; jobTitle?: string | null;
enabled?: boolean; enabled?: boolean;
password?: string;
authorities?: string[]; authorities?: string[];
allowedMerchantIds?: number[]; allowedMerchantIds?: number[];
created?: string; created?: string;
@ -40,5 +42,5 @@ export interface ILoginResponse {
user: IUserResponse; user: IUserResponse;
success: boolean; success: boolean;
message: string; message: string;
must_change_password: boolean; must_change_password?: boolean;
} }

16
app/redux/ui/epic.ts Normal file
View File

@ -0,0 +1,16 @@
import { Epic } from "redux-observable";
import { filter, map } from "rxjs/operators";
import { addUser } from "../auth/authSlice";
import { setShowNewUserModal } from "./uiSlice";
export const logoutRedirectEpic: Epic = action$ =>
action$.pipe(
filter(addUser.fulfilled.match),
map(() => {
return setShowNewUserModal(true);
})
);
const authEpics = [logoutRedirectEpic];
export default authEpics;

View File

@ -0,0 +1,4 @@
import { RootState } from "../types";
export const selectShowNewUserModal = (state: RootState) =>
state.ui.showNewUserModal;

View File

@ -2,10 +2,12 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface UIState { interface UIState {
sidebarOpen: boolean; sidebarOpen: boolean;
showNewUserModal: boolean;
} }
const initialState: UIState = { const initialState: UIState = {
sidebarOpen: true, sidebarOpen: true,
showNewUserModal: false,
}; };
const uiSlice = createSlice({ const uiSlice = createSlice({
@ -18,8 +20,12 @@ const uiSlice = createSlice({
setSidebarOpen: (state, action: PayloadAction<boolean>) => { setSidebarOpen: (state, action: PayloadAction<boolean>) => {
state.sidebarOpen = action.payload; state.sidebarOpen = action.payload;
}, },
setShowNewUserModal: (state, action: PayloadAction<boolean>) => {
state.showNewUserModal = action.payload;
},
}, },
}); });
export const { toggleSidebar, setSidebarOpen } = uiSlice.actions; export const { toggleSidebar, setSidebarOpen, setShowNewUserModal } =
uiSlice.actions;
export default uiSlice.reducer; export default uiSlice.reducer;