Sfoglia il codice sorgente

Partlu finished feature/685

feature/685
Djordje Mitrovic 3 anni fa
parent
commit
af43261050

+ 5
- 2
src/components/Cards/CategoryCard/CategoryCard.js Vedi File

@@ -24,7 +24,7 @@ const CategoryCard = (props) => {
const [openedDeleteModal, setOpenedDeleteModal] = useState(false);
const [openedEditModal, setOpenedEditModal] = useState(false);
const navigateToCategory = () => {
if (!props.subcategory) {
if (!props.hideCheckButton) {
history.push(
replaceInRoute(ADMIN_SUBCATEGORIES_PAGE, {
categoryId: props.category._id,
@@ -32,12 +32,12 @@ const CategoryCard = (props) => {
);
}
};
console.log(props);
return (
<>
<CategoryCardContainer className={props.className}>
<CategoryCardLeftContainer>
<CategoryCardName
image={props?.category?.image}
categoryName={props?.category?.name || props?.category?.city}
onClick={navigateToCategory}
/>
@@ -67,6 +67,7 @@ const CategoryCard = (props) => {
</CategoryCardContainer>
{openedDeleteModal && (
<DeleteCategory
categoryId={props.categoryId}
setOpenedDeleteModal={setOpenedDeleteModal}
subcategory={props.subcategory}
category={props.category}
@@ -78,6 +79,7 @@ const CategoryCard = (props) => {
hideImagePicker={props.type !== "categories"}
setOpenedEditModal={setOpenedEditModal}
category={props.category}
categoryId={props.categoryId}
subcategory={props.subcategory}
type={props.type}
method="edit"
@@ -98,6 +100,7 @@ CategoryCard.propTypes = {
className: PropTypes.string,
subcategory: PropTypes.bool,
type: PropTypes.string,
categoryId: PropTypes.string,
};

export default CategoryCard;

+ 15
- 1
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.js Vedi File

@@ -1,14 +1,27 @@
import React from "react";
import PropTypes from "prop-types";
import {
CategoryCardImage,
CategoryCardImageContainer,
CategoryCardNameContainer,
CategoryCardNameText,
CategoryCardNameTextContainer,
} from "./CategoryCardName.styled";
import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter";

const CategoryCardName = (props) => {
return (
<CategoryCardNameContainer onClick={props.onClick}>
<CategoryCardNameText>{props.categoryName}</CategoryCardNameText>
{props.image && (
<CategoryCardImageContainer>
<CategoryCardImage
src={getImageUrl(props.image, variants.categoryIcon)}
/>
</CategoryCardImageContainer>
)}
<CategoryCardNameTextContainer>
<CategoryCardNameText>{props.categoryName}</CategoryCardNameText>
</CategoryCardNameTextContainer>
</CategoryCardNameContainer>
);
};
@@ -17,6 +30,7 @@ CategoryCardName.propTypes = {
children: PropTypes.node,
categoryName: PropTypes.string,
onClick: PropTypes.func,
image: PropTypes.string,
};

export default CategoryCardName;

+ 37
- 3
src/components/Cards/CategoryCard/CategoryCardName/CategoryCardName.styled.js Vedi File

@@ -3,19 +3,53 @@ import styled from "styled-components";
import selectedTheme from "../../../../themes";

export const CategoryCardNameContainer = styled(Box)`
padding: 18px;
min-width: 234px;
width: 234px;
max-width: 234px;
display: flex;
padding-left: 18px;
/* max-width: 300px; */
flex-direction: row;
`;
export const CategoryCardNameTextContainer = styled(Box)`
height: 82px;
display: flex;
flex-direction: column;
justify-content: center;
@media (max-width: 600px) {
height: 60px;
}
`
export const CategoryCardNameText = styled(Typography)`
font-family: ${selectedTheme.fonts.textFont};
font-weight: 700;
font-size: 16px;
line-height: 21px;
padding-top: 12px;
max-height: 42px;
overflow: hidden;
text-overflow: ellipsis;
color: ${selectedTheme.colors.primaryPurple};
cursor: pointer;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@media (max-width: 600px) {
font-size: 18px;
padding: 0;
}
`;
export const CategoryCardImageContainer = styled(Box)`
height: 82px;
display: flex;
flex-direction: column;
justify-content: center;
@media (max-width: 600px) {
height: 60px;
}
`
export const CategoryCardImage = styled.img`
width: 18px;
height: 18px;
margin-right: 9px;
position: relative;
top: 1px;
`;

+ 3
- 0
src/components/Cards/CategoryCard/CategoryCheckButton/CategoryCheckButton.styled.js Vedi File

@@ -11,6 +11,9 @@ export const CheckButton = styled(PrimaryButton)`
background-color: ${selectedTheme.colors.primaryPurple} !important;
color: white !important;
}
@media (max-width: 1390px) {
display: none;
}
@media (max-width: 850px) {
display: none;
}

+ 5
- 0
src/components/Cards/ItemDetailsCard/Information/Information.styled.js Vedi File

@@ -17,6 +17,11 @@ export const InfoText = styled(Typography)`
text-transform: capitalize;
color: ${selectedTheme.colors.primaryText};
font-size: 12px;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-height: 16px;
@media (max-width: 600px) {
font-size: 12px;
}

+ 8
- 0
src/components/Dropdown/DropdownList/DropdownList.styled.js Vedi File

@@ -19,6 +19,14 @@ export const DropdownTitle = styled(Typography)`
padding-bottom: 10px;
padding-top: 5px;
padding-right: 0.9rem;
width: 185px;
margin-right: 15px;
max-width: 185px;
overflow: hidden;
white-space: nowrap;
display: inline-block;
text-overflow: ellipsis;
max-height: 26px;
font-family: ${selectedTheme.fonts.textFont};
color: ${(props) =>
props.disabled

+ 23
- 0
src/components/Modals/DeleteCategory/DeleteCategory.js Vedi File

@@ -14,8 +14,12 @@ import LabeledCard from "../../Cards/LabeledCard/LabeledCard";
import { useTranslation } from "react-i18next";
import { useMemo } from "react";
import selectedTheme from "../../../themes";
import { useDispatch } from "react-redux";
import { fetchAdminMethod } from "../../../store/actions/admin/adminActions";
import { DELETE_TYPE } from "../../../constants/adminMethodConstants";

const DeleteCategory = (props) => {
const dispatch = useDispatch();
const { t } = useTranslation();
console.log(props.category);
const handleCancel = () => {
@@ -24,6 +28,23 @@ const DeleteCategory = (props) => {
const reassuranceText = useMemo(() => {
return t(`admin.${props.type}.reassuranceDelete`);
}, [props, t]);

const handleApiResponseSuccess = () => {
handleCancel();
};

const handleSubmit = () => {
dispatch(
fetchAdminMethod({
type: props.type,
method: DELETE_TYPE,
name: props.category?.name,
id: props.category?._id,
categoryId: props.categoryId,
handleApiResponseSuccess,
})
);
};
return (
<>
<BackdropComponent
@@ -55,6 +76,7 @@ const DeleteCategory = (props) => {
<SaveButton
variant="outlined"
buttoncolor={selectedTheme.colors.primaryPurple}
onClick={handleSubmit}
>
{t(`admin.${props.type}.delete`)}
</SaveButton>
@@ -73,6 +95,7 @@ DeleteCategory.propTypes = {
customLabeledCardWidth: PropTypes.string,
customLabeledCardHeight: PropTypes.string,
customLabeledCardIcon: PropTypes.node,
categoryId: PropTypes.string,
};

export default DeleteCategory;

+ 33
- 7
src/components/Modals/EditCategory/EditCategory.js Vedi File

@@ -17,11 +17,16 @@ import { useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { useFormik } from "formik";
import { useMemo } from "react";
import { useDispatch } from "react-redux";
import { fetchAdminMethod } from "../../../store/actions/admin/adminActions";
import { useRef } from "react";

const EditCategory = (props) => {
const { t } = useTranslation();
const [image, setImage] = useState("");
console.log(props);
const dispatch = useDispatch();
const [clickedOnNext, setClickedOnNext] = useState(false);
const setImage = useState("")[1];
const inputRef = useRef(null);
const title = useMemo(() => {
return t(`admin.${props.type}.${props.method}.title`);
}, [props.type, props.method]);
@@ -44,17 +49,36 @@ const EditCategory = (props) => {
setImage(image);
formik.setFieldValue("image", image);
};
const handleApiResponseSuccess = () => {
if (clickedOnNext) {
formik.resetForm();
inputRef.current.focus();
} else props.setOpenedEditModal(false);
};
const handleSubmit = (values) => {
console.log(values);
console.log(image);
dispatch(
fetchAdminMethod({
type: props.type,
method: props.method,
values,
name: props?.category?.name,
id: props?.category?._id,
categoryId: props.categoryId,
handleApiResponseSuccess,
})
);
};
const formik = useFormik({
initialValues: {
image: "",
title: props?.category?.name || props?.category?.city || "",
title: props?.category?.name || props?.category?.city || "",
},
onSubmit: handleSubmit,
});
const handleClick = (next) => {
if (next !== clickedOnNext) setClickedOnNext(next);
formik.handleSubmit();
};
console.log(props);
return (
<>
@@ -78,6 +102,7 @@ const EditCategory = (props) => {
<FieldLabel leftText={fieldLabel} />
<CategoryTitleField
name="title"
ref={inputRef}
placeholder={placeholder}
italicPlaceholder
margin="normal"
@@ -94,7 +119,7 @@ const EditCategory = (props) => {
showSecondButton={props.showSecondButton}
variant={props.firstOutlined ? "outlined" : "contained"}
height="48px"
onClick={formik.handleSubmit}
onClick={() => handleClick(true)}
>
{firstButtonText}
</SaveButton>
@@ -102,7 +127,7 @@ const EditCategory = (props) => {
<SaveButton
variant={props.secondOutlined ? "outlined" : "contained"}
showSecondButton={props.showSecondButton}
onClick={formik.handleSubmit}
onClick={() => handleClick(false)}
>
{secondButtonText}
</SaveButton>
@@ -122,6 +147,7 @@ EditCategory.propTypes = {
method: PropTypes.string,
firstOutlined: PropTypes.bool,
secondOutlined: PropTypes.bool,
categoryId: PropTypes.string,
};

EditCategory.defaultProps = {

+ 3
- 0
src/constants/adminMethodConstants.js Vedi File

@@ -0,0 +1,3 @@
export const EDIT_TYPE = "edit";
export const DELETE_TYPE = "delete";
export const ADD_TYPE = "add";

+ 5
- 0
src/constants/adminTypeConstants.js Vedi File

@@ -0,0 +1,5 @@
export const CATEGORIES_TYPE = "categories";
export const SUBCATEGORIES_TYPE = "subcategories";
export const USERS_TYPE = "users";
export const LOCATIONS_TYPE = "locations";
export const REVIEW_TYPE = "reviews";

+ 1
- 1
src/i18n/resources/rs.js Vedi File

@@ -488,7 +488,7 @@ export default {
fieldTitle: "Naslov",
placeholder: "Naziv podkategorije...",
save: "Izmeni",
next: "Sledeća",
next: "Izmeni",
},
add: {
title: "Nova Podkategorija",

+ 1
- 0
src/pages/AdminHomePage/AdminLocationsPage/AdminLocationsPage.js Vedi File

@@ -57,6 +57,7 @@ const AdminLocationsPage = () => {
<LocationsList>
{locationsToShow.map((category) => (
<CategoryCard
dontNavigate
key={category._id}
category={category}
type="locations"

+ 6
- 4
src/pages/AdminHomePage/AdminSubcategoriesPage/AdminSubcategoriesPage.js Vedi File

@@ -60,7 +60,7 @@ const AdminSubcategoriesPage = () => {
return [];
}, [category, manualSearchString]);
console.log("subc", categories);
return (
<>
<AdminSubcategoriesPageContainer>
@@ -95,11 +95,12 @@ const AdminSubcategoriesPage = () => {
hideBackButton
/>
<SubcategoriesList>
{subcategories.map((category) => (
{subcategories.map((subcategory) => (
<CategoryCard
categoryId={category._id}
subcategory
key={category._id}
category={category}
key={subcategory._id}
category={subcategory}
type="subcategories"
hideSecondLabel
hideCheckButton
@@ -118,6 +119,7 @@ const AdminSubcategoriesPage = () => {
{openedAddModal && (
<EditCategory
hideImagePicker
categoryId={category._id}
setOpenedEditModal={setOpenedAddModal}
type="subcategories"
method="add"

+ 19
- 2
src/request/apiEndpoints.js Vedi File

@@ -122,7 +122,7 @@ export default {
editProfile: "users",
editProfileAsAdmin: "admin/users/{userId}",
getAllProfiles: "users",
getAllProfilesAsAdmin: "admin/users"
getAllProfilesAsAdmin: "admin/users",
},
applications: {
application: "/applications/{applicationUid}",
@@ -182,9 +182,26 @@ export default {
},
exchange: {
getExchange: "exchanges",
validateExchange: "exchanges"
validateExchange: "exchanges",
},
reviews: {
postReview: "reviews",
},
admin: {
categories: {
newCategory: "admin/categories",
editCategory: "admin/categories/{categoryId}",
deleteCategory: "admin/categories/{categoryId}",
},
subcategories: {
newSubcategory: "admin/categories/{categoryId}",
editSubcategory: "admin/categories/{categoryId}/{subcategoryName}",
deleteSubcategory: "admin/categories/{categoryId}/{subcategoryName}",
},
locations: {
newLocation: "admin/locations",
editLocation: "admin/locations/{locationId}",
deleteLocation: "admin/locations/{locationId}"
}
},
};

+ 48
- 1
src/request/categoriesRequest.js Vedi File

@@ -1,5 +1,52 @@
import { getRequest } from ".";
import {
deleteRequest,
getRequest,
postRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints";

export const attemptFetchCategories = () =>
getRequest(apiEndpoints.offers.categories);

export const attemptAddNewCategory = (payload) =>
postRequest(apiEndpoints.admin.categories.newCategory, payload.body);

export const attemptEditCategory = (payload) =>
putRequest(
replaceInUrl(apiEndpoints.admin.categories.editCategory, {
categoryId: payload.categoryId,
}),
payload.body
);
export const attemptDeleteCategory = (payload) =>
deleteRequest(
replaceInUrl(apiEndpoints.admin.categories.deleteCategory, {
categoryId: payload.categoryId,
})
);

export const attemptAddNewSubcategory = (payload) =>
postRequest(
replaceInUrl(apiEndpoints.admin.subcategories.newSubcategory, {
categoryId: payload.categoryId,
}),
payload.body
);
export const attemptEditSubcategory = (payload) =>
putRequest(
replaceInUrl(apiEndpoints.admin.subcategories.editSubcategory, {
categoryId: payload.categoryId,
subcategoryName: payload.subcategoryName,
}),
payload.body
);
export const attemptDeleteSubcategory = (payload) =>
deleteRequest(
replaceInUrl(apiEndpoints.admin.subcategories.deleteSubcategory, {
categoryId: payload.categoryId,
subcategoryName: payload.subcategoryName,
})
);

+ 25
- 2
src/request/locationsRequest.js Vedi File

@@ -1,5 +1,28 @@
import { getRequest } from ".";
import {
deleteRequest,
getRequest,
postRequest,
putRequest,
replaceInUrl,
} from ".";
import apiEndpoints from "./apiEndpoints";

export const attemptFetchLocations = () =>
getRequest(apiEndpoints.offers.locations);
getRequest(apiEndpoints.offers.locations);
export const attemptAddNewLocation = (payload) =>
postRequest(apiEndpoints.admin.locations.newLocation, payload.body);

export const attemptEditLocation = (payload) =>
putRequest(
replaceInUrl(apiEndpoints.admin.locations.editLocation, {
locationId: payload.locationId,
}),
payload.body
);

export const attemptDeleteLocation = (payload) =>
deleteRequest(
replaceInUrl(apiEndpoints.admin.locations.deleteLocation, {
locationId: payload.locationId,
})
);

+ 10
- 0
src/store/actions/admin/adminActionConstants.js Vedi File

@@ -0,0 +1,10 @@
import {
createErrorType,
createFetchType,
createSuccessType,
} from "../actionHelpers";

const ADMIN_SCOPE = "ADMIN";
export const ADMIN_FETCH = createFetchType(ADMIN_SCOPE);
export const ADMIN_FETCH_SUCCESS = createSuccessType(ADMIN_SCOPE);
export const ADMIN_FETCH_ERROR = createErrorType(ADMIN_SCOPE);

+ 16
- 0
src/store/actions/admin/adminActions.js Vedi File

@@ -0,0 +1,16 @@
import {
ADMIN_FETCH,
ADMIN_FETCH_ERROR,
ADMIN_FETCH_SUCCESS,
} from "./adminActionConstants";

export const fetchAdminMethod = (payload) => ({
type: ADMIN_FETCH,
payload,
});
export const fetchAdminMethodSuccess = () => ({
type: ADMIN_FETCH_SUCCESS,
});
export const fetchAdminMethodError = () => ({
type: ADMIN_FETCH_ERROR,
});

+ 194
- 0
src/store/saga/adminSaga.js Vedi File

@@ -0,0 +1,194 @@
import { all, call, put, takeLatest } from "@redux-saga/core/effects";
import {
ADD_TYPE,
DELETE_TYPE,
EDIT_TYPE,
} from "../../constants/adminMethodConstants";
import {
CATEGORIES_TYPE,
LOCATIONS_TYPE,
SUBCATEGORIES_TYPE,
} from "../../constants/adminTypeConstants";
import {
attemptAddNewCategory,
attemptAddNewSubcategory,
attemptDeleteCategory,
attemptDeleteSubcategory,
attemptEditCategory,
attemptEditSubcategory,
} from "../../request/categoriesRequest";
import {
attemptAddNewLocation,
attemptDeleteLocation,
attemptEditLocation,
} from "../../request/locationsRequest";
// import { attemptAddNewCategory } from "../../request/categoriesRequest";
import { ADMIN_FETCH } from "../actions/admin/adminActionConstants";
import {
fetchAdminMethodError,
fetchAdminMethodSuccess,
} from "../actions/admin/adminActions";
import { fetchCategories } from "../actions/categories/categoriesActions";
import { fetchLocations } from "../actions/locations/locationsActions";

function* editCategory(payload) {
try {
yield call(attemptEditCategory, {
categoryId: payload.id,
body: { name: payload.values.title },
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}
function* addCategory(payload) {
try {
let dataToSend = new FormData();
dataToSend.append("name", payload.values.title);
dataToSend.append("file", payload.values.image);
dataToSend.append("subcategories", JSON.stringify([]));
yield call(attemptAddNewCategory, {
body: dataToSend,
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* deleteCategory(payload) {
try {
yield call(attemptDeleteCategory, {
categoryId: payload.id,
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* editSubcategory(payload) {
try {
yield call(attemptEditSubcategory, {
categoryId: payload.id,
subcategoryName: payload.name,
body: { name: payload.values.title },
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}
function* addSubcategory(payload) {
try {
yield call(attemptAddNewSubcategory, {
categoryId: payload.id,
body: { name: payload.values.title },
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* deleteSubcategory(payload) {
try {
yield call(attemptDeleteSubcategory, {
categoryId: payload.id,
subcategoryName: payload.name,
});
yield put(fetchCategories());
} catch (e) {
yield call(console.log, e);
}
}

function* editLocation(payload) {
try {
yield call(attemptEditLocation, {
locationId: payload.id,
body: { city: payload.values.title },
});
yield put(fetchLocations());
} catch (e) {
yield call(console.log, e);
}
}
function* addLocation(payload) {
try {
yield call(attemptAddNewLocation, {
locationId: payload.id,
body: { city: payload.values.title },
});
yield put(fetchLocations());
} catch (e) {
yield call(console.log, e);
}
}

function* deleteLocation(payload) {
try {
yield call(attemptDeleteLocation, {
locationId: payload.id,
});
yield put(fetchLocations());
} catch (e) {
yield call(console.log, e);
}
}

function* fetchAdminMethod({ payload }) {
try {
yield call(console.log, "admin", payload);
if (payload.type === CATEGORIES_TYPE) {
if (payload.method === ADD_TYPE)
yield call(addCategory, { values: payload.values, id: payload.id });
else if (payload.method === EDIT_TYPE)
yield call(editCategory, { values: payload.values, id: payload.id });
else if (payload.method === DELETE_TYPE)
yield call(deleteCategory, { id: payload.id });
} else if (payload.type === SUBCATEGORIES_TYPE) {
if (payload.method === EDIT_TYPE)
yield call(editSubcategory, {
values: payload.values,
id: payload.categoryId,
name: payload.name,
});
else if (payload.method === DELETE_TYPE)
yield call(deleteSubcategory, {
id: payload.categoryId,
name: payload.name,
});
else if (payload.method === ADD_TYPE)
yield call(addSubcategory, {
values: payload.values,
id: payload.categoryId,
});
} else if (payload.type === LOCATIONS_TYPE) {
if (payload.method === ADD_TYPE)
yield call(addLocation, {
values: payload.values,
id: payload.id,
});
else if (payload.method === EDIT_TYPE)
yield call(editLocation, {
values: payload.values,
id: payload.id,
});
else if (payload.method === DELETE_TYPE)
yield call(deleteLocation, {
id: payload.id,
});
}
if (payload.handleApiResponseSuccess)
yield call(payload.handleApiResponseSuccess);
yield put(fetchAdminMethodSuccess());
} catch (e) {
yield put(fetchAdminMethodError());
}
}

export default function* adminSaga() {
yield all([takeLatest(ADMIN_FETCH, fetchAdminMethod)]);
}

+ 3
- 1
src/store/saga/index.js Vedi File

@@ -1,4 +1,5 @@
import { all } from 'redux-saga/effects';
import adminSaga from './adminSaga';
import categoriesSaga from './categoriesSaga';
import chatSaga from './chatSaga';
import counterSaga from './counterSaga';
@@ -25,6 +26,7 @@ export default function* rootSaga() {
queryStringSaga(),
exchangeSaga(),
reviewSaga(),
counterSaga()
counterSaga(),
adminSaga()
]);
}

+ 3
- 1
src/util/helpers/imageUrlGetter.js Vedi File

@@ -15,7 +15,8 @@ export const variants = {
profileCard: "profileCard",
createReviewCard: "createReviewCard",
carousel: "carousel",
adminProfileCard: "reviewCard"
adminProfileCard: "reviewCard",
categoryIcon: "categoryIcon"
};

const cloudFlareVariants = {
@@ -37,6 +38,7 @@ const cloudFlareVariants = {
createReviewCard: "primaryMobile",
carousel: "carousel",
carouselMobile: "carouselMobile",
categoryIcon: "categoryIcon"
};
export const getImageUrl = (imageUrl, variant, isMobile = false) => {
let imageVariant = "";

+ 2
- 1
src/util/helpers/routeHelpers.js Vedi File

@@ -57,7 +57,8 @@ export const isAdminRoute = () => {
dynamicRouteMatches(ADMIN_SUBCATEGORIES_PAGE) ||
routeMatches(ADMIN_LOCATIONS_PAGE) ||
routeMatches(ADMIN_PAYMENT_PAGE) ||
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE)
dynamicRouteMatches(ADMIN_SINGLE_USER_PAGE) ||
isInRoute(ADMIN_HOME_PAGE)
)
return true;
return false;

Loading…
Annulla
Salva