| @@ -6559,6 +6559,11 @@ | |||
| "es6-symbol": "^3.1.1" | |||
| } | |||
| }, | |||
| "es6-promise": { | |||
| "version": "4.2.8", | |||
| "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", | |||
| "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" | |||
| }, | |||
| "es6-symbol": { | |||
| "version": "3.1.3", | |||
| "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", | |||
| @@ -14130,6 +14135,18 @@ | |||
| "whatwg-fetch": "^3.4.1" | |||
| } | |||
| }, | |||
| "react-autosuggest": { | |||
| "version": "10.1.0", | |||
| "resolved": "https://registry.npmjs.org/react-autosuggest/-/react-autosuggest-10.1.0.tgz", | |||
| "integrity": "sha512-/azBHmc6z/31s/lBf6irxPf/7eejQdR0IqnZUzjdSibtlS8+Rw/R79pgDAo6Ft5QqCUTyEQ+f0FhL+1olDQ8OA==", | |||
| "requires": { | |||
| "es6-promise": "^4.2.8", | |||
| "prop-types": "^15.7.2", | |||
| "react-themeable": "^1.1.0", | |||
| "section-iterator": "^2.0.0", | |||
| "shallow-equal": "^1.2.1" | |||
| } | |||
| }, | |||
| "react-dev-utils": { | |||
| "version": "11.0.4", | |||
| "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", | |||
| @@ -14549,6 +14566,21 @@ | |||
| "resolved": "https://registry.npmjs.org/react-singleton-hook/-/react-singleton-hook-3.4.0.tgz", | |||
| "integrity": "sha512-eQEpyacGAaRejmWUizUdNNQFn5AO0iaKRSl1jxgC0FQadVY/I1WFuPrYiutglPzO9s8yEbIh95UXVJQel4d7HQ==" | |||
| }, | |||
| "react-themeable": { | |||
| "version": "1.1.0", | |||
| "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz", | |||
| "integrity": "sha512-kl5tQ8K+r9IdQXZd8WLa+xxYN04lLnJXRVhHfdgwsUJr/SlKJxIejoc9z9obEkx1mdqbTw1ry43fxEUwyD9u7w==", | |||
| "requires": { | |||
| "object-assign": "^3.0.0" | |||
| }, | |||
| "dependencies": { | |||
| "object-assign": { | |||
| "version": "3.0.0", | |||
| "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", | |||
| "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==" | |||
| } | |||
| } | |||
| }, | |||
| "react-toastify": { | |||
| "version": "9.0.3", | |||
| "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.0.3.tgz", | |||
| @@ -15466,6 +15498,11 @@ | |||
| "ajv-keywords": "^3.5.2" | |||
| } | |||
| }, | |||
| "section-iterator": { | |||
| "version": "2.0.0", | |||
| "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz", | |||
| "integrity": "sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ==" | |||
| }, | |||
| "select-hose": { | |||
| "version": "2.0.0", | |||
| "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", | |||
| @@ -15670,6 +15707,11 @@ | |||
| "safe-buffer": "^5.0.1" | |||
| } | |||
| }, | |||
| "shallow-equal": { | |||
| "version": "1.2.1", | |||
| "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", | |||
| "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" | |||
| }, | |||
| "shallowequal": { | |||
| "version": "1.1.0", | |||
| "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", | |||
| @@ -26,6 +26,7 @@ | |||
| "owasp-password-strength-test": "^1.3.0", | |||
| "query-string": "^7.1.1", | |||
| "react": "^17.0.2", | |||
| "react-autosuggest": "^10.1.0", | |||
| "react-dom": "^17.0.2", | |||
| "react-helmet-async": "^1.0.9", | |||
| "react-i18next": "^11.10.0", | |||
| @@ -15,16 +15,27 @@ import "react-toastify/dist/ReactToastify.css"; | |||
| const URL = "https://trampa-api-test.dilig.net/"; | |||
| const socket = io(URL, {autoConnect: true}); | |||
| const App = () => { | |||
| console.log(io) | |||
| console.log(socket); | |||
| const [isConnected, setIsConnected] = useState(socket.connected); | |||
| const [lastPong, setLastPong] = useState(null); | |||
| console.log(); | |||
| useEffect(() => { | |||
| socket.auth = { | |||
| // userId: "62de57c6dff6f986e43d14ec", | |||
| userId: "62de5844dff6f986e43d14f6", | |||
| sessionID: localStorage.getItem("sessionID"), | |||
| }; | |||
| socket.on("connect", (client) => { | |||
| console.log(client); | |||
| console.log("client: ", client); | |||
| setIsConnected(true); | |||
| }); | |||
| socket.on("session", ({ sessionID, userID }) => { | |||
| localStorage.setItem("sessionID", sessionID); | |||
| localStorage.setItem("userID", userID); | |||
| console.log("sessionID: ", sessionID); | |||
| console.log("userID: ", userID); | |||
| }); | |||
| // socket.on("connect_error", (err) => { | |||
| // console.log(err); | |||
| // }); | |||
| @@ -37,19 +48,25 @@ const App = () => { | |||
| setIsConnected(false); | |||
| }); | |||
| socket.on("user disconnected", (userID) => { | |||
| console.log(userID); | |||
| }); | |||
| // socket.on('emit', (client) => { | |||
| // console.log(client); | |||
| // }) | |||
| socket.on("sokkk", (clg) => { | |||
| console.log(clg); | |||
| }) | |||
| }); | |||
| // socket.onAny((event, ...args) => { | |||
| // console.log(event, args); | |||
| // }); | |||
| socket.on('povratna', (data) => { | |||
| console.log(data) | |||
| }) | |||
| socket.on("povratna", (data) => { | |||
| console.log(data); | |||
| }); | |||
| socket.on("private_message", (data) => { | |||
| console.log(data); | |||
| }); | |||
| // socket.open; | |||
| @@ -71,10 +88,18 @@ const App = () => { | |||
| // socket.emit("sock") | |||
| }; | |||
| const sendPing = () => { | |||
| socket.emit("sokkk", { | |||
| poruka: "Za Duleta" | |||
| socket.emit("private_message", { | |||
| text: "Probica", | |||
| // toUserId: "62de5844dff6f986e43d14f6", | |||
| toUserId: "62de57c6dff6f986e43d14ec", | |||
| chatId: "62eb8424632e1112ef467750", | |||
| }); | |||
| }; | |||
| const disconnect = () => { | |||
| // socket.disconnect(); | |||
| socket.disconnect(); | |||
| }; | |||
| console.log(socket); | |||
| return ( | |||
| <Router history={history}> | |||
| <Helmet> | |||
| @@ -84,14 +109,16 @@ const App = () => { | |||
| <Header /> | |||
| <GlobalStyle /> | |||
| <ToastContainer /> | |||
| {/* <div> | |||
| {/* <div style={{ position: "relative", top: "100px", left: "400px" }}> | |||
| <p>Connected: {"" + isConnected}</p> | |||
| <br /> | |||
| <p>Last pong: {lastPong || "-"}</p> | |||
| <br /> | |||
| <button onClick={sendPing}>Send ping</button> | |||
| <br /> | |||
| <button onClick={disconnect}>Disconnect</button> | |||
| </div> */} | |||
| <AppRoutes /> | |||
| <AppRoutes /> | |||
| </StyledEngineProvider> | |||
| </Router> | |||
| ); | |||
| @@ -2,6 +2,7 @@ import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| HeaderPopoverContainer, | |||
| NameOfProduct, | |||
| PopoverButton, | |||
| PopoverButtonsContainer, | |||
| PopoverList, | |||
| @@ -12,6 +13,8 @@ import { | |||
| PopoverListItemTextContainer, | |||
| PopoverNoItemsText, | |||
| PopoverTitle, | |||
| SecondaryText, | |||
| SecondaryTextContainer, | |||
| } from "./HeaderPopover.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| @@ -44,7 +47,12 @@ const HeaderPopover = (props) => { | |||
| onClick: item.onClick, | |||
| }} | |||
| primary={item.title} | |||
| secondary={item.text} | |||
| secondary={ | |||
| <SecondaryTextContainer> | |||
| <SecondaryText>{item.text}</SecondaryText> | |||
| <NameOfProduct>{item?.bigText}</NameOfProduct> | |||
| </SecondaryTextContainer> | |||
| } | |||
| ></PopoverListItemTextContainer> | |||
| </PopoverListItem> | |||
| )) | |||
| @@ -95,6 +103,7 @@ HeaderPopover.propTypes = { | |||
| buttonOnClick: PropTypes.func, | |||
| secondButtonOnClick: PropTypes.func, | |||
| hideButtons: PropTypes.bool, | |||
| bigText: PropTypes.string, | |||
| }; | |||
| export default HeaderPopover; | |||
| @@ -85,3 +85,21 @@ export const PopoverNoItemsText = styled(Typography)` | |||
| font-size: 13px; | |||
| font-family: "DM Sans"; | |||
| `; | |||
| export const NameOfProduct = styled(Typography)` | |||
| font-size: 12px; | |||
| font-weight: 700; | |||
| font-family: "DM Sans"; | |||
| letter-spacing: 0.02em; | |||
| color: ${selectedTheme.primaryDarkText}; | |||
| `; | |||
| export const SecondaryTextContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| gap: 4px; | |||
| `; | |||
| export const SecondaryText = styled(Typography)` | |||
| font-family: "DM Sans"; | |||
| font-size: 9px; | |||
| letter-spacing: 0.01em; | |||
| color: ${selectedTheme.primaryDarkText}; | |||
| `; | |||
| @@ -17,12 +17,14 @@ export const MyMessages = (props) => { | |||
| const [lastChats, setLastChats] = useState([]); | |||
| const convertMessages = (messages) => { | |||
| console.log(messages) | |||
| return messages | |||
| .map((item) => ({ | |||
| src: item.interlocutorData.image, | |||
| title: item.interlocutorData.name, | |||
| onClick: () => goToMessage(item?.chat?._id), | |||
| text: item?.chat?.messages[item?.chat?.messages?.length - 1]?.text, | |||
| text: "Proizvod: ", | |||
| bigText: item.offerData.name | |||
| })) | |||
| .slice(0, 2); | |||
| }; | |||
| @@ -0,0 +1,73 @@ | |||
| import React, { useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import Autosuggest from "react-autosuggest"; | |||
| import { AutoSuggestTextFieldContainer } from "./AutoSuggestTextField.styled"; | |||
| const escapeRegexCharacters = (str) => | |||
| str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | |||
| const AutoSuggestTextField = (props) => { | |||
| const [suggestions, setSuggestions] = useState([]); | |||
| const data = [...props.data]; | |||
| const getSuggestions = (value) => { | |||
| const escapedValue = escapeRegexCharacters(value.trim()); | |||
| if (escapedValue === "") { | |||
| return []; | |||
| } | |||
| const regex = new RegExp("^" + escapedValue, "i"); | |||
| const suggestions = data.filter((dataItem) => regex.test(dataItem.name)); | |||
| if (suggestions.length === 0) { | |||
| return [{ isAddNew: true }]; | |||
| } | |||
| return suggestions; | |||
| }; | |||
| const getSuggestionValue = (suggestion) => { | |||
| return suggestion.name; | |||
| }; | |||
| const onSuggestionsFetchRequested = ({ value }) => { | |||
| setSuggestions(getSuggestions(value)); | |||
| }; | |||
| const onSuggestionsClearRequested = () => { | |||
| setSuggestions([]); | |||
| }; | |||
| const renderSuggestion = (suggestion) => { | |||
| return suggestion.name; | |||
| }; | |||
| const inputProps = { | |||
| placeholder: props.placeholder, | |||
| value: props.value, | |||
| onChange: props.onChange, | |||
| }; | |||
| return ( | |||
| <AutoSuggestTextFieldContainer> | |||
| <Autosuggest | |||
| suggestions={suggestions} | |||
| onSuggestionsFetchRequested={onSuggestionsFetchRequested} | |||
| onSuggestionsClearRequested={onSuggestionsClearRequested} | |||
| getSuggestionValue={getSuggestionValue} | |||
| renderSuggestion={renderSuggestion} | |||
| inputProps={inputProps} | |||
| /> | |||
| </AutoSuggestTextFieldContainer> | |||
| ); | |||
| }; | |||
| AutoSuggestTextField.propTypes = { | |||
| children: PropTypes.node, | |||
| placeholder: PropTypes.string, | |||
| value: PropTypes.string, | |||
| onChange: PropTypes.func, | |||
| data: PropTypes.array, | |||
| }; | |||
| export default AutoSuggestTextField; | |||
| @@ -0,0 +1,59 @@ | |||
| import styled from "styled-components"; | |||
| import { Box } from "@mui/material"; | |||
| import selectedTheme from "../../../themes"; | |||
| export const AutoSuggestTextFieldContainer = styled(Box)` | |||
| & .react-autosuggest__container { | |||
| width: 100%; | |||
| height: 48px; | |||
| position: relative; | |||
| z-index: 20; | |||
| & input { | |||
| width: 100%; | |||
| height: 48px; | |||
| padding: 4px 14px; | |||
| border-radius: 4px; | |||
| border: 1px solid rgba(0, 0, 0, 0.23); | |||
| outline-width: 0; | |||
| background-color: initial; | |||
| font-family: "DM Sans"; | |||
| font-size: 16px; | |||
| padding-bottom: 6px; | |||
| } | |||
| /* & input:hover { | |||
| border: 1px solid rgba(0, 0, 0, 0.87); | |||
| } */ | |||
| & input:focus-visible { | |||
| border: 2px solid ${selectedTheme.primaryPurple}; | |||
| } | |||
| & input::placeholder { | |||
| color: rgba(0,0,0, 0.38); | |||
| } | |||
| & div { | |||
| z-index: 3000; | |||
| background-color: ${selectedTheme.primaryBackgroundColor}; | |||
| } | |||
| & div ul { | |||
| border: 1px solid black; | |||
| border-radius: 4px; | |||
| padding: 10px; | |||
| } | |||
| & div ul li { | |||
| padding-left: 16px; | |||
| cursor: pointer; | |||
| height: 40px; | |||
| padding-top: 10px; | |||
| font-family: "DM Sans"; | |||
| border-radius: 4px; | |||
| } | |||
| & div ul li:hover { | |||
| background-color: ${selectedTheme.primaryPurple}; | |||
| color: ${selectedTheme.primaryBackgroundColor}; | |||
| } | |||
| } | |||
| `; | |||
| @@ -101,5 +101,5 @@ TextField.defaultProps = { | |||
| italicPlaceholder: false, | |||
| showAnimation: false, | |||
| height: "48px", | |||
| // font: "DM Sans" | |||
| font: "DM Sans" | |||
| }; | |||
| @@ -4,6 +4,7 @@ import { | |||
| authScopeSetHelper, | |||
| authScopeStringGetHelper, | |||
| } from '../util/helpers/authScopeHelpers'; | |||
| import selectedTheme from '../themes'; | |||
| const useToggleColorMode = () => { | |||
| const currentColorMode = authScopeStringGetHelper('colorMode') || 'light'; | |||
| @@ -19,7 +20,9 @@ const useToggleColorMode = () => { | |||
| () => | |||
| createTheme({ | |||
| palette: { | |||
| mode, | |||
| primary: { | |||
| main: selectedTheme.primaryPurple | |||
| } | |||
| }, | |||
| }), | |||
| [mode] | |||
| @@ -99,6 +99,9 @@ export default { | |||
| welcome: "Dobro došli na trampu, želimo vam uspešno trampovanje!", | |||
| imageError: "Slika je obavezna!", | |||
| serverError: "Greška sa serverom!", | |||
| phoneNumberNoOfCharacters: "Broj telefona mora imati izmedju 6 i 15 karaktera!", | |||
| locationError: "Odaberite ispravnu lokaciju!", | |||
| websiteError: "Unesite ispravnu adresu svog website!" | |||
| }, | |||
| forgotPassword: { | |||
| title: "Povrati lozinku", | |||
| @@ -10,32 +10,39 @@ import { useTranslation } from "react-i18next"; | |||
| import { TextField } from "../../../../components/TextFields/TextField/TextField"; | |||
| import { PrimaryButton } from "../../../../components/Buttons/PrimaryButton/PrimaryButton"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import AutoSuggestTextField from "../../../../components/TextFields/AutoSuggestTextField/AutoSuggestTextField"; | |||
| import { useSelector } from "react-redux"; | |||
| import { selectLocations } from "../../../../store/selectors/locationsSelectors"; | |||
| import { ErrorMessage } from "../FirstPart/FirstPartOfRegistration.styled"; | |||
| const ThirdPartOfRegistration = (props) => { | |||
| const { t } = useTranslation(); | |||
| const locations = useSelector(selectLocations); | |||
| useEffect(() => { | |||
| if (props.informations?.phoneNumber) { | |||
| formik.setFieldValue("phoneNumber", props.informations?.phoneNumber) | |||
| formik.setFieldValue("phoneNumber", props.informations?.phoneNumber); | |||
| } | |||
| if (props.informations?.location) { | |||
| formik.setFieldValue("location", props.informations?.location) | |||
| formik.setFieldValue("location", props.informations?.location); | |||
| } | |||
| if (props.informations?.website) { | |||
| formik.setFieldValue("website", props.informations?.website) | |||
| formik.setFieldValue("website", props.informations?.website); | |||
| } | |||
| }, [props.informations]) | |||
| }, [props.informations]); | |||
| const handleSubmit = () => { | |||
| if (formik.values.website?.length !== 0 && !formik.values.website.match( | |||
| /^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/gm | |||
| )) { | |||
| if ( | |||
| formik.values.website?.length !== 0 && | |||
| !formik.values.website.match( | |||
| /^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/gm | |||
| ) | |||
| ) { | |||
| formik.setFieldError("website"); | |||
| } else { | |||
| props.handleSubmit(formik.values); | |||
| } | |||
| } | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| @@ -45,8 +52,11 @@ const ThirdPartOfRegistration = (props) => { | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| phoneNumber: Yup.number(), | |||
| location: Yup.string(), | |||
| website: Yup.string() | |||
| location: Yup.string().oneOf( | |||
| locations.map((item) => item.city), | |||
| "Greska!!!" | |||
| ), | |||
| website: Yup.string(), | |||
| }), | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| @@ -66,15 +76,13 @@ const ThirdPartOfRegistration = (props) => { | |||
| type="number" | |||
| value={formik.values.phoneNumber} | |||
| onChange={formik.handleChange} | |||
| error={ | |||
| (formik.touched.phoneNumber && Boolean(formik.errors.phoneNumber)) | |||
| } | |||
| error={formik.touched.phoneNumber && Boolean(formik.errors.phoneNumber)} | |||
| helperText={formik.touched.phoneNumber && formik.errors.phoneNumber} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <TextField | |||
| {/* <TextField | |||
| name="location" | |||
| placeholder={t("common.labelLocation")} | |||
| margin="normal" | |||
| @@ -84,6 +92,15 @@ const ThirdPartOfRegistration = (props) => { | |||
| error={formik.touched.location && Boolean(formik.errors.location)} | |||
| helperText={formik.touched.location && formik.errors.location} | |||
| fullWidth | |||
| /> */} | |||
| <AutoSuggestTextField | |||
| placeholder={t("common.labelLocation")} | |||
| data={locations.map((item) => ({ name: item.city }))} | |||
| value={formik.values.location} | |||
| onChange={(event, { newValue }) => | |||
| formik.setFieldValue("location", newValue) | |||
| } | |||
| /> | |||
| <TextField | |||
| @@ -98,6 +115,8 @@ const ThirdPartOfRegistration = (props) => { | |||
| fullWidth | |||
| /> | |||
| <ErrorMessage>{formik.errors.phoneNumber}</ErrorMessage> | |||
| <PrimaryButton | |||
| type="submit" | |||
| variant="contained" | |||