Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"react-redux": "^8.0.4",
"react-redux-toastr": "^7.6.10",
"react-responsive": "^9.0.0-beta.5",
"react-responsive-masonry": "^2.1.7",
"react-responsive-modal": "^6.2.0",
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1",
Expand Down Expand Up @@ -159,6 +160,7 @@
"@types/react-gtm-module": "^2.0.1",
"@types/react-helmet": "^6.1.6",
"@types/react-redux-toastr": "^7.6.2",
"@types/react-responsive-masonry": "^2.1.1",
"@types/react-router-dom": "^5.3.3",
"@types/redux-actions": "2.6.2",
"@types/redux-logger": "^3.0.9",
Expand Down
5 changes: 2 additions & 3 deletions src/apps/onboarding/src/models/MemberInfo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { MemberMaxRating } from '~/apps/talent-search/src/lib/models'
import { MemberStats } from '~/libs/core'
import { Skill } from '~/libs/shared'
import { MemberStats, UserSkill } from '~/libs/core'

import MemberAddress from './MemberAddress'

Expand All @@ -14,7 +13,7 @@ export default interface MemberInfo {
email: string
accountAge: number
maxRating: MemberMaxRating
emsiSkills: Array<Skill>
skills: Array<UserSkill>
stats: Array<MemberStats>
addresses?: MemberAddress[]
country: string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createContext, FC, ReactNode, useContext, useMemo } from 'react'

import { Skill } from '~/libs/shared'
import { UserSkill } from '~/libs/core'

export interface MemberProfileContextValue {
isTalentSearch?: boolean
skillsRenderer?: (
skills: Pick<Skill, 'name'|'id'|'skillSources'>[]
skills: Pick<UserSkill, 'name'|'id'|'levels'>[]
) => ReactNode
}

Expand Down
2 changes: 2 additions & 0 deletions src/apps/profiles/src/member-profile/links/MemberLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export function renderLinkIcon(linkName: string): JSX.Element {
return <GitHubLinkIcon />
case 'Twitter':
return <SocialIconTwitter />
case 'X / Twitter':
return <SocialIconTwitter />
case 'LinkedIn':
return <LinkedInLinkIcon />
case 'Instagram':
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { trim } from 'lodash'
import { FC, forwardRef, ForwardRefExoticComponent, SVGProps, useEffect, useImperativeHandle, useState } from 'react'
import {
FC,
forwardRef,
ForwardRefExoticComponent,
SVGProps,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react'
import classNames from 'classnames'

import { Button, IconOutline, InputSelect, InputText } from '~/libs/ui'

import { linkTypes } from '../link-types.config'
import { additionalLinkTypes } from '../link-types.config'
import { isValidURL } from '../../../../lib'
import { renderLinkIcon } from '../../MemberLinks'

Expand All @@ -24,7 +33,6 @@ interface LinkFormProps {
onRemove?: () => void
removeIcon?: FC<SVGProps<SVGSVGElement>>
hideRemoveIcon?: boolean
allowEmptyUrl?: boolean
labelUrlField?: string
disabled?: boolean
}
Expand All @@ -41,6 +49,8 @@ const LinkForm: ForwardRefExoticComponent<
const [selectedLinkType, setSelectedLinkType] = useState<string | undefined>()
const [selectedLinkURL, setSelectedLinkURL] = useState<string | undefined>()
const [shouldValidateForm, setShouldValidateForm] = useState<boolean>(false)
const canShowTypeError = useRef(false)
const canShowUrlError = useRef(false)

useEffect(() => {
if (shouldValidateForm) {
Expand All @@ -52,37 +62,51 @@ const LinkForm: ForwardRefExoticComponent<
resetForm() {
setShouldValidateForm(false)
setFormErrors({})
canShowTypeError.current = false
canShowUrlError.current = false
},
validateForm() {
canShowTypeError.current = true
canShowUrlError.current = true
handleFormAction()
},
}))

function handleSelectedLinkTypeChange(event: React.ChangeEvent<HTMLInputElement>): void {
canShowTypeError.current = true
setSelectedLinkType(event.target.value)
setShouldValidateForm(true)
}

function handleURLChange(event: React.ChangeEvent<HTMLInputElement>): void {
canShowUrlError.current = true
setSelectedLinkURL(event.target.value)
setShouldValidateForm(true)
}

function handleFormAction(): void {
function getFormError(): boolean {
setFormErrors({})

let isError = false
if (!selectedLinkType) {
setFormErrors({ selectedLinkType: 'Please select a link type' })
return
isError = true
if (canShowTypeError.current) {
setFormErrors({ selectedLinkType: 'Please select a link type' })
}
}

if (!props.allowEmptyUrl && !trim(selectedLinkURL)) {
setFormErrors({ url: 'Please enter a URL' })
return
if (selectedLinkURL && trim(selectedLinkURL) && !isValidURL(selectedLinkURL as string)) {
isError = true
if (canShowUrlError.current) {
setFormErrors({ url: 'Invalid URL' })
}
}

if (selectedLinkURL && !isValidURL(selectedLinkURL as string)) {
setFormErrors({ url: 'Invalid URL' })
return isError
}

function handleFormAction(): void {
if (getFormError()) {
return
}

Expand All @@ -91,14 +115,14 @@ const LinkForm: ForwardRefExoticComponent<
if (absoluteURL.indexOf('://') > 0 || absoluteURL.indexOf('//') === 0) {

props.onSave({
name: selectedLinkType,
name: selectedLinkType ?? '',
url: absoluteURL,
})
} else {
absoluteURL = absoluteURL ? `https://${absoluteURL}` : ''

props.onSave({
name: selectedLinkType,
name: selectedLinkType ?? '',
url: absoluteURL,
})
}
Expand All @@ -124,7 +148,7 @@ const LinkForm: ForwardRefExoticComponent<
<div className={styles.form}>
{props.allowEditType ? (
<InputSelect
options={linkTypes}
options={additionalLinkTypes}
value={selectedLinkType}
onChange={handleSelectedLinkTypeChange}
name='linkType'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { find, findIndex, omit, reject, uniqBy } from 'lodash'
import { FC, useEffect, useRef, useState } from 'react'
import { cloneDeep, findIndex, isEqual, omit, reject, uniqBy } from 'lodash'
import { FC, useEffect, useMemo, useRef, useState } from 'react'
import { toast } from 'react-toastify'
import classNames from 'classnames'

Expand Down Expand Up @@ -28,7 +28,6 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
const inputRef = useRef<HTMLInputElement | any>()

const [isSaving, setIsSaving] = useState<boolean>(false)
const [hasChanges, setHasChanges] = useState<boolean>(false)
const [currentMemberLinks, setCurrentMemberLinks] = useState<UserTrait[]>(
[],
)
Expand All @@ -44,16 +43,33 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
name: 'Instagram',
url: '',
})
const [newLink, setNewLink] = useState<UserTrait>({
const [defaultLink, setDefaultLink] = useState<UserTrait>({
name: '',
url: '',
})

const updatedLinks = useMemo(() => uniqBy(
[
defaultLinkedIn,
defaultGitHub,
defaultInstagram,
defaultLink,
...currentMemberLinks,
].filter(
l => l.name && l.url,
),
e => `${e.name}-${e.url}`,
)
.map(
item => omit(item, ['id']),
), [defaultLinkedIn, defaultGitHub, defaultInstagram, defaultLink, currentMemberLinks])
const hasChanges = useMemo(() => !isEqual(updatedLinks, props.memberLinks), [updatedLinks])

const addNewLinkRef = useRef<LinkFormHandle>(null)

useEffect(() => {
const memberLinks = [
...(props.memberLinks ?? []),
...cloneDeep(props.memberLinks ?? []),
]
const firstLinkedInIndex = findIndex(memberLinks, {
name: 'LinkedIn',
Expand All @@ -76,6 +92,10 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
setDefaultInstagram(memberLinks.splice(firstInstagramIndex, 1)[0])
}

if (memberLinks.length > 0) {
setDefaultLink(memberLinks.splice(0, 1)[0])
}

setCurrentMemberLinks(memberLinks.map((item: UserTrait, index: number) => ({
...item,
id: `id-${index}-${(new Date())
Expand All @@ -85,30 +105,16 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
}, [props.memberLinks])

function handleAddAdditional(): void {
if (newLink.url && newLink.name) {
const updatedLinks: UserTrait[] = uniqBy([
defaultLinkedIn,
defaultGitHub,
defaultInstagram,
...currentMemberLinks,
].filter(l => l.name && l.url), e => `${e.name}-${e.url}`)
if (!find(updatedLinks, newLink)) {
setCurrentMemberLinks(links => [...links, {
...newLink,
id: `id-${(new Date())
.getTime()}`,
}])
}

addNewLinkRef.current?.resetForm()
setNewLink({
name: '',
url: '',
})
setHasChanges(true)
} else {
addNewLinkRef.current?.validateForm()
}
setCurrentMemberLinks(links => [...links, {
id: `id-${(new Date())
.getTime()}`,
...defaultLink,
}])
setDefaultLink({
name: '',
url: '',
})
addNewLinkRef.current?.resetForm()
}

function handleRemoveLink(index: number): void {
Expand All @@ -118,15 +124,12 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
...currentMemberLinks,
],
)
setHasChanges(true)
}

function handleSaveLink(link: UserTrait, index: number): void {
setCurrentMemberLinks(links => (links ?? []).map((l, i) => (
i === index ? link : l
)))

setHasChanges(true)
}

function handleLinksSave(): void {
Expand All @@ -135,21 +138,6 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
const updatedPersonalizationTraits: UserTrait[]
= reject(props.memberPersonalizationTraitsFullData, (trait: UserTrait) => trait.links)

const updatedLinks: UserTrait[] = uniqBy(
[
defaultLinkedIn,
defaultGitHub,
defaultInstagram,
...currentMemberLinks,
].filter(
l => l.name && l.url,
),
e => `${e.name}-${e.url}`,
)
.map(
item => omit(item, ['id']),
)

updateOrCreateMemberTraitsAsync(props.profile.handle, [{
categoryName: UserTraitCategoryNames.personalization,
traitId: UserTraitIds.personalization,
Expand Down Expand Up @@ -204,59 +192,50 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
link={defaultLinkedIn as UserLink}
onSave={function onSave(link: UserLink) {
setDefaultLinkedIn(link)
setHasChanges(true)
}}
onRemove={function onRemove() {
setDefaultLinkedIn({
...defaultLinkedIn,
url: '',
})
setHasChanges(true)
}}
placeholder='Add URL'
removeIcon={IconOutline.XCircleIcon}
hideRemoveIcon={!defaultLinkedIn.url}
allowEmptyUrl
disabled={isSaving}
labelUrlField='Linkedin'
/>
<LinkForm
link={defaultGitHub as UserLink}
onSave={function onSave(link: UserLink) {
setDefaultGitHub(link)
setHasChanges(true)
}}
onRemove={function onRemove() {
setDefaultGitHub({
...defaultGitHub,
url: '',
})
setHasChanges(true)
}}
placeholder='Add URL'
removeIcon={IconOutline.XCircleIcon}
hideRemoveIcon={!defaultGitHub.url}
allowEmptyUrl
disabled={isSaving}
labelUrlField='Git'
/>
<LinkForm
link={defaultInstagram as UserLink}
onSave={function onSave(link: UserLink) {
setDefaultInstagram(link)
setHasChanges(true)
}}
onRemove={function onRemove() {
setDefaultInstagram({
...defaultInstagram,
url: '',
})
setHasChanges(true)
}}
placeholder='Add URL'
removeIcon={IconOutline.XCircleIcon}
hideRemoveIcon={!defaultInstagram.url}
allowEmptyUrl
disabled={isSaving}
labelUrlField='Instagram'
/>
Expand All @@ -265,8 +244,8 @@ const ModifyMemberLinksModal: FC<ModifyMemberLinksModalProps> = (props: ModifyMe
<hr className={styles.spacer} />

<LinkForm
link={newLink as UserLink}
onSave={setNewLink}
link={defaultLink as UserLink}
onSave={setDefaultLink}
allowEditType
placeholder='http://'
ref={addNewLinkRef}
Expand Down
Loading