| @@ -81,7 +81,7 @@ const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => { | |||
| // Create (or edit) offer | |||
| const handleSubmitOffer = () => { | |||
| if (editOffer) { | |||
| dispatch(editOneOffer(offer._id, offerData)); | |||
| dispatch(editOneOffer({offerId: offer._id, offerData, handleApiResponseSuccess})); | |||
| } else { | |||
| dispatch(addOffer({ offerData, handleApiResponseSuccess })); | |||
| } | |||
| @@ -7,15 +7,15 @@ import { | |||
| MessageText, | |||
| ProfileImage, | |||
| } from "./MessageCard.styled"; | |||
| import { formatDateTime } from "../../../util/helpers/dateHelpers"; | |||
| import useIsMobile from "../../../hooks/useIsMobile"; | |||
| import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter"; | |||
| import { getMessageDate } from "../../../util/helpers/dateHelpers"; | |||
| const MessageCard = (props) => { | |||
| const message = props.message; | |||
| const { isMobile } = useIsMobile(); | |||
| const dateString = formatDateTime(new Date(message._created)); | |||
| const dateString = getMessageDate(new Date(message._created)); | |||
| return ( | |||
| <MessageCardContainer ismymessage={props.isMyMessage}> | |||
| <ProfileImage | |||
| @@ -10,6 +10,7 @@ export const MessageCardContainer = styled(Box)` | |||
| export const ProfileImage = styled.img` | |||
| width: 54px; | |||
| height: 54px; | |||
| min-width: 54px; | |||
| overflow: hidden; | |||
| border-radius: 100%; | |||
| @media (max-width: 600px) { | |||
| @@ -24,6 +25,7 @@ export const MessageContent = styled(Box)` | |||
| border-radius: ${(props) => | |||
| props.ismymessage ? "9px 0px 9px 9px" : "0px 9px 9px 9px"}; | |||
| padding: 9px; | |||
| padding-bottom: 31px; | |||
| position: relative; | |||
| min-height: 65px; | |||
| margin: 0 18px; | |||
| @@ -40,7 +40,6 @@ const ProfileCard = () => { | |||
| const profileFromRedux = useSelector(selectProfile); | |||
| const userId = useSelector(selectUserId); | |||
| const idProfile = useMemo(() => { | |||
| console.log("routematch", routeMatch); | |||
| return routeMatch.params.idProfile; | |||
| }, [routeMatch.params.idProfile]); | |||
| const { t } = useTranslation(); | |||
| @@ -83,7 +83,6 @@ const DirectChat = () => { | |||
| // Listener to socket.IO chat | |||
| useEffect(() => { | |||
| addMesageListener(({ succeed, data }) => { | |||
| console.log(succeed); | |||
| if (succeed) { | |||
| if ( | |||
| [...allChats].find((item) => { | |||
| @@ -19,6 +19,7 @@ import { useHistory, useLocation } from "react-router-dom"; | |||
| import { selectExchange } from "../../../store/selectors/exchangeSelector"; | |||
| import { validateExchange } from "../../../store/actions/exchange/exchangeActions"; | |||
| import NotAllowedChat from "./NotAllowedChat/NotAllowedChat"; | |||
| import { convertLocalDateToUTCDate } from "../../../util/helpers/dateHelpers"; | |||
| const DirectChatNewMessage = (props) => { | |||
| const [typedValue, setTypedValue] = useState(""); | |||
| @@ -40,7 +41,7 @@ const DirectChatNewMessage = (props) => { | |||
| message: { | |||
| userId, | |||
| text: typedValue, | |||
| _created: new Date().toISOString(), | |||
| _created: convertLocalDateToUTCDate(new Date()), | |||
| }, | |||
| }) | |||
| ); | |||
| @@ -6,10 +6,14 @@ import { | |||
| HeaderAltLocation, | |||
| HeaderButton, | |||
| HeaderButtons, | |||
| HeaderCategoryString, | |||
| HeaderContainer, | |||
| HeaderLocation, | |||
| HeaderLocationsMainString, | |||
| HeaderLocationsString, | |||
| HeaderOptions, | |||
| HeaderSelect, | |||
| HeaderSubcategoryString, | |||
| // HeaderText, | |||
| HeaderTitleContainer, | |||
| HeaderTitleText, | |||
| @@ -20,6 +24,7 @@ import { | |||
| SwapsHeaderIcon, | |||
| SwapsIcon, | |||
| SwapsTitle, | |||
| TooltipInnerContainer, | |||
| } from "./Header.styled"; | |||
| import { ReactComponent as GridSquare } from "../../../assets/images/svg/offer-grid-square.svg"; | |||
| import { ReactComponent as GridLine } from "../../../assets/images/svg/offer-grid-line.svg"; | |||
| @@ -50,20 +55,26 @@ const Header = (props) => { | |||
| const altString = useMemo(() => { | |||
| if (sorting.selectedSortOption === sortEnum.OLD) { | |||
| return t("header.oldOffers"); | |||
| return `: ${t("header.oldOffers")}`; | |||
| } | |||
| if (sorting.selectedSortOption === sortEnum.POPULAR) { | |||
| return t("header.popularOffers"); | |||
| return `: ${t("header.popularOffers")}`; | |||
| } | |||
| return t("header.newOffers"); | |||
| return `: ${t("header.newOffers")}`; | |||
| }, [sorting.selectedSortOption]); | |||
| const handleChangeSelect = (event) => { | |||
| sorting.changeSorting(event.target.value); | |||
| }; | |||
| // const goBack = () => { | |||
| // history.goBack(); | |||
| // }; | |||
| const handleClickCategory = () => { | |||
| props.offers.filters.locations.clear(); | |||
| props.offers.filters.subcategory.clear(); | |||
| props.offers.applyFilters(); | |||
| }; | |||
| const handleClickSubcategory = () => { | |||
| props.offers.filters.locations.clear(); | |||
| props.offers.applyFilters(); | |||
| }; | |||
| return ( | |||
| <> | |||
| @@ -71,15 +82,28 @@ const Header = (props) => { | |||
| <HeaderWrapperContainer skeleton={props.skeleton}> | |||
| <HeaderContainer> | |||
| {/* Setting appropriate header title if page is market place or my offers */} | |||
| <Tooltip title={headerString}> | |||
| <> | |||
| <Tooltip title={headerString.text}> | |||
| <TooltipInnerContainer> | |||
| {!props.myOffers ? ( | |||
| <> | |||
| <CategoryHeaderIcon /> | |||
| <HeaderLocation initial={altString}> | |||
| {headerString} | |||
| <HeaderLocation> | |||
| {/* {headerString} */} | |||
| <HeaderCategoryString component="span" onClick={handleClickCategory}> | |||
| {headerString.categoryString} | |||
| {headerString.subcategoryString && <> </>} | |||
| </HeaderCategoryString> | |||
| <HeaderSubcategoryString component="span" onClick={handleClickSubcategory}> | |||
| {headerString.subcategoryString} | |||
| {headerString.locationsString && <> </>} | |||
| </HeaderSubcategoryString> | |||
| <HeaderLocationsString component="span"> | |||
| <HeaderLocationsMainString component="span"> | |||
| {headerString.locationsString} | |||
| </HeaderLocationsMainString> | |||
| <HeaderAltLocation component="span">{altString}</HeaderAltLocation> | |||
| </HeaderLocationsString> | |||
| </HeaderLocation> | |||
| <HeaderAltLocation>{altString}</HeaderAltLocation> | |||
| </> | |||
| ) : ( | |||
| <HeaderTitleContainer> | |||
| @@ -87,7 +111,7 @@ const Header = (props) => { | |||
| <HeaderTitleText>{t("header.myOffers")}</HeaderTitleText> | |||
| </HeaderTitleContainer> | |||
| )} | |||
| </> | |||
| </TooltipInnerContainer> | |||
| </Tooltip> | |||
| {/* ^^^^^^ */} | |||
| @@ -166,7 +190,7 @@ Header.propTypes = { | |||
| children: PropTypes.node, | |||
| setIsGrid: PropTypes.func, | |||
| isGrid: PropTypes.bool, | |||
| filters: PropTypes.any, | |||
| offers: PropTypes.any, | |||
| category: PropTypes.string, | |||
| myOffers: PropTypes.bool, | |||
| skeleton: PropTypes.bool, | |||
| @@ -17,7 +17,16 @@ export const HeaderContainer = styled(Box)` | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| `; | |||
| export const HeaderLocation = styled(Box)` | |||
| export const TooltipInnerContainer = styled(Box)` | |||
| width: 100%; | |||
| white-space: nowrap; | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| & * { | |||
| display: inline; | |||
| } | |||
| `; | |||
| export const HeaderLocation = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| font-weight: 700; | |||
| @@ -25,10 +34,10 @@ export const HeaderLocation = styled(Box)` | |||
| font-size: 16px; | |||
| flex: 2; | |||
| margin-left: 9px; | |||
| max-width: ${(props) => (props.initial ? "fit-content" : "50%")}; | |||
| white-space: nowrap; | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| max-width: 50%; | |||
| position: relative; | |||
| top: -2px; | |||
| &:after { | |||
| content: ${(props) => (props.initial ? `":"` : `""`)}; | |||
| @media (max-width: 600px) { | |||
| @@ -40,6 +49,58 @@ export const HeaderLocation = styled(Box)` | |||
| padding-top: 3px; | |||
| } | |||
| `; | |||
| export const HeaderCategoryString = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| font-weight: 700; | |||
| line-height: 22px; | |||
| font-size: 16px; | |||
| /* position: relative; | |||
| bottom: 2px; */ | |||
| cursor: pointer; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| padding-top: 3px; | |||
| } | |||
| `; | |||
| export const HeaderSubcategoryString = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| font-weight: 700; | |||
| line-height: 22px; | |||
| font-size: 16px; | |||
| /* position: relative; | |||
| bottom: 2px; */ | |||
| cursor: pointer; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| padding-top: 3px; | |||
| } | |||
| `; | |||
| export const HeaderLocationsString = styled(Typography)` | |||
| /* position: relative; | |||
| bottom: 2px; */ | |||
| white-space: nowrap; | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| padding-top: 3px; | |||
| } | |||
| max-width: 100%; | |||
| `; | |||
| export const HeaderLocationsMainString = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| max-width: 200px; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| font-weight: 700; | |||
| line-height: 22px; | |||
| font-size: 16px; | |||
| @media (max-width: 600px) { | |||
| font-size: 12px; | |||
| padding-top: 3px; | |||
| } | |||
| `; | |||
| export const HeaderButton = styled(IconButton)` | |||
| padding: 2px 10px; | |||
| @media (max-width: 1500px) { | |||
| @@ -98,7 +159,8 @@ export const HeaderAltLocation = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 16px; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| margin-left: 5px; | |||
| position: relative; | |||
| top: 0.5px; | |||
| @media (max-width: 600px) { | |||
| display: none; | |||
| } | |||
| @@ -121,10 +183,11 @@ export const MySwapsTitle = styled(Typography)` | |||
| left: 9px; | |||
| `; | |||
| export const CategoryHeaderIcon = styled(CategoryHeader)` | |||
| position: relative; | |||
| top: 4px; | |||
| @media (max-width: 600px) { | |||
| width: 12px; | |||
| height: 12px; | |||
| position: relative; | |||
| top: 1px; | |||
| } | |||
| `; | |||
| @@ -168,15 +231,15 @@ export const HeaderText = styled(Typography)` | |||
| export const SwapsHeaderIcon = styled(SwapsIcon)` | |||
| width: 18px; | |||
| height: 18px; | |||
| ` | |||
| `; | |||
| export const HeaderTitleContainer = styled(Box)` | |||
| position: relative; | |||
| left: 5px; | |||
| ` | |||
| `; | |||
| export const HeaderTitleText = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryText}; | |||
| font-size: 16px; | |||
| position: relative; | |||
| bottom: 2px; | |||
| ` | |||
| `; | |||
| @@ -15,6 +15,7 @@ const MarketPlace = (props) => { | |||
| setIsGrid={setIsGrid} | |||
| myOffers={props.myOffers} | |||
| sorting={props.offers.sorting} | |||
| offers={props.offers} | |||
| skeleton={props.skeleton} | |||
| /> | |||
| <Offers | |||
| @@ -10,7 +10,6 @@ import { useTranslation } from "react-i18next"; | |||
| const PhonePopover = (props) => { | |||
| const {t} = useTranslation(); | |||
| console.log(props); | |||
| return ( | |||
| <PhonePopoverContainer> | |||
| <Arrow /> | |||
| @@ -20,6 +20,7 @@ import useQueryString from "./useQueryString"; | |||
| import { setQueryString } from "../../store/actions/queryString/queryStringActions"; | |||
| import { | |||
| convertQueryStringForBackend, | |||
| makeHeaderStringFromQueryObjectHelper, | |||
| makeHeaderStringHelper, | |||
| makeQueryStringHelper, | |||
| } from "../../util/helpers/queryHelpers"; | |||
| @@ -63,7 +64,7 @@ const useOffers = () => { | |||
| useEffect(() => { | |||
| const headerStringLocal = makeHeaderStringHelper(filters); | |||
| dispatch(setHeaderString(headerStringLocal)); | |||
| }, [queryStringHook.queryString]); | |||
| }, [queryStringHook.queryString, filtersCleared]); | |||
| // Initially set category, location and subcategory based on query string | |||
| useEffect(() => { | |||
| @@ -101,6 +102,7 @@ const useOffers = () => { | |||
| search.clear(); | |||
| } | |||
| dispatch(setSearchString(queryObject[KEY_SEARCH])); | |||
| dispatch(setHeaderString(makeHeaderStringFromQueryObjectHelper(queryObject))); | |||
| } | |||
| }, [queryStringHook.isInitiallyLoaded]); | |||
| @@ -126,6 +128,10 @@ const useOffers = () => { | |||
| dispatch(setQueryString(convertQueryStringForBackend(newQueryString))); | |||
| }; | |||
| const applyFilters = () => { | |||
| setFiltersCleared(true); | |||
| }; | |||
| const clearFiltersAndApply = () => { | |||
| clear(); | |||
| setFiltersCleared(true); | |||
| @@ -164,6 +170,7 @@ const useOffers = () => { | |||
| queryStringHook, | |||
| allOffersToShow, | |||
| totalOffers, | |||
| applyFilters, | |||
| clearFiltersAndApply, | |||
| clearOnlyFiltersAndApply, | |||
| apply, | |||
| @@ -87,7 +87,6 @@ const Register = () => { | |||
| registerUser({ ...informations, ...values }); | |||
| } | |||
| } | |||
| console.log({...informations, ...values}) | |||
| setInformations({ ...informations, ...values }); | |||
| }; | |||
| @@ -19,7 +19,12 @@ const initialState = { | |||
| sortOption: null, | |||
| isApplied: false, | |||
| queryString: "", | |||
| headerString: "", | |||
| headerString: { | |||
| categoryString: "", | |||
| subcategoryString: "", | |||
| locationsString: "", | |||
| text: "" | |||
| }, | |||
| searchString: "" | |||
| }, | |||
| }; | |||
| @@ -1,53 +1,82 @@ | |||
| import { format } from 'date-fns'; | |||
| // import { enUS } from 'date-fns/locale'; | |||
| import i18next from 'i18next'; | |||
| import { format } from "date-fns"; | |||
| import { enUS, sr } from "date-fns/locale"; | |||
| import i18next from "i18next"; | |||
| export function formatDate(date, fmt = 'MM/dd/y', locale) { | |||
| export function formatDate(date, fmt = "MM/dd/y", locale = sr) { | |||
| const dt = new Date(date); | |||
| return format(dt, fmt, { locale }); | |||
| } | |||
| export function formatDateTime(date) { | |||
| export function formatDateTime(date, locale = enUS) { | |||
| const dt = new Date(date); | |||
| return format(dt, 'MM.dd.y hh:mm'); | |||
| return format(dt, "MM.dd.y hh:mm"), { locale }; | |||
| } | |||
| export function getDateDay(date) { | |||
| const dt = new Date(date); | |||
| return format(dt, 'dd'); | |||
| return format(dt, "dd"); | |||
| } | |||
| export function getDateMonth(date) { | |||
| const dt = new Date(date); | |||
| return format(dt, 'MM'); | |||
| return format(dt, "MM"); | |||
| } | |||
| export function getDateYear(date) { | |||
| const dt = new Date(date); | |||
| return format(dt, 'y'); | |||
| return format(dt, "y"); | |||
| } | |||
| export function formatDateTimeLocale(date) { | |||
| const dt = new Date(date); | |||
| return format(dt, 'MM/dd/y hh:mm aa'); | |||
| const dayCreated = | |||
| date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); | |||
| const monthCreated = | |||
| date.getMonth() < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; | |||
| const yearCreated = date.getFullYear(); | |||
| const hourCreated = | |||
| date.getHours() < 10 ? "0" + date.getHours() : date.getHours(); | |||
| const minutesCreated = | |||
| date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(); | |||
| return `${dayCreated}.${monthCreated}.${yearCreated} ${hourCreated}:${minutesCreated}`; | |||
| } | |||
| // TODO add locale | |||
| export function formatDateRange(dates) { | |||
| const start = formatDate(dates.start); | |||
| const end = formatDate(dates.end); | |||
| return i18next.t('common.date.range', { start, end }); | |||
| return i18next.t("common.date.range", { start, end }); | |||
| } | |||
| export function formatDateLocale(date) { | |||
| const dayCreated = | |||
| date.getDate() < 10 | |||
| ? "0" + date.getDate() | |||
| : date.getDate(); | |||
| date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); | |||
| const monthCreated = | |||
| date.getMonth() < 10 | |||
| ? "0" + (date.getMonth() + 1) | |||
| : date.getMonth() + 1; | |||
| date.getMonth() < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; | |||
| const yearCreated = date.getFullYear(); | |||
| return `${dayCreated}.${monthCreated}.${yearCreated}`; | |||
| } | |||
| export function convertUTCDateToLocalDate(date) { | |||
| var newDate = new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000); | |||
| var offset = date.getTimezoneOffset() / 60; | |||
| var hours = date.getHours(); | |||
| newDate.setHours(hours - offset); | |||
| return newDate; | |||
| } | |||
| export function convertLocalDateToUTCDate(date) { | |||
| var newDate = new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000); | |||
| var offset = date.getTimezoneOffset() / 60; | |||
| var hours = date.getHours(); | |||
| newDate.setHours(hours + offset); | |||
| return newDate; | |||
| } | |||
| export function getMessageDate(date) { | |||
| const blankTime = new Date(date.toISOString()); | |||
| const newDate = convertUTCDateToLocalDate(blankTime); | |||
| return formatDateTimeLocale(newDate); | |||
| } | |||
| @@ -185,13 +185,15 @@ export const getQueryObjectHelper = (queryString) => { | |||
| }; | |||
| export const makeHeaderStringHelper = (filters) => { | |||
| let headerStringLocal = ALL_CATEGORIES; | |||
| let categoryString = `${ALL_CATEGORIES}`; | |||
| let subcategoryString = ""; | |||
| let locationsString = ""; | |||
| // Adding category to header string | |||
| if (filters?.category?.selectedCategory?.name) { | |||
| headerStringLocal = filters.category.selectedCategory?.name; | |||
| categoryString = filters.category.selectedCategory?.name; | |||
| // Adding subcategories to header string | |||
| if (filters.subcategory.selectedSubcategory?.name) { | |||
| headerStringLocal += `${SPREAD}${filters.subcategory.selectedSubcategory.name}`; | |||
| subcategoryString = `${SPREAD}${filters.subcategory.selectedSubcategory.name}`; | |||
| } | |||
| } | |||
| // Adding locations to header string | |||
| @@ -199,18 +201,59 @@ export const makeHeaderStringHelper = (filters) => { | |||
| filters?.locations?.selectedLocations && | |||
| filters?.locations?.selectedLocations?.length > 0 | |||
| ) { | |||
| headerStringLocal += SPREAD; | |||
| locationsString = SPREAD; | |||
| filters.locations.selectedLocations.forEach((location, index) => { | |||
| // Checking if item is last | |||
| if (index + 1 === filters.locations.selectedLocations.length) { | |||
| headerStringLocal += location.city; | |||
| locationsString += location.city; | |||
| } else { | |||
| headerStringLocal += location.city + COMMA; | |||
| locationsString += location.city + COMMA; | |||
| } | |||
| }); | |||
| } | |||
| return headerStringLocal; | |||
| let headerStringLocal = categoryString + subcategoryString + locationsString; | |||
| return { | |||
| categoryString, | |||
| subcategoryString, | |||
| locationsString, | |||
| text: headerStringLocal, | |||
| }; | |||
| }; | |||
| export const makeHeaderStringFromQueryObjectHelper = (queryObject) => { | |||
| new URLSearchParams().get; | |||
| let categoryString = `${ALL_CATEGORIES}`; | |||
| let subcategoryString = ""; | |||
| let locationsString = ""; | |||
| if (KEY_CATEGORY in queryObject) { | |||
| categoryString = queryObject[KEY_CATEGORY]; | |||
| if (KEY_SUBCATEGORY in queryObject) { | |||
| subcategoryString = `${SPREAD}${queryObject[KEY_SUBCATEGORY]}`; | |||
| } | |||
| } | |||
| if (KEY_LOCATION in queryObject) { | |||
| locationsString = SPREAD; | |||
| if (!Array.isArray(queryObject[KEY_LOCATION])) { | |||
| locationsString += queryObject[KEY_LOCATION]; | |||
| } else { | |||
| queryObject[KEY_LOCATION].forEach((location, index) => { | |||
| // Checking if item is last | |||
| if (index + 1 === queryObject[KEY_LOCATION].length) { | |||
| locationsString += location; | |||
| } else { | |||
| locationsString += location + COMMA; | |||
| } | |||
| }); | |||
| } | |||
| } | |||
| let headerStringLocal = categoryString + subcategoryString + locationsString; | |||
| return { | |||
| categoryString, | |||
| subcategoryString, | |||
| locationsString, | |||
| text: headerStringLocal, | |||
| }; | |||
| }; | |||
| export const makeQueryStringHelper = (filters, paging, search, sorting) => { | |||
| const newQueryString = new URLSearchParams(); | |||
| @@ -268,4 +311,4 @@ export const removePageAndSizeHelper = (queryString) => { | |||
| if (newQueryString.has(KEY_PAGE)) newQueryString.delete(KEY_PAGE); | |||
| if (newQueryString.has(KEY_SIZE)) newQueryString.delete(KEY_SIZE); | |||
| return newQueryString.toString(); | |||
| } | |||
| }; | |||