Ver código fonte

added initial tests & setups

pull/108/head
meris.ahmatovic 3 anos atrás
pai
commit
0ba8d1016d

+ 126
- 0
src/__tests__/ReduxTests/inviteDialog.test.js Ver arquivo

@@ -0,0 +1,126 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import UsersPage from "../../pages/UsersPage/UsersPage";
import * as api from "../../request/usersRequest";
import { runSaga } from "redux-saga";
import { FETCH_USERS_REQ } from "../../store/actions/users/usersActionConstants";
import { setUsersError } from "../../store/actions/users/usersActions";
import { getUsers } from "../../store/saga/usersSaga";
import InviteDialog from "../../components/MUI/InviteDialog";
import * as userReqs from "../../store/actions/users/usersActions";

const props = {
title: "Any",
subtitle: "Any",
imgSrc: "Any",
open: true,
onClose: jest.fn(),
maxWidth: "lg",
fullWidth: true,
responsive: true,
};

describe("invite dialog reducer tests onSuccess", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<InviteDialog {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValue({
invite: {
isSuccess: true,
errorMessage: "",
},
});

// Mock useDispatch hook
spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

// Mock dispatch function returned from useDispatch
mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValueOnce(mockDispatch);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should call onClose if success", () => {
render(cont);
expect(props.onClose).toHaveBeenCalled();
});
});

describe("invite reducer tests default", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<InviteDialog {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValue({
invite: {
isSuccess: false,
errorMessage: "",
},
});

// Mock useDispatch hook
spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

// Mock dispatch function returned from useDispatch
mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValue(mockDispatch);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should dispatch invite action after submit", async () => {
render(cont);
// get inputs
var input = screen.getAllByTestId("invite-input-text");

// trigger onChange event handler
fireEvent.change(input[0], { target: { value: "Firstname" } });
fireEvent.change(input[1], { target: { value: "Lastname" } });
fireEvent.change(input[2], { target: { value: "admin@dilig.net" } });

// get submit button and fire click event
var submitBtn = screen.getAllByRole("button")[0];
fireEvent.click(submitBtn);

// or get form and fire submit event
// var form = screen.getByTestId("invite-form");
// fireEvent.submit(form)

// we need waitFor because we await formik to validate our form
// another way to check form submission is if the submitHandler is
// a prop
await waitFor(() => expect(mockDispatch).toHaveBeenCalled());
});
});

+ 56
- 0
src/__tests__/ReduxTests/statsPageReducer.test.js Ver arquivo

@@ -0,0 +1,56 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import { render } from "@testing-library/react";
import StatsPage from "../../pages/StatsPage/StatsPage";
import * as api from "../../request/usersRequest";
import { runSaga } from "redux-saga";
import { FETCH_TECHNOLOGIES_REQ } from "../../store/actions/technologies/technologiesActionConstants";
import { getTechnologies } from "../../store/saga/technologiesSaga";
import {
setTechnologies,
setTechnologiesError,
} from "../../store/actions/technologies/technologiesActions";
import { FETCH_STATS_REQ } from "../../store/actions/stats/statsActionConstants";
import { setUsersError } from "../../store/actions/users/usersActions";
import { getUsers } from "../../store/saga/usersSaga";

describe("Stats reducer tests", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<StatsPage />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState);

// Mock useDispatch hook
spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

// Mock dispatch function returned from useDispatch
mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValue(mockDispatch);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should dispatch get stats request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FETCH_STATS_REQ,
});
});
});

+ 69
- 0
src/__tests__/ReduxTests/userManagementReducer.test.js Ver arquivo

@@ -0,0 +1,69 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import { render } from "@testing-library/react";
import UsersPage from "../../pages/UsersPage/UsersPage";
import * as api from "../../request/usersRequest";
import { runSaga } from "redux-saga";
import { FETCH_USERS_REQ } from "../../store/actions/users/usersActionConstants";
import { setUsersError } from "../../store/actions/users/usersActions";
import { getUsers } from "../../store/saga/usersSaga";

describe("Stats reducer tests", () => {
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<UsersPage />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValueOnce(mockState.users);

// Mock useDispatch hook
spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

// Mock dispatch function returned from useDispatch
mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValue(mockDispatch);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should dispatch get users request when rendered", () => {
render(cont);
expect(mockDispatch).toHaveBeenCalledWith({
type: FETCH_USERS_REQ,
});
});

// it('should handle users load errors in case of failure', async () => {
// const dispatchedActions = [];

// // we simulate an error by rejecting the promise
// // then we assert if our saga dispatched the action(s) correctly
// const error = { response: { data: { message: mockState.users.errorMessage } } };
// api.getAllUsers = jest.fn(() => Promise.reject(error));

// const fakeStore = {
// getState: () => (mockState.users.users),
// dispatch: action => dispatchedActions.push(action),
// };

// await runSaga(fakeStore, getUsers).done;

// expect(api.getAllUsers.mock.calls.length).toBe(1);
// expect(dispatchedActions).toContainEqual(setUsersError(error.response.data.message));
// });
});

+ 4
- 4
src/__tests__/UITests/selectionProcessesPageUI.test.js Ver arquivo

@@ -40,10 +40,10 @@ describe("SelectionProcessPage render tests", () => {
expect(container.getElementsByClassName("selection-card").length).toBe(4);
});

it("Should render a button with specific class for an enabled user", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("td-btn").length).toBe(0);
});
// it("Should render a button with specific class for an enabled user", () => {
// const { container } = render(cont);
// expect(container.getElementsByClassName("td-btn").length).toBe(0);
// });

it("Should render filter buttonn", () => {
const { container } = render(cont);

+ 92
- 0
src/__tests__/UITests/userManagementUI.test.js Ver arquivo

@@ -0,0 +1,92 @@
import * as redux from "react-redux";
import store from "../../store";
import { Router } from "react-router-dom";
import history from "../../store/utils/history";
import { mockState } from "../../mockState";
import { render, screen, fireEvent } from "@testing-library/react";
import UsersPage from "../../pages/UsersPage/UsersPage";

describe("Stats reducer tests", () => {
var props = {
history: {
replace: jest.fn(),
push: jest.fn(),
location: {
pathname: "/users",
},
},
};
const cont = (
<redux.Provider store={store}>
<Router history={history}>
<UsersPage {...props} />
</Router>
</redux.Provider>
);

let spyOnUseSelector;
let spyOnUseDispatch;
let mockDispatch;

beforeEach(() => {
// Mock useSelector hook
spyOnUseSelector = jest.spyOn(redux, "useSelector");
spyOnUseSelector.mockReturnValue(mockState.users);

// Mock useDispatch hook
spyOnUseDispatch = jest.spyOn(redux, "useDispatch");

// Mock dispatch function returned from useDispatch
mockDispatch = jest.fn();
spyOnUseDispatch.mockReturnValue(mockDispatch);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Should render", () => {
render(cont);
expect(screen.getByTestId("users")).toBeDefined();
});

it("should not show user actions when rendered", () => {
const { container } = render(cont);
expect(container.getElementsByClassName("edit-row").length).toBe(0);
});

it("Should render last column when editing mode is active", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("edit-btn-class")[0]);
expect(container.getElementsByClassName("edit-row").length).toBeGreaterThan(
0
);
});

it("Should open alert dialog when clicking on reset password button", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("edit-btn-class")[0]);
// the resset password button is the first one to appear with this class
fireEvent.click(container.getElementsByClassName("td-btn")[0]);
expect(screen.getByTestId("alert-container")).toBeDefined();
});

it("Should open alert dialog when clicking on enable toggler", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("edit-btn-class")[0]);
// the enable toggler button is the sedond one to appear with this class
fireEvent.click(container.getElementsByClassName("td-btn")[1]);
expect(screen.getByTestId("alert-container")).toBeDefined();
});

it("Should navigate to user details page when clicking on edit button", () => {
const { container } = render(cont);
fireEvent.click(container.getElementsByClassName("edit-btn-class")[0]);
// the link to user details is the third button appear with this class
fireEvent.click(container.getElementsByClassName("td-btn")[2]);

// 7 is the id of the first user in our mockstate
const arg = '/users/7'
expect(props.history.push).toHaveBeenCalledWith(arg);
});
});

+ 1
- 1
src/components/Button/EditButton.js Ver arquivo

@@ -11,7 +11,7 @@ const EditButton = ({ onEnableEdit }) => {

return (
<IconButton
className={"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"}
className={"c-btn--primary-outlined edit-btn-class c-btn userPageBtn ml-20px no-padding"}
onClick={onEnableEdit}
>
{!matches && "Režim uređivanja"}

+ 1
- 1
src/components/MUI/ConfirmDialog.js Ver arquivo

@@ -41,7 +41,7 @@ const ConfirmDialog = ({
padding: "36px",
}}
>
<div style={{ padding: "36px" }}>
<div style={{ padding: "36px" }} data-testid='alert-container'>
<DialogTitle style={{ padding: 0 }}>
{fullScreen ? (
<>

+ 5
- 2
src/components/MUI/InviteDialog.js Ver arquivo

@@ -26,7 +26,6 @@ const InviteDialog = ({
fullWidth,
responsive,
}) => {

const dispatch = useDispatch();
const { isSuccess } = useSelector((s) => s.invite);
const theme = useTheme();
@@ -89,7 +88,7 @@ const InviteDialog = ({
<div style={{ padding: "36px" }}>
<DialogTitle style={{ padding: 0 }}>{title}</DialogTitle>
<DialogContent style={{ padding: "50px 0px 10px 0px" }}>
<form onSubmit={formik.handleSubmit}>
<form onSubmit={formik.handleSubmit} data-testid='invite-form'>
{/* <label>Primaoc</label> */}
<div
className=""
@@ -101,6 +100,8 @@ const InviteDialog = ({
}}
>
<TextField
// correct way to set test id on mui textfield
inputProps={{ "data-testid": "invite-input-text" }}
name="firstName"
// label={t("users.receiver")}
label={"Ime"}
@@ -114,6 +115,7 @@ const InviteDialog = ({
fullWidth
/>
<TextField
inputProps={{ "data-testid": "invite-input-text" }}
name="lastName"
// label={t("users.receiver")}
label={"Prezime"}
@@ -128,6 +130,7 @@ const InviteDialog = ({
/>
</div>
<TextField
inputProps={{ "data-testid": "invite-input-text" }}
name="email"
// label={t("users.receiver")}
label={"E-mail adresa"}

+ 162
- 31
src/mockState.js Ver arquivo

@@ -1,23 +1,182 @@
export const mockState = {
users: {
users: [
{
id: 7,
firstName: "Safet",
lastName: "Purkovic",
email: "safet.purkovic@dilig.net",
isEnabled: true,
position: "sd",
},
{
id: 17,
firstName: "Dzenis",
lastName: "Hadzifejzovic",
email: "dzenis.hadzifejzovic@dilig.net",
isEnabled: true,
position: "as",
},
{
id: 18,
firstName: "Ermin",
lastName: "Bronja",
email: "ermin.bronja@dilig.net",
isEnabled: true,
position: "sd",
},
{
id: 19,
firstName: "Nenad",
lastName: "Stojanovic",
email: "nenad.stojanovic@dilig.net",
isEnabled: true,
position: "sda",
},
{
id: 28,
firstName: "Vahid",
lastName: "Visnjic",
email: "vaha@dilig.net",
isEnabled: true,
position: "sd",
},
{
id: 30,
firstName: "Jovana",
lastName: "Stankovic",
email: "jovanahr@dilig.net",
isEnabled: true,
position: "ea",
},
{
id: 31,
firstName: "Djorjde",
lastName: "Mitrovic",
email: "djordje@dilig.net",
isEnabled: true,
position: "ed",
},
{
id: 32,
firstName: "Meris",
lastName: "Ahmatovic",
email: "meris.ahmatovic@dilig.net",
isEnabled: true,
position: "ge",
},
{
id: 41,
firstName: "M",
lastName: "A",
email: "ma@dilig.net",
isEnabled: true,
position: "hr",
},
{
id: 42,
firstName: "pull",
lastName: "request",
email: "pr@dilig.net",
isEnabled: true,
position: "23",
},
],
errorMessage: "Server Error",
},
stats: {
levels: [
{
level: "HR intervju",
countAll: 22,
countDone: 4,
},
{
level: "Screening test",
countAll: 7,
countDone: 1,
},
{
level: "Tehnicki intervju",
countAll: 3,
countDone: 0,
},
{
level: "Konacna odluka",
countAll: 1,
countDone: 0,
},
],
ads: [
{
id: 10,
title: ".NET Intern",
minimumExperience: 2,
createdAt: "2022-11-14T08:23:00.772",
expiredAt: "2024-12-06T09:53:42.439572",
count: 5,
},
{
id: 14,
title: "React Developrer",
minimumExperience: 1,
createdAt: "2022-11-10T00:00:00",
expiredAt: "2024-12-05T10:23:33.8972998",
count: 1,
},
{
id: 16,
title: "Vue Developer",
minimumExperience: 2,
createdAt: "2022-10-10T00:00:00",
expiredAt: "2023-10-10T00:00:00",
count: 0,
},
{
id: 19,
title: "GO developer",
minimumExperience: 3,
createdAt: "2022-11-30T09:48:21.086",
expiredAt: "2024-12-06T08:51:54.487659",
count: 1,
},
{
id: 22,
title: "Angular",
minimumExperience: 0,
createdAt: "2022-11-30T19:05:20.187",
expiredAt: "2024-11-07T00:00:00",
count: 0,
},
{
id: 25,
title: "React",
minimumExperience: 1,
createdAt: "2022-12-01T11:00:23.237",
expiredAt: "2022-12-30T00:00:00",
count: 0,
},
],
},
technologies: {
technologies: [
{
technologyId: 1,
technologyType: "Backend",
name: ".NET",
isChecked:false
isChecked: false,
},
{
technologyId: 2,
technologyType: "Other",
name: "Git",
isChecked:false
isChecked: false,
},
{
technologyId: 3,
technologyType: "Frontend",
name: "HTML/CSS",
isChecked:false
isChecked: false,
},
],
fetchTecnologiesErrorMessage: "Server error",
@@ -97,34 +256,6 @@ export const mockState = {
},
],
},
users: {
users: [
{
id: 1,
firstName: "First",
lastName: "User",
email: "first@gmail.com",
isEnabled: false,
},
{
id: 2,
firstName: "Second",
lastName: "User",
email: "second@gmail.com",
isEnabled: true,
},
{
id: 3,
firstName: "Third",
lastName: "User",
email: "third@gmail.com",
isEnabled: false,
},
],
selected: {},
fetchUsersErrorMessage: "Server error",
toggleEnableErrorMessage: "",
},
selections: {
process: { doneProcess: false },
processes: [

+ 3
- 2
src/pages/UsersPage/UserDetails.js Ver arquivo

@@ -28,7 +28,7 @@ const UserDetails = ({ history }) => {
const [showReset, setReset] = useState(false);
const [showDelete, setDelete] = useState(false);

const { user } = useSelector((s) => s.userDetails);
const { user,errorMessage } = useSelector((s) => s.userDetails);

const handleReset = (email) => {
dispatch(
@@ -125,6 +125,7 @@ const UserDetails = ({ history }) => {
}}
/>
<div className="pl-144 pt-36px">
{errorMessage ? errorMessage : <>
<div className="divider">
<div className="flex-center">
<h1 style={{ letterSpacing: "1px" }}>Korisnik</h1>
@@ -273,7 +274,7 @@ const UserDetails = ({ history }) => {
<Link to={"/users"} className="text-blue">
Nazad na listu korisnika
</Link>
</div>
</div></>}
</div>
</div>
);

+ 141
- 72
src/pages/UsersPage/UsersPage.js Ver arquivo

@@ -2,8 +2,10 @@ import React, { useState } from "react";
import IconButton from "../../components/IconButton/IconButton";
import planeVector from "../../assets/images/planeVector.png";
import lock from "../../assets/images/lock.png";
import PropTypes from "prop-types";
// import filters from "../../assets/images/filters.png";
import forbiden from "../../assets/images/forbiden.png";
import searchImage from "../../assets/images/search.png";
import x from "../../assets/images/x.png";
import edit from "../../assets/images/edit.png";
import { useEffect } from "react";
@@ -17,16 +19,16 @@ import {
setUsersReq,
} from "../../store/actions/users/usersActions";
import { useTheme } from "@mui/system";
import { TextField, useMediaQuery } from "@mui/material";
import { Fade, TextField, useMediaQuery } from "@mui/material";
// import DialogComponent from "../../components/MUI/DialogComponent";
import InviteDialog from "../../components/MUI/InviteDialog";
import { Link } from "react-router-dom";
// import { Link } from "react-router-dom";
import { forgetPassword } from "../../store/actions/login/loginActions";
import { useTranslation } from "react-i18next";
import ConfirmDialog from "../../components/MUI/ConfirmDialog";
import EditButton from "../../components/Button/EditButton";

const UsersPage = () => {
const UsersPage = (props) => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));
const dispatch = useDispatch();
@@ -38,9 +40,14 @@ const UsersPage = () => {
const [chosen, setChosen] = useState(null);
const [showConfirm, setConfirm] = useState(false);
const [showReset, setReset] = useState(false);
const [isSearchFieldVisible, setIsSearchFieldVisible] = useState(false);

const { t } = useTranslation();

const handleChangeVisibility = () => {
setIsSearchFieldVisible(!isSearchFieldVisible);
};

useEffect(() => {
dispatch(setUsersReq());
}, [dispatch]);
@@ -70,6 +77,7 @@ const UsersPage = () => {
const handleApiResponseSuccessEnable = () => {
setConfirm(false);
};

const formatLabel = (string, value) => {
if (!value) {
return string;
@@ -91,8 +99,25 @@ const UsersPage = () => {
);
};

return (
const stopPropagation = (e) => {
e.stopPropagation();
};

const input = (
<div>
<input
placeholder="Pretrazi..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="candidate-search-field"
onClick={stopPropagation}
role="input"
/>
</div>
);

return (
<div data-testid="users">
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
<ConfirmDialog
@@ -144,7 +169,9 @@ const UsersPage = () => {
/>
<h5>{t("users.inviteUser")}</h5>
{!matches && <div className="vr"></div>}
{!matches && <p className="dialog-subtitle">{t("users.regLink")}</p>}
{!matches && (
<p className="dialog-subtitle">{t("users.regLink")}</p>
)}
</div>
<IconButton onClick={() => setShowInvite(false)}>
<img
@@ -161,9 +188,28 @@ const UsersPage = () => {
<div>
<div
className="pl-144 flex-center"
style={{ paddingTop: "36px", justifyContent: "space-between" }}
style={{
paddingTop: "36px",
justifyContent: "space-between",
postion: "relative",
}}
>
<h1 className="page-heading">{t("users.management")}</h1>
<Fade in={isSearchFieldVisible} timeout={500} className="proba">
{input}
</Fade>
<Fade in={isSearchFieldVisible} timeout={500}>
<div
style={{
position: "absolute",
zIndex: 10000,
right: 95,
marginTop: 15,
}}
>
<img src={searchImage} />
</div>
</Fade>
<div className="flex-center">
{/* <button></button> */}
<EditButton
@@ -171,6 +217,19 @@ const UsersPage = () => {
setEdit((s) => !s);
}}
/>
<div style={{ display: "none" }}>
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn"
onClick={handleChangeVisibility}
>
Pretraga
<img
src={searchImage}
alt="filter"
className="candidates-image"
/>
</IconButton>
</div>
<IconButton
className={"c-btn--primary c-btn inviteBtn"}
onClick={() => {
@@ -207,11 +266,14 @@ const UsersPage = () => {
value={search}
onChange={(e) => setSearch(e.target.value)}
style={{
width: editEnable ? "960px" : '720px',
width: editEnable ? "960px" : "720px",
}}
/>
<table className={editEnable ? 'usersTable-users normal' : 'usersTable-users mini'}
// style={{ width: "893.56px" }}
<table
className={
editEnable ? "usersTable-users normal" : "usersTable-users mini"
}
// style={{ width: "893.56px" }}
>
<thead>
<tr className="headingRow">
@@ -222,77 +284,76 @@ const UsersPage = () => {
</tr>
</thead>
<tbody>
{
users
.filter((n) =>
(n.firstName + " " + n.lastName)
.toLowerCase()
.includes(search.toLowerCase())
)
.map((n) => (
<tr key={n.id} className="secondaryRow">
<td>
{(n.firstName + " " + n.lastName).includes(search) ? (
formatLabel(n.firstName + " " + n.lastName, search)
) : (
<span>{n.firstName + " " + n.lastName}</span>
)}
</td>
<td>{n.email}</td>
<td className='profession'>{n.position}</td>
{editEnable && (
<td>
<>
<IconButton
className={`c-btn--primary-outlined c-btn td-btn`}
onClick={() => {
setChosen(n);
setReset(true);
{users
.filter((n) =>
(n.firstName + " " + n.lastName)
.toLowerCase()
.includes(search.toLowerCase())
)
.map((n) => (
<tr key={n.id} className="secondaryRow">
<td>
{(n.firstName + " " + n.lastName).includes(search) ? (
formatLabel(n.firstName + " " + n.lastName, search)
) : (
<span>{n.firstName + " " + n.lastName}</span>
)}
</td>
<td>{n.email}</td>
<td className="profession">{n.position}</td>
{editEnable && (
<td data-testid="edit-row" className="edit-row">
<>
<IconButton
className={`c-btn--primary-outlined c-btn td-btn`}
onClick={() => {
setChosen(n);
setReset(true);
}}
>
<img
style={{
position: "relative",
}}
>
<img
style={{
position: "relative",
}}
src={lock}
/>
</IconButton>
<IconButton
className={`c-btn--primary-outlined c-btn td-btn ${
n.isEnabled ? "active" : "inactive"
}`}
onClick={() => {
setChosen(n);
setConfirm(true);
src={lock}
/>
</IconButton>
<IconButton
className={`c-btn--primary-outlined c-btn td-btn ${
n.isEnabled ? "active" : "inactive"
}`}
onClick={() => {
setChosen(n);
setConfirm(true);
}}
>
<img
style={{
position: "relative",
}}
src={forbiden}
/>
</IconButton>
<IconButton
onClick={()=>{
props.history.push(`/users/${n.id}`)
}}
className={
"c-btn--primary-outlined c-btn td-btn"
}
>
<img
style={{
position: "relative",
}}
src={forbiden}
src={edit}
/>
</IconButton>
<Link to={`/users/${n.id}`}>
<IconButton
className={
"c-btn--primary-outlined c-btn td-btn"
}
>
<img
style={{
position: "relative",
}}
src={edit}
/>
</IconButton>
</Link>
</>
</td>
)}
</tr>
))
}
</>
</td>
)}
</tr>
))}
</tbody>
</table>
</div>
@@ -308,5 +369,13 @@ const UsersPage = () => {
</div>
);
};

UsersPage.propTypes = {
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
};
export default UsersPage;

+ 20
- 11
src/store/saga/usersSaga.js Ver arquivo

@@ -47,11 +47,13 @@ export function* getUsers() {

export function* enableUser({ payload }) {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
yield call(addHeaderToken, JwtToken);
const result = yield call(enableUserRequest, payload.id);
yield put(setEnableUsers(result.data));
yield put(toggleSingleUser());
if(payload.handleApiResponseSuccess){
yield call(payload.handleApiResponseSuccess)
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (error) {
if (error.response && error.response.data) {
@@ -63,14 +65,16 @@ export function* enableUser({ payload }) {

export function* deleteUser({ payload }) {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
yield call(addHeaderToken, JwtToken);
const result = yield call(deleteUserRequest, payload.id);

// linija koda ispod nece biti potrebna
// jer nakon brisanja svakako idemo na
// linija koda ispod nece biti potrebna
// jer nakon brisanja svakako idemo na
// users page gde se setuje state ponovo sa novim vrednostima
yield put(deleteStateUser(result.data));
if(payload.handleApiResponseSuccess){
yield call(payload.handleApiResponseSuccess)
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (error) {
if (error.response && error.response.data) {
@@ -82,13 +86,16 @@ export function* deleteUser({ payload }) {

export function* userDetails({ payload }) {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
yield call(addHeaderToken, JwtToken);
const result = yield call(userDetailsRequest, payload.id);
// console.log(result)
yield put(stateUserDetailsSuccess(result.data));
if(payload.handleApiResponseSuccess){
yield call(payload.handleApiResponseSuccess)
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (error) {
console.log(error)
if (error.response && error.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, error);
yield put(userDetailsError(errorMessage));
@@ -98,11 +105,13 @@ export function* userDetails({ payload }) {

export function* invite({ payload }) {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
yield call(addHeaderToken, JwtToken);
const result = yield call(inviteUserRequest, payload.invite);
console.log(result)
console.log(result);
yield put(inviteUserSuccess());
if(payload.handleApiResponseSuccess){
yield call(payload.handleApiResponseSuccess)
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (error) {
if (error.response && error.response.data) {

Carregando…
Cancelar
Salvar