| @@ -0,0 +1,11 @@ | |||
| <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <g clip-path="url(#clip0_3817_12834)"> | |||
| <rect width="18" height="18" rx="9" fill="white"/> | |||
| <path d="M0 9C0 4.02891 4.02891 0 9 0C13.9711 0 18 4.02891 18 9C18 13.9711 13.9711 18 9 18C4.02891 18 0 13.9711 0 9ZM13.0711 7.44609C13.4543 7.06289 13.4543 6.43711 13.0711 6.05391C12.6879 5.6707 12.0621 5.6707 11.6789 6.05391L7.875 9.85781L6.32109 8.30391C5.93789 7.9207 5.31211 7.9207 4.92891 8.30391C4.5457 8.68711 4.5457 9.31289 4.92891 9.69609L7.17891 11.9461C7.56211 12.3293 8.18789 12.3293 8.57109 11.9461L13.0711 7.44609Z" fill="#0FC136"/> | |||
| </g> | |||
| <defs> | |||
| <clipPath id="clip0_3817_12834"> | |||
| <rect width="18" height="18" rx="9" fill="white"/> | |||
| </clipPath> | |||
| </defs> | |||
| </svg> | |||
| @@ -75,17 +75,37 @@ const ProfileCard = (props) => { | |||
| dispatch(fetchProfileOffers(profileId)); | |||
| }; | |||
| let numberOfExchanges = | |||
| profile?.statistics?.exchanges?.failedExchanges + | |||
| profile?.statistics?.exchanges?.succeededExchanges; | |||
| let percentOfSucceededExchanges; | |||
| if (profile?.statistics?.exchanges?.succeeded === 0) { | |||
| if (profile?.statistics?.exchanges?.succeededExchanges === 0) { | |||
| percentOfSucceededExchanges = 0; | |||
| } else { | |||
| percentOfSucceededExchanges = Math.ceil( | |||
| (profile?.statistics?.exchanges?.total / | |||
| profile?.statistics?.exchanges?.succeeded) * | |||
| ((profile?.statistics?.exchanges?.succeededExchanges + | |||
| profile?.statistics?.exchanges?.failedExchanges) / | |||
| profile?.statistics?.exchanges?.succeededExchanges) * | |||
| 100 | |||
| ); | |||
| } | |||
| let percentOfSucceededCommunication; | |||
| if (profile?.statistics?.exchanges?.succeededCommunication === 0) { | |||
| percentOfSucceededCommunication = 0; | |||
| } else { | |||
| percentOfSucceededCommunication = Math.ceil( | |||
| ((profile?.statistics?.exchanges?.succeededCommunication + | |||
| profile?.statistics?.exchanges?.failedCommunication) / | |||
| profile?.statistics?.exchanges?.succeededCommunication) * | |||
| 100 | |||
| ); | |||
| } | |||
| let verifiedUser = | |||
| numberOfExchanges >= 20 && percentOfSucceededCommunication >= 90; | |||
| return ( | |||
| <> | |||
| {isLoading ? ( | |||
| @@ -135,6 +155,7 @@ const ProfileCard = (props) => { | |||
| profile={profile} | |||
| isMyProfile={isMyProfile} | |||
| isBlocked={!props.isAdmin && profile?._blocked} | |||
| verifiedUser={verifiedUser} | |||
| /> | |||
| {/* Profile Contact */} | |||
| <ProfileContact | |||
| @@ -149,8 +170,12 @@ const ProfileCard = (props) => { | |||
| {/* Profile Stats */} | |||
| <ProfileStats | |||
| profile={profile} | |||
| numberOfExchanges={numberOfExchanges} | |||
| percentOfSucceededExchanges={percentOfSucceededExchanges} | |||
| isBlocked={!props.isAdmin && profile?._blocked} | |||
| percentOfSucceededCommunication={ | |||
| percentOfSucceededCommunication | |||
| } | |||
| /> | |||
| </ProfileInfoContainer> | |||
| </ProfileCardWrapper> | |||
| @@ -9,6 +9,7 @@ import { | |||
| ProfilePIBContainer, | |||
| PocketIcon, | |||
| ProfilePIB, | |||
| VerifiedUserContainer, | |||
| // BlockedProfileText, | |||
| } from "./ProfileMainInfo.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| @@ -21,6 +22,8 @@ import { | |||
| } from "../../../../util/helpers/routeHelpers"; | |||
| import { ADMIN_SINGLE_USER_PAGE } from "../../../../constants/pages"; | |||
| import BlockedProfile from "../BlockedProfile/BlockedProfile"; | |||
| import { ReactComponent as VerifiedIcon } from "../../../../assets/images/svg/verified-user.svg"; | |||
| import { Tooltip } from "@mui/material"; | |||
| const ProfileMainInfo = (props) => { | |||
| const { t } = useTranslation(); | |||
| @@ -64,6 +67,13 @@ const ProfileMainInfo = (props) => { | |||
| onClick={goToUser} | |||
| > | |||
| {props.profile?.company?.name} | |||
| {props.verifiedUser && ( | |||
| <Tooltip title={t("profile.verifiedTooltip")} placement="right"> | |||
| <VerifiedUserContainer> | |||
| <VerifiedIcon /> | |||
| </VerifiedUserContainer> | |||
| </Tooltip> | |||
| )} | |||
| </ProfileName> | |||
| <ProfilePIBContainer> | |||
| <PocketIcon /> | |||
| @@ -87,6 +97,7 @@ ProfileMainInfo.propTypes = { | |||
| isAdmin: PropTypes.any, | |||
| bigProfileCard: PropTypes.bool, | |||
| isBlocked: PropTypes.bool, | |||
| verifiedUser: PropTypes.bool, | |||
| }; | |||
| ProfileMainInfo.defaultProps = { | |||
| isAdmin: false, | |||
| @@ -145,3 +145,7 @@ export const ProfilePIB = styled(Typography)` | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const VerifiedUserContainer = styled.span` | |||
| margin-left: 9px; | |||
| `; | |||
| @@ -16,24 +16,25 @@ const ProfileStats = (props) => { | |||
| isBlocked={props.isBlocked} | |||
| > | |||
| <ProfileStatsGrid> | |||
| <StatsItem variant="subtitle2"> | |||
| {/* <StatsItem variant="subtitle2"> | |||
| <b>{props.profile?.statistics?.publishes?.count}</b> | |||
| {t("profile.publishes")} | |||
| </StatsItem> | |||
| </StatsItem> */} | |||
| <StatsItem variant="subtitle2"> | |||
| <b>{props.percentOfSucceededExchanges}%</b> | |||
| <b>{props.numberOfExchanges}</b> | |||
| {t("profile.successExchange")} | |||
| <b>({props.percentOfSucceededExchanges}%)</b> | |||
| </StatsItem> | |||
| </ProfileStatsGrid> | |||
| <ProfileStatsGrid> | |||
| <StatsItem variant="subtitle2"> | |||
| {/* <StatsItem variant="subtitle2"> | |||
| <b>{props.profile?.statistics?.views?.count}</b> | |||
| {t("profile.numberOfViews")} | |||
| </StatsItem> | |||
| </StatsItem> */} | |||
| <StatsItem variant="subtitle2"> | |||
| <b>{props.percentOfSucceededExchanges}%</b> | |||
| {t("profile.successComunication")} | |||
| <b>{props.percentOfSucceededCommunication}%</b> | |||
| </StatsItem> | |||
| </ProfileStatsGrid> | |||
| </ProfileStatsContainer> | |||
| @@ -46,6 +47,8 @@ ProfileStats.propTypes = { | |||
| className: PropTypes.string, | |||
| twoRows: PropTypes.bool, | |||
| isBlocked: PropTypes.bool, | |||
| numberOfExchanges: PropTypes.number, | |||
| percentOfSucceededCommunication: PropTypes.number, | |||
| }; | |||
| export default ProfileStats; | |||
| @@ -32,6 +32,8 @@ import { | |||
| PROFILE_PAGE, | |||
| } from "../../../constants/pages"; | |||
| import { NEW_CHAT } from "../../../constants/chatConstants"; | |||
| import { VerifiedUserContainer } from "../../Cards/ProfileCard/ProfileMainInfo/ProfileMainInfo.styled"; | |||
| import { ReactComponent as VerifiedIcon } from "../../../assets/images/svg/verified-user.svg"; | |||
| const ItemDetailsHeaderCard = (props) => { | |||
| const history = useHistory(); | |||
| @@ -102,6 +104,13 @@ const ItemDetailsHeaderCard = (props) => { | |||
| <OfferDetails> | |||
| <OfferTitle isMyProfile={props.isMyProfile} onClick={handleGoProfile}> | |||
| {offer?.user?.company?.name} | |||
| {props.verify && props.verifiedUser && ( | |||
| <Tooltip title={t("profile.verifiedTooltip")} placement="right"> | |||
| <VerifiedUserContainer> | |||
| <VerifiedIcon /> | |||
| </VerifiedUserContainer> | |||
| </Tooltip> | |||
| )} | |||
| </OfferTitle> | |||
| <PIBDetail offer={offer} isMyProfile={props.isMyProfile} /> | |||
| <CategoryDetail offer={offer} isMyProfile={props.isMyProfile} /> | |||
| @@ -143,6 +152,8 @@ ItemDetailsHeaderCard.propTypes = { | |||
| isMyProfile: PropTypes.bool, | |||
| singleOffer: PropTypes.bool, | |||
| isAdmin: PropTypes.bool, | |||
| verify: PropTypes.bool, | |||
| verifiedUser: PropTypes.bool, | |||
| }; | |||
| ItemDetailsHeaderCard.defaultProps = { | |||
| halfwidth: false, | |||
| @@ -17,7 +17,11 @@ const LinkPopover = (props) => { | |||
| let urlLink = linkValue.trim(); | |||
| if (urlLink.startsWith("http://") || urlLink.startsWith("https://")) | |||
| props?.callbackFunction(urlLink); | |||
| else props?.callbackFunction(`http://${urlLink}`); | |||
| else | |||
| props?.callbackFunction({ | |||
| url: `http://${urlLink}`, | |||
| link: urlLink, | |||
| }); | |||
| }; | |||
| return ( | |||
| @@ -26,6 +26,38 @@ const ProfileMini = () => { | |||
| if (offer?.offer?.userId?.toString() === userId?.toString()) return true; | |||
| return false; | |||
| }, [offer, userId]); | |||
| let numberOfExchanges = | |||
| offer?.user?.statistics?.exchanges?.failedExchanges + | |||
| offer?.user?.statistics?.exchanges?.succeededExchanges; | |||
| let percentOfSucceededExchanges; | |||
| if (offer?.user?.statistics?.exchanges?.succeededExchanges === 0) { | |||
| percentOfSucceededExchanges = 0; | |||
| } else { | |||
| percentOfSucceededExchanges = Math.ceil( | |||
| ((offer?.user?.statistics?.exchanges?.succeededExchanges + | |||
| offer?.user?.statistics?.exchanges?.failedExchanges) / | |||
| offer?.user?.statistics?.exchanges?.succeededExchanges) * | |||
| 100 | |||
| ); | |||
| } | |||
| let percentOfSucceededCommunication; | |||
| if (offer?.user?.statistics?.exchanges?.succeededCommunication === 0) { | |||
| percentOfSucceededCommunication = 0; | |||
| } else { | |||
| percentOfSucceededCommunication = Math.ceil( | |||
| ((offer?.user?.statistics?.exchanges?.succeededCommunication + | |||
| offer?.user?.statistics?.exchanges?.failedCommunication) / | |||
| offer?.user?.statistics?.exchanges?.succeededCommunication) * | |||
| 100 | |||
| ); | |||
| } | |||
| let verifiedUser = | |||
| numberOfExchanges >= 20 && percentOfSucceededCommunication >= 90; | |||
| return ( | |||
| <> | |||
| {isLoadingOfferContent || isLoadingOfferContent === undefined ? ( | |||
| @@ -44,12 +76,14 @@ const ProfileMini = () => { | |||
| offer={offer} | |||
| isMyProfile={isMyProfile} | |||
| singleOffer | |||
| verify | |||
| verifiedUser={verifiedUser} | |||
| /> | |||
| <ProfileMiniStats | |||
| profile={offer.companyData} | |||
| percentOfSucceededExchanges={ | |||
| offer.companyData?.statistics?.exchanges?.total | |||
| } | |||
| // profile={profile} | |||
| numberOfExchanges={numberOfExchanges} | |||
| percentOfSucceededExchanges={percentOfSucceededExchanges} | |||
| percentOfSucceededCommunication={percentOfSucceededCommunication} | |||
| isMyProfile={isMyProfile} | |||
| /> | |||
| </ProfileHeader> | |||
| @@ -8,8 +8,14 @@ import LinkPopover from "../../Popovers/LinkPopover/LinkPopover"; | |||
| import PopoverComponent from "../../Popovers/PopoverComponent"; | |||
| const toggleMark = (editor, format, link) => { | |||
| Editor.addMark(editor, format, link); | |||
| Editor.addMark(editor, format, link.url); | |||
| if ( | |||
| (editor?.selection && | |||
| Editor.string(editor, editor.selection).length === 0) || | |||
| !editor?.selection | |||
| ) { | |||
| Editor.insertNode(editor, { text: link.link, a: link.url }); | |||
| } | |||
| }; | |||
| const LinkButton = (props) => { | |||
| @@ -17,9 +23,9 @@ const LinkButton = (props) => { | |||
| const [isLinkPopoverShowing, setIsLinkPopoverShowing] = useState(false); | |||
| const [linkPopoverAnchor, setLinkPopoverAnchor] = useState(null); | |||
| const callbackFunction = (link) => { | |||
| const callbackFunction = (linkObject) => { | |||
| setIsLinkPopoverShowing(false); | |||
| toggleMark(editor, "a", link); | |||
| toggleMark(editor, "a", linkObject); | |||
| }; | |||
| const handleClickLinkButton = (event) => { | |||
| setIsLinkPopoverShowing(true); | |||
| @@ -72,6 +72,7 @@ const RichTextComponent = (props) => { | |||
| Editor.removeMark(editor, "a"); | |||
| } | |||
| } | |||
| console.log(newValue); | |||
| if (props?.onChange) props?.onChange(JSON.stringify(newValue)); | |||
| else setValue(newValue); | |||
| }} | |||
| @@ -316,9 +316,9 @@ export default { | |||
| myProfile: "Moj profil", | |||
| PIB: "PIB:", | |||
| publishes: " objava", | |||
| successExchange: " uspešna trampa", | |||
| successExchange: " uspešnih trampi ", | |||
| numberOfViews: " ukupnih pregleda", | |||
| successComunication: " korektna komunikacija", | |||
| successComunication: "Procenat uspešne komunikacije ", | |||
| back: "Nazad na objave", | |||
| companyProfile: "Profil kompanije", | |||
| noOffers: { | |||
| @@ -334,6 +334,7 @@ export default { | |||
| deletedProfile: "Obrisan profil", | |||
| admin: "Administrator", | |||
| adminPanel: "Admin Panel", | |||
| verifiedTooltip: "Pouzdana saradnja", | |||
| }, | |||
| about: { | |||
| header: { | |||