From 3b35bae393dafa5632d22a9a55e38455a6b6e059 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 8 Jun 2023 15:35:28 +0300 Subject: [PATCH 1/3] TC&You init - tracks --- src/apps/accounts/src/lib/assets/index.ts | 1 + .../src/lib/assets/tcandyou/data_science.svg | 11 +++ .../src/lib/assets/tcandyou/design.svg | 14 +++ .../src/lib/assets/tcandyou/develop.svg | 11 +++ .../accounts/src/lib/assets/tcandyou/index.ts | 9 ++ .../src/settings/tabs/AccountSettingsTabs.tsx | 5 ++ .../config/account-settings-tabs-config.ts | 7 ++ .../tabs/tcandyou/TCandYouTab.module.scss | 12 +++ .../settings/tabs/tcandyou/TCandYouTab.tsx | 21 +++++ .../src/settings/tabs/tcandyou/index.ts | 1 + .../tabs/tcandyou/tracks/Tracks.module.scss | 15 ++++ .../settings/tabs/tcandyou/tracks/Tracks.tsx | 89 +++++++++++++++++++ .../settings/tabs/tcandyou/tracks/index.ts | 1 + src/libs/core/lib/profile/index.ts | 1 + .../core/lib/profile/modify-tracks.request.ts | 5 ++ .../lib/profile/profile-functions/index.ts | 1 + .../profile-store/profile-xhr.store.ts | 5 ++ .../profile-functions/profile.functions.ts | 6 ++ 18 files changed, 215 insertions(+) create mode 100644 src/apps/accounts/src/lib/assets/tcandyou/data_science.svg create mode 100644 src/apps/accounts/src/lib/assets/tcandyou/design.svg create mode 100644 src/apps/accounts/src/lib/assets/tcandyou/develop.svg create mode 100644 src/apps/accounts/src/lib/assets/tcandyou/index.ts create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.module.scss create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/index.ts create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.module.scss create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/tracks/index.ts create mode 100644 src/libs/core/lib/profile/modify-tracks.request.ts diff --git a/src/apps/accounts/src/lib/assets/index.ts b/src/apps/accounts/src/lib/assets/index.ts index 093847926..3dc6095bc 100644 --- a/src/apps/accounts/src/lib/assets/index.ts +++ b/src/apps/accounts/src/lib/assets/index.ts @@ -2,3 +2,4 @@ export * from './security' export * from './preferences' export * from './payments' export * from './tools' +export * from './tcandyou' diff --git a/src/apps/accounts/src/lib/assets/tcandyou/data_science.svg b/src/apps/accounts/src/lib/assets/tcandyou/data_science.svg new file mode 100644 index 000000000..4a5565e0e --- /dev/null +++ b/src/apps/accounts/src/lib/assets/tcandyou/data_science.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/apps/accounts/src/lib/assets/tcandyou/design.svg b/src/apps/accounts/src/lib/assets/tcandyou/design.svg new file mode 100644 index 000000000..2e344e11b --- /dev/null +++ b/src/apps/accounts/src/lib/assets/tcandyou/design.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/apps/accounts/src/lib/assets/tcandyou/develop.svg b/src/apps/accounts/src/lib/assets/tcandyou/develop.svg new file mode 100644 index 000000000..fe60ce444 --- /dev/null +++ b/src/apps/accounts/src/lib/assets/tcandyou/develop.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/apps/accounts/src/lib/assets/tcandyou/index.ts b/src/apps/accounts/src/lib/assets/tcandyou/index.ts new file mode 100644 index 000000000..ce248d51e --- /dev/null +++ b/src/apps/accounts/src/lib/assets/tcandyou/index.ts @@ -0,0 +1,9 @@ +import { ReactComponent as DevelopmentTrackIcon } from './develop.svg' +import { ReactComponent as DesignTrackIcon } from './design.svg' +import { ReactComponent as DataScienceTrackIcon } from './data_science.svg' + +export { + DesignTrackIcon, + DataScienceTrackIcon, + DevelopmentTrackIcon, +} diff --git a/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx b/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx index c2a70aa22..059f420a8 100644 --- a/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx +++ b/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx @@ -9,6 +9,7 @@ import { AccountTab } from './account' import { PreferencesTab } from './preferences' import { PaymentsTab } from './payments' import { ToolsTab } from './tools' +import { TCandYouTab } from './tcandyou' import styles from './AccountSettingsTabs.module.scss' interface AccountSettingsTabsProps { @@ -45,6 +46,10 @@ const AccountSettingsTabs: FC = (props: AccountSetting 'Topcoder'].join(' | ')} + {activeTab === AccountSettingsTabViews.tcandyou && ( + + )} + {activeTab === AccountSettingsTabViews.tools && ( )} diff --git a/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts b/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts index 7f2c468ba..f5e98ebf5 100644 --- a/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts +++ b/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts @@ -5,9 +5,14 @@ export enum AccountSettingsTabViews { account = '1', preferences = '2', payment = '3', + tcandyou = '4', } export const AccountSettingsTabsConfig: TabsNavItem[] = [ + { + id: AccountSettingsTabViews.tcandyou, + title: 'Topcoder & You', + }, { id: AccountSettingsTabViews.tools, title: 'Tools', @@ -28,6 +33,7 @@ export const AccountSettingsTabsConfig: TabsNavItem[] = [ export function getHashFromTabId(tabId: string): string { switch (tabId) { + case AccountSettingsTabViews.tcandyou: return '#tcandyou' case AccountSettingsTabViews.tools: return '#tools' case AccountSettingsTabViews.account: return '#account' case AccountSettingsTabViews.preferences: return '#preferences' @@ -38,6 +44,7 @@ export function getHashFromTabId(tabId: string): string { export function getTabIdFromHash(hash: string): string { switch (hash) { + case '#tcandyu': return AccountSettingsTabViews.tcandyou case '#tools': return AccountSettingsTabViews.tools case '#account': return AccountSettingsTabViews.account case '#preferences': return AccountSettingsTabViews.preferences diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.module.scss b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.module.scss new file mode 100644 index 000000000..67f820d51 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.module.scss @@ -0,0 +1,12 @@ +@import '@libs/ui/styles/includes'; + +.container { + background-color: $black-5; + padding: $sp-6; + margin: $sp-8 0; + border-radius: 6px; + + @include ltelg { + padding: $sp-4; + } +} diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx new file mode 100644 index 000000000..3aaa5cee3 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx @@ -0,0 +1,21 @@ +import { FC } from 'react' + +import { UserProfile, UserTraits } from '~/libs/core' + +import { Tracks } from './tracks' +import styles from './TCandYouTab.module.scss' + +interface TCandYouTabProps { + profile: UserProfile + memberTraits: UserTraits[] | undefined +} + +const TCandYouTab: FC = (props: TCandYouTabProps) => ( +
+

You And Topcoder

+ + +
+) + +export default TCandYouTab diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/index.ts b/src/apps/accounts/src/settings/tabs/tcandyou/index.ts new file mode 100644 index 000000000..ca106cbd3 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/index.ts @@ -0,0 +1 @@ +export { default as TCandYouTab } from './TCandYouTab' diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.module.scss b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.module.scss new file mode 100644 index 000000000..c40b3d3cf --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.module.scss @@ -0,0 +1,15 @@ +@import '@libs/ui/styles/includes'; + +.container { + margin: $sp-8 0; + + .content { + display: flex; + flex-direction: column; + margin-bottom: 0; + + svg { + margin-right: $sp-4; + } + } +} diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx new file mode 100644 index 000000000..264025acf --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx @@ -0,0 +1,89 @@ +import { Dispatch, FC, useState } from 'react' +import { bind } from 'lodash' + +import { TC_TRACKS, UserProfile } from '~/libs/core' +import { Collapsible, FormToggleSwitch } from '~/libs/ui' +import { DataScienceTrackIcon, DesignTrackIcon, DevelopmentTrackIcon, SettingSection } from '~/apps/accounts/src/lib' + +import styles from './Tracks.module.scss' + +interface TracksProps { + profile: UserProfile +} + +const Tracks: FC = (props: TracksProps) => { + const [devTrack, setDevTrack]: [boolean, Dispatch] + = useState(!!props.profile.tracks?.includes('DEVELOP')) + + const [designTrack, setDesignTrack]: [boolean, Dispatch] + = useState(!!props.profile.tracks?.includes('DESIGN')) + + const [dsTrack, setDSTrack]: [boolean, Dispatch] + = useState(!!props.profile.tracks?.includes('DATA_SCIENCE')) + + function handleTracksChange(type: TC_TRACKS): void { + + } + + console.log('devTrack', props) + + return ( + Tracks} + containerClass={styles.container} + contentClass={styles.content} + > +

+ Topcoder's three categories of challenges... + please pick at least one based on your skills and interests. +

+ + + )} + title='Design' + infoText='Website, mobile, and product design; UI and UX' + actionElement={( + + )} + /> + + + )} + title='Development' + infoText='Software architecture, component assembly, application development, and bug hunting' + actionElement={( + + )} + /> + + + )} + title='Data Science' + infoText='Algorithms and data structures, statistical analysis' + actionElement={( + + )} + /> +
+ ) +} + +export default Tracks diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/tracks/index.ts b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/index.ts new file mode 100644 index 000000000..63dc2e4a1 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/index.ts @@ -0,0 +1 @@ +export { default as Tracks } from './Tracks' diff --git a/src/libs/core/lib/profile/index.ts b/src/libs/core/lib/profile/index.ts index 1caca04e0..ec14a045f 100644 --- a/src/libs/core/lib/profile/index.ts +++ b/src/libs/core/lib/profile/index.ts @@ -13,3 +13,4 @@ export * from './user-email-preference.model' export * from './modify-user-email-preferences.model' export * from './modify-user-mfa.model' export * from './user-traits.model' +export * from './modify-tracks.request' diff --git a/src/libs/core/lib/profile/modify-tracks.request.ts b/src/libs/core/lib/profile/modify-tracks.request.ts new file mode 100644 index 000000000..595c67973 --- /dev/null +++ b/src/libs/core/lib/profile/modify-tracks.request.ts @@ -0,0 +1,5 @@ +import { TC_TRACKS } from './user-profile.model' + +export interface ModifyTracksRequest { + tracks: TC_TRACKS[] +} diff --git a/src/libs/core/lib/profile/profile-functions/index.ts b/src/libs/core/lib/profile/profile-functions/index.ts index 7db6438ec..e09c62af1 100644 --- a/src/libs/core/lib/profile/profile-functions/index.ts +++ b/src/libs/core/lib/profile/profile-functions/index.ts @@ -12,6 +12,7 @@ export { updateMemberPasswordAsync, updateMemberTraitsAsync, createMemberTraitsAsync, + modifyTracksAsync, } from './profile.functions' export * from './profile-store' export * from './rating.functions' diff --git a/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts b/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts index cbee64597..26472c350 100644 --- a/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts +++ b/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts @@ -1,6 +1,7 @@ import { xhrGetAsync, xhrPatchAsync, xhrPostAsync, xhrPutAsync } from '../../../xhr' import { CountryLookup } from '../../country-lookup.model' import { EditNameRequest } from '../../edit-name-request.model' +import { ModifyTracksRequest } from '../../modify-tracks.request' import { ModifyMemberEmailPreferencesRequest } from '../../modify-user-email-preferences.model' import { ModifyUserMFARequest, ModifyUserMFAResponse } from '../../modify-user-mfa.model' import { ModifyUserPropertyRequest, ModifyUserPropertyResponse } from '../../modify-user-role.model' @@ -92,3 +93,7 @@ export async function createMemberTraits( ): Promise { return xhrPostAsync(`${profileUrl(handle)}/traits`, traits) } + +export function modifyTracks(handle: string, request: ModifyTracksRequest): Promise { + return xhrPutAsync(profileUrl(handle), request) +} diff --git a/src/libs/core/lib/profile/profile-functions/profile.functions.ts b/src/libs/core/lib/profile/profile-functions/profile.functions.ts index b6c074568..4fab21153 100644 --- a/src/libs/core/lib/profile/profile-functions/profile.functions.ts +++ b/src/libs/core/lib/profile/profile-functions/profile.functions.ts @@ -1,6 +1,7 @@ import { tokenGetAsync, TokenModel, userGetDiceStatusAsync } from '../../auth' import { CountryLookup } from '../country-lookup.model' import { EditNameRequest } from '../edit-name-request.model' +import { ModifyTracksRequest } from '../modify-tracks.request' import { ModifyMemberEmailPreferencesRequest } from '../modify-user-email-preferences.model' import { ModifyUserMFARequest, ModifyUserMFAResponse } from '../modify-user-mfa.model' import { ModifyUserPropertyResponse } from '../modify-user-role.model' @@ -15,6 +16,7 @@ import { getMemberStats, getVerification, profileStoreGet, profileStorePatchName import { createMemberTraits, getCountryLookup, + modifyTracks, updateMemberEmailPreferences, updateMemberMFA, updateMemberPassword, @@ -116,3 +118,7 @@ export async function createMemberTraitsAsync( ): Promise { return createMemberTraits(handle, traits) } + +export async function modifyTracksAsync(handle: string, tracks: ModifyTracksRequest): Promise { + return modifyTracks(handle, tracks) +} From 60eff58c30549ec99f37fd4f75261f1360457da3 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 9 Jun 2023 11:23:53 +0300 Subject: [PATCH 2/3] MP-33 member tracks in settings --- .../config/account-settings-tabs-config.ts | 2 +- .../settings/tabs/tcandyou/TCandYouTab.tsx | 1 + .../settings/tabs/tcandyou/tracks/Tracks.tsx | 55 ++++++++++++++----- src/libs/core/lib/profile/index.ts | 1 + .../lib/profile/modify-user-profile.model.ts | 7 +++ .../profile-context-data.model.ts | 1 + .../profile.context-provider.tsx | 12 ++++ .../profile-context/profile.context.tsx | 1 + .../lib/profile/profile-functions/index.ts | 1 + .../profile-store/profile-xhr.store.ts | 7 ++- .../profile-functions/profile.functions.ts | 6 ++ 11 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 src/libs/core/lib/profile/modify-user-profile.model.ts diff --git a/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts b/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts index f5e98ebf5..fe005a402 100644 --- a/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts +++ b/src/apps/accounts/src/settings/tabs/config/account-settings-tabs-config.ts @@ -44,7 +44,7 @@ export function getHashFromTabId(tabId: string): string { export function getTabIdFromHash(hash: string): string { switch (hash) { - case '#tcandyu': return AccountSettingsTabViews.tcandyou + case '#tcandyou': return AccountSettingsTabViews.tcandyou case '#tools': return AccountSettingsTabViews.tools case '#account': return AccountSettingsTabViews.account case '#preferences': return AccountSettingsTabViews.preferences diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx index 3aaa5cee3..8a88105d9 100644 --- a/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx +++ b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx @@ -7,6 +7,7 @@ import styles from './TCandYouTab.module.scss' interface TCandYouTabProps { profile: UserProfile + // eslint-disable-next-line react/no-unused-prop-types memberTraits: UserTraits[] | undefined } diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx index 264025acf..bb267fead 100644 --- a/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx +++ b/src/apps/accounts/src/settings/tabs/tcandyou/tracks/Tracks.tsx @@ -1,7 +1,8 @@ -import { Dispatch, FC, useState } from 'react' +import { Dispatch, FC, useContext, useEffect, useState } from 'react' import { bind } from 'lodash' +import { toast } from 'react-toastify' -import { TC_TRACKS, UserProfile } from '~/libs/core' +import { profileContext, ProfileContextData, TC_TRACKS, updateMemberProfileAsync, UserProfile } from '~/libs/core' import { Collapsible, FormToggleSwitch } from '~/libs/ui' import { DataScienceTrackIcon, DesignTrackIcon, DevelopmentTrackIcon, SettingSection } from '~/apps/accounts/src/lib' @@ -12,20 +13,46 @@ interface TracksProps { } const Tracks: FC = (props: TracksProps) => { - const [devTrack, setDevTrack]: [boolean, Dispatch] - = useState(!!props.profile.tracks?.includes('DEVELOP')) + const [memberTracks, setMemberTracks]: [TC_TRACKS[], Dispatch] + = useState(props.profile.tracks || []) - const [designTrack, setDesignTrack]: [boolean, Dispatch] - = useState(!!props.profile.tracks?.includes('DESIGN')) + const memberProfileContext: ProfileContextData = useContext(profileContext) - const [dsTrack, setDSTrack]: [boolean, Dispatch] - = useState(!!props.profile.tracks?.includes('DATA_SCIENCE')) + useEffect(() => { + setMemberTracks(props.profile.tracks || []) + }, [props.profile]) function handleTracksChange(type: TC_TRACKS): void { - - } + const hasTrack: boolean = memberTracks.includes(type) + let updatedTracks: TC_TRACKS[] + + if (hasTrack) { + // remove track + updatedTracks = memberTracks.filter((track: TC_TRACKS) => track !== type) + } else { + // add track + updatedTracks = memberTracks.concat(type) + } - console.log('devTrack', props) + updateMemberProfileAsync( + props.profile.handle, + { tracks: updatedTracks }, + ) + .then(() => { + setMemberTracks(updatedTracks) + memberProfileContext.updateProfileContext({ + ...memberProfileContext, + profile: { + ...memberProfileContext.profile, + tracks: updatedTracks, + } as any, + }) + toast.success('Your profile has been updated.') + }) + .catch(() => { + toast.error('Failed to update your profile.') + }) + } return ( = (props: TracksProps) => { )} /> @@ -63,7 +90,7 @@ const Tracks: FC = (props: TracksProps) => { )} /> @@ -78,7 +105,7 @@ const Tracks: FC = (props: TracksProps) => { )} /> diff --git a/src/libs/core/lib/profile/index.ts b/src/libs/core/lib/profile/index.ts index ec14a045f..87b18783d 100644 --- a/src/libs/core/lib/profile/index.ts +++ b/src/libs/core/lib/profile/index.ts @@ -14,3 +14,4 @@ export * from './modify-user-email-preferences.model' export * from './modify-user-mfa.model' export * from './user-traits.model' export * from './modify-tracks.request' +export * from './modify-user-profile.model' diff --git a/src/libs/core/lib/profile/modify-user-profile.model.ts b/src/libs/core/lib/profile/modify-user-profile.model.ts new file mode 100644 index 000000000..f1d05f227 --- /dev/null +++ b/src/libs/core/lib/profile/modify-user-profile.model.ts @@ -0,0 +1,7 @@ +import { TC_TRACKS } from './user-profile.model' + +export interface UpdateProfileRequest { + firstName?: string + lastName?: string + tracks?: TC_TRACKS[] +} diff --git a/src/libs/core/lib/profile/profile-context/profile-context-data.model.ts b/src/libs/core/lib/profile/profile-context/profile-context-data.model.ts index db130b7f4..f316a2e20 100644 --- a/src/libs/core/lib/profile/profile-context/profile-context-data.model.ts +++ b/src/libs/core/lib/profile/profile-context/profile-context-data.model.ts @@ -7,4 +7,5 @@ export interface ProfileContextData { isLoggedIn: boolean profile?: UserProfile updateProfile: (updatedProfileContext: ProfileContextData) => Promise + updateProfileContext: (updatedProfileContext: ProfileContextData) => void } diff --git a/src/libs/core/lib/profile/profile-context/profile.context-provider.tsx b/src/libs/core/lib/profile/profile-context/profile.context-provider.tsx index 041a1a671..86e0132a3 100644 --- a/src/libs/core/lib/profile/profile-context/profile.context-provider.tsx +++ b/src/libs/core/lib/profile/profile-context/profile.context-provider.tsx @@ -31,6 +31,7 @@ export const ProfileProvider: FC = (props: ProfileProvider isLoggedIn: !!profile, profile, updateProfile, + updateProfileContext, } setProfileContextData(contextData) } @@ -52,6 +53,17 @@ export const ProfileProvider: FC = (props: ProfileProvider .then(() => setProfileContextData(updatedContext)) } + function updateProfileContext(updatedContext: ProfileContextData): void { + + const { profile }: ProfileContextData = updatedContext + + if (!profile) { + throw new Error('Cannot update an undefined profile') + } + + setProfileContextData(updatedContext) + } + useEffect(() => { // if our profile is already initialized, no need to continue diff --git a/src/libs/core/lib/profile/profile-context/profile.context.tsx b/src/libs/core/lib/profile/profile-context/profile.context.tsx index d61f6c3c2..3ff734319 100644 --- a/src/libs/core/lib/profile/profile-context/profile.context.tsx +++ b/src/libs/core/lib/profile/profile-context/profile.context.tsx @@ -7,6 +7,7 @@ export const defaultProfileContextData: ProfileContextData = { initialized: false, isLoggedIn: false, updateProfile: () => Promise.resolve(undefined), + updateProfileContext: () => undefined, } const profileContext: Context = createContext(defaultProfileContextData) diff --git a/src/libs/core/lib/profile/profile-functions/index.ts b/src/libs/core/lib/profile/profile-functions/index.ts index e09c62af1..3e4b0930e 100644 --- a/src/libs/core/lib/profile/profile-functions/index.ts +++ b/src/libs/core/lib/profile/profile-functions/index.ts @@ -13,6 +13,7 @@ export { updateMemberTraitsAsync, createMemberTraitsAsync, modifyTracksAsync, + updateMemberProfileAsync, } from './profile.functions' export * from './profile-store' export * from './rating.functions' diff --git a/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts b/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts index 26472c350..aa07641fc 100644 --- a/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts +++ b/src/libs/core/lib/profile/profile-functions/profile-store/profile-xhr.store.ts @@ -4,6 +4,7 @@ import { EditNameRequest } from '../../edit-name-request.model' import { ModifyTracksRequest } from '../../modify-tracks.request' import { ModifyMemberEmailPreferencesRequest } from '../../modify-user-email-preferences.model' import { ModifyUserMFARequest, ModifyUserMFAResponse } from '../../modify-user-mfa.model' +import { UpdateProfileRequest } from '../../modify-user-profile.model' import { ModifyUserPropertyRequest, ModifyUserPropertyResponse } from '../../modify-user-role.model' import { UserEmailPreferences } from '../../user-email-preference.model' import { UserProfile } from '../../user-profile.model' @@ -94,6 +95,10 @@ export async function createMemberTraits( return xhrPostAsync(`${profileUrl(handle)}/traits`, traits) } -export function modifyTracks(handle: string, request: ModifyTracksRequest): Promise { +export async function modifyTracks(handle: string, request: ModifyTracksRequest): Promise { return xhrPutAsync(profileUrl(handle), request) } + +export async function updateMemberProfile(handle: string, profile: UpdateProfileRequest): Promise { + return xhrPutAsync(profileUrl(handle), profile) +} diff --git a/src/libs/core/lib/profile/profile-functions/profile.functions.ts b/src/libs/core/lib/profile/profile-functions/profile.functions.ts index 4fab21153..174aa2204 100644 --- a/src/libs/core/lib/profile/profile-functions/profile.functions.ts +++ b/src/libs/core/lib/profile/profile-functions/profile.functions.ts @@ -4,6 +4,7 @@ import { EditNameRequest } from '../edit-name-request.model' import { ModifyTracksRequest } from '../modify-tracks.request' import { ModifyMemberEmailPreferencesRequest } from '../modify-user-email-preferences.model' import { ModifyUserMFARequest, ModifyUserMFAResponse } from '../modify-user-mfa.model' +import { UpdateProfileRequest } from '../modify-user-profile.model' import { ModifyUserPropertyResponse } from '../modify-user-role.model' import { UserEmailPreferences } from '../user-email-preference.model' import { UserProfile } from '../user-profile.model' @@ -20,6 +21,7 @@ import { updateMemberEmailPreferences, updateMemberMFA, updateMemberPassword, + updateMemberProfile, updateMemberTraits, updatePrimaryMemberRole, } from './profile-store/profile-xhr.store' @@ -122,3 +124,7 @@ export async function createMemberTraitsAsync( export async function modifyTracksAsync(handle: string, tracks: ModifyTracksRequest): Promise { return modifyTracks(handle, tracks) } + +export async function updateMemberProfileAsync(handle: string, profile: UpdateProfileRequest): Promise { + return updateMemberProfile(handle, profile) +} From b42260776da576442e8aa0194b0a1eb303141856 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 9 Jun 2023 12:57:31 +0300 Subject: [PATCH 3/3] MP-34 member communities --- .../src/lib/assets/tcandyou/ico-ethereum.png | Bin 0 -> 2649 bytes .../src/lib/assets/tcandyou/ico-ibmcloud.png | Bin 0 -> 3424 bytes .../src/lib/assets/tcandyou/ico-veteran.png | Bin 0 -> 16329 bytes .../accounts/src/lib/assets/tcandyou/index.ts | 6 ++ .../src/settings/tabs/AccountSettingsTabs.tsx | 4 +- .../account/user-and-pass/UserAndPassword.tsx | 13 ++- .../settings/tabs/tcandyou/TCandYouTab.tsx | 7 +- .../communities/Communities.module.scss | 50 +++++++++ .../tabs/tcandyou/communities/Communities.tsx | 95 ++++++++++++++++++ .../communities/communities-config.tsx | 31 ++++++ .../tabs/tcandyou/communities/index.ts | 1 + .../service-provider/ServiceProvider.tsx | 8 +- .../settings/tabs/tools/software/Software.tsx | 8 +- .../tools/subscriptions/Subscriptions.tsx | 8 +- .../profile/data-providers/useMemberTraits.ts | 16 ++- 15 files changed, 237 insertions(+), 10 deletions(-) create mode 100755 src/apps/accounts/src/lib/assets/tcandyou/ico-ethereum.png create mode 100755 src/apps/accounts/src/lib/assets/tcandyou/ico-ibmcloud.png create mode 100644 src/apps/accounts/src/lib/assets/tcandyou/ico-veteran.png create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.module.scss create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.tsx create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/communities/communities-config.tsx create mode 100644 src/apps/accounts/src/settings/tabs/tcandyou/communities/index.ts diff --git a/src/apps/accounts/src/lib/assets/tcandyou/ico-ethereum.png b/src/apps/accounts/src/lib/assets/tcandyou/ico-ethereum.png new file mode 100755 index 0000000000000000000000000000000000000000..907388abf20f521e040aaa6251f1b9e3aa2cb8fe GIT binary patch literal 2649 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 LNkvXXu0mjfJk8&7 literal 0 HcmV?d00001 diff --git a/src/apps/accounts/src/lib/assets/tcandyou/index.ts b/src/apps/accounts/src/lib/assets/tcandyou/index.ts index ce248d51e..7e4b90ac6 100644 --- a/src/apps/accounts/src/lib/assets/tcandyou/index.ts +++ b/src/apps/accounts/src/lib/assets/tcandyou/index.ts @@ -1,8 +1,14 @@ import { ReactComponent as DevelopmentTrackIcon } from './develop.svg' import { ReactComponent as DesignTrackIcon } from './design.svg' import { ReactComponent as DataScienceTrackIcon } from './data_science.svg' +import ethereumCommunityImage from './ico-ethereum.png' +import ibmCommunityImage from './ico-ibmcloud.png' +import veteransCommunityImage from './ico-veteran.png' export { + ethereumCommunityImage, + ibmCommunityImage, + veteransCommunityImage, DesignTrackIcon, DataScienceTrackIcon, DevelopmentTrackIcon, diff --git a/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx b/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx index 059f420a8..58fc63ffc 100644 --- a/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx +++ b/src/apps/accounts/src/settings/tabs/AccountSettingsTabs.tsx @@ -24,7 +24,9 @@ const AccountSettingsTabs: FC = (props: AccountSetting const [activeTab, setActiveTab]: [string, Dispatch>] = useState(activeTabHash) - const memberTraits: UserTraits[] | undefined = useMemberTraits(props.profile.handle) + const { data: memberTraits }: { + data: UserTraits[] | undefined + } = useMemberTraits(props.profile.handle) function handleTabChange(tabId: string): void { setActiveTab(tabId) diff --git a/src/apps/accounts/src/settings/tabs/account/user-and-pass/UserAndPassword.tsx b/src/apps/accounts/src/settings/tabs/account/user-and-pass/UserAndPassword.tsx index 75ed68182..2b6372ac0 100644 --- a/src/apps/accounts/src/settings/tabs/account/user-and-pass/UserAndPassword.tsx +++ b/src/apps/accounts/src/settings/tabs/account/user-and-pass/UserAndPassword.tsx @@ -1,6 +1,7 @@ import { Dispatch, FC, useCallback, useEffect, useMemo, useState } from 'react' import { has, trim } from 'lodash' import { toast } from 'react-toastify' +import { KeyedMutator } from 'swr' import { Collapsible, @@ -8,7 +9,14 @@ import { FormInputModel, FormToggleSwitch, } from '~/libs/ui' -import { updateMemberPasswordAsync, updateMemberTraitsAsync, UserProfile, UserTrait, UserTraits } from '~/libs/core' +import { + updateMemberPasswordAsync, + updateMemberTraitsAsync, + useMemberTraits, + UserProfile, + UserTrait, + UserTraits, +} from '~/libs/core' import { SettingSection } from '~/apps/accounts/src/lib' import { UserAndPassFromConfig } from './user-and-pass.form.config' @@ -30,6 +38,8 @@ const UserAndPassword: FC = (props: UserAndPasswordProps) [props.memberTraits], ) + const { mutate: mutateTraits }: { mutate: KeyedMutator } = useMemberTraits(props.profile.handle) + const [userConsent, setUserConsent]: [boolean, Dispatch] = useState(false) const requestGenerator: (inputs: ReadonlyArray) => any @@ -70,6 +80,7 @@ const UserAndPassword: FC = (props: UserAndPasswordProps) }]) .then(() => { setUserConsent(!userConsent) + mutateTraits() toast.success('User consent updated successfully.') }) .catch(() => { diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx index 8a88105d9..82288f5bb 100644 --- a/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx +++ b/src/apps/accounts/src/settings/tabs/tcandyou/TCandYouTab.tsx @@ -3,11 +3,11 @@ import { FC } from 'react' import { UserProfile, UserTraits } from '~/libs/core' import { Tracks } from './tracks' +import { Communities } from './communities' import styles from './TCandYouTab.module.scss' interface TCandYouTabProps { profile: UserProfile - // eslint-disable-next-line react/no-unused-prop-types memberTraits: UserTraits[] | undefined } @@ -16,6 +16,11 @@ const TCandYouTab: FC = (props: TCandYouTabProps) => (

You And Topcoder

+ + trait.traitId === 'communities')} + profile={props.profile} + /> ) diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.module.scss b/src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.module.scss new file mode 100644 index 000000000..816d79880 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.module.scss @@ -0,0 +1,50 @@ +@import '@libs/ui/styles/includes'; + +.container { + margin: $sp-8 0; + + .content { + display: flex; + flex-direction: column; + margin-bottom: 0; + + .communityCard { + border: 1px solid #d4d4d4; + border-radius: 8px; + padding: $sp-4; + margin-bottom: $sp-4; + display: flex; + justify-content: space-between; + align-items: flex-start; + + .communityCardHeader { + display: flex; + align-items: flex-start; + + @include ltelg { + flex-direction: column; + } + } + + img { + margin-right: $sp-4; + border-radius: 4px; + + @include ltelg { + margin-right: 0; + margin-bottom: $sp-4; + } + } + + .communityInfo { + flex: 1; + + .infoText { + padding-right: 74px; + color: #767676; + margin: $sp-3 0; + } + } + } + } +} \ No newline at end of file diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.tsx b/src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.tsx new file mode 100644 index 000000000..fae849595 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/communities/Communities.tsx @@ -0,0 +1,95 @@ +import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react' +import { bind } from 'lodash' +import { KeyedMutator } from 'swr' +import { toast } from 'react-toastify' + +import { updateMemberTraitsAsync, useMemberTraits, UserProfile, UserTraits } from '~/libs/core' +import { Button, Collapsible, FormToggleSwitch } from '~/libs/ui' + +import { communitiesConfig } from './communities-config' +import styles from './Communities.module.scss' + +interface CommunitiesProps { + communityTraits: UserTraits | undefined + profile: UserProfile +} + +interface CommunitiesDataStatus { + [key: string]: boolean +} + +const Communities: FC = (props: CommunitiesProps) => { + const [memberCommunities, setMemberCommunities]: [ + CommunitiesDataStatus | undefined, + Dispatch> + ] + = useState() + + const { mutate: mutateTraits }: { mutate: KeyedMutator } = useMemberTraits(props.profile.handle) + + useEffect(() => { + setMemberCommunities(props.communityTraits?.traits.data[0]) + }, [props.communityTraits]) + + function handleCommunitiesChange(communityId: string): void { + const updatedCommunities: CommunitiesDataStatus = { + ...memberCommunities, + [communityId]: !memberCommunities?.[communityId], + } + + updateMemberTraitsAsync(props.profile.handle, [{ + categoryName: 'Communities', + traitId: 'communities', + traits: { + data: [updatedCommunities], + }, + }]) + .then(() => { + setMemberCommunities(updatedCommunities) + mutateTraits() + toast.success('Communities updated successfully.') + }) + .catch(() => { + toast.error('Failed to update user Communities.') + }) + } + + function handleLearnMoreClick(link: string): void { + window.open(link, '_blank') + } + + return ( + Your Communities} + containerClass={styles.container} + contentClass={styles.content} + > + { + communitiesConfig.map(community => ( +
+
+ {community.name} +
+

{community.name}

+

{community.description}

+
+
+ +
+ )) + } +
+ ) +} + +export default Communities diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/communities/communities-config.tsx b/src/apps/accounts/src/settings/tabs/tcandyou/communities/communities-config.tsx new file mode 100644 index 000000000..0971828b7 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/communities/communities-config.tsx @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/typedef */ +/* eslint-disable max-len */ +/* eslint-disable sort-keys */ +import { ethereumCommunityImage, ibmCommunityImage, veteransCommunityImage } from '~/apps/accounts/src/lib' + +export const communitiesConfig = [ + { + id: 'blockchain', + icon: ethereumCommunityImage, + programID: 20000010, + name: 'Topcoder Blockchain Community', + description: 'Meet like-minded peers from around the world, share tips and insights, and collaborate with customers to build cutting-edge solutions. The Topcoder Blockchain Community provides opportunities to learn from Ethereum experts and work with top companies that are embracing blockchain technology.', + link: 'https://blockchain.topcoder.com/', + }, + { + id: 'cognitive', + icon: ibmCommunityImage, + programID: 3449, + name: 'Topcoder Cognitive Community', + description: 'By becoming a member of the Topcoder Community and registering for this specialized community, you can compete in fun cognitive challenges, access educational resources, and win money by solving real-life business problems for companies in need of cognitive expertise. ', + link: 'https://cognitive.topcoder.com/', + }, + { + id: 'veteran', + icon: veteransCommunityImage, + programID: 3450, + name: 'Topcoder Veterans Community', + description: 'We help military service members and veterans transition to a career in technology with the world\'s premier crowdsourcing platform.', + link: 'https://veterans.topcoder.com/', + }, +] diff --git a/src/apps/accounts/src/settings/tabs/tcandyou/communities/index.ts b/src/apps/accounts/src/settings/tabs/tcandyou/communities/index.ts new file mode 100644 index 000000000..96ac5b97a --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/tcandyou/communities/index.ts @@ -0,0 +1 @@ +export { default as Communities } from './Communities' diff --git a/src/apps/accounts/src/settings/tabs/tools/service-provider/ServiceProvider.tsx b/src/apps/accounts/src/settings/tabs/tools/service-provider/ServiceProvider.tsx index 05ec76154..e2ec75b68 100644 --- a/src/apps/accounts/src/settings/tabs/tools/service-provider/ServiceProvider.tsx +++ b/src/apps/accounts/src/settings/tabs/tools/service-provider/ServiceProvider.tsx @@ -1,9 +1,10 @@ import { Dispatch, FC, MutableRefObject, SetStateAction, useEffect, useRef, useState } from 'react' import { bind, isEmpty, reject, trim } from 'lodash' import { toast } from 'react-toastify' +import { KeyedMutator } from 'swr' import classNames from 'classnames' -import { createMemberTraitsAsync, updateMemberTraitsAsync, UserProfile, UserTrait } from '~/libs/core' +import { createMemberTraitsAsync, updateMemberTraitsAsync, useMemberTraits, UserProfile, UserTrait } from '~/libs/core' import { Button, Collapsible, ConfirmModal, IconOutline, InputSelect, InputText } from '~/libs/ui' import { FinancialInstitutionIcon, @@ -64,6 +65,8 @@ const ServiceProvider: FC = (props: ServiceProviderProps) const [itemToRemove, setItemToRemove]: [UserTrait | undefined, Dispatch>] = useState() + const { mutate: mutateTraits }: { mutate: KeyedMutator } = useMemberTraits(props.profile.handle) + useEffect(() => { setServiceProviderTypesData(props.serviceProviderTrait?.traits.data) }, [props.serviceProviderTrait]) @@ -155,6 +158,7 @@ const ServiceProvider: FC = (props: ServiceProviderProps) ...updatedServiceProviderTypesData || [], serviceProviderTypeUpdate, ]) + mutateTraits() }) .catch(() => { toast.error('Error updating Service Provider') @@ -183,6 +187,7 @@ const ServiceProvider: FC = (props: ServiceProviderProps) ...serviceProviderTypesData || [], serviceProviderTypeUpdate, ]) + mutateTraits() }) .catch(() => { toast.error('Error adding new Service Provider') @@ -222,6 +227,7 @@ const ServiceProvider: FC = (props: ServiceProviderProps) .then(() => { toast.success('Service Provider deleted successfully') setServiceProviderTypesData(updatedServiceProviderTypesData) + mutateTraits() }) .catch(() => { toast.error('Error deleting Service Provider') diff --git a/src/apps/accounts/src/settings/tabs/tools/software/Software.tsx b/src/apps/accounts/src/settings/tabs/tools/software/Software.tsx index 9f16577ab..72e716508 100644 --- a/src/apps/accounts/src/settings/tabs/tools/software/Software.tsx +++ b/src/apps/accounts/src/settings/tabs/tools/software/Software.tsx @@ -1,9 +1,10 @@ import { Dispatch, FC, MutableRefObject, SetStateAction, useEffect, useRef, useState } from 'react' import { bind, isEmpty, reject, trim } from 'lodash' import { toast } from 'react-toastify' +import { KeyedMutator } from 'swr' import classNames from 'classnames' -import { createMemberTraitsAsync, updateMemberTraitsAsync, UserProfile, UserTrait } from '~/libs/core' +import { createMemberTraitsAsync, updateMemberTraitsAsync, useMemberTraits, UserProfile, UserTrait } from '~/libs/core' import { Button, Collapsible, ConfirmModal, IconOutline, InputSelect, InputText } from '~/libs/ui' import { SettingSection, SoftwareIcon } from '~/apps/accounts/src/lib' @@ -57,6 +58,8 @@ const Software: FC = (props: SoftwareProps) => { const [itemToRemove, setItemToRemove]: [UserTrait | undefined, Dispatch>] = useState() + const { mutate: mutateTraits }: { mutate: KeyedMutator } = useMemberTraits(props.profile.handle) + useEffect(() => { setSoftwareTypesData(props.softwareTrait?.traits.data) }, [props.softwareTrait]) @@ -144,6 +147,7 @@ const Software: FC = (props: SoftwareProps) => { ...updatedSoftwareTypesData || [], softwareTypeUpdate, ]) + mutateTraits() }) .catch(() => { toast.error('Error updating software') @@ -172,6 +176,7 @@ const Software: FC = (props: SoftwareProps) => { ...softwareTypesData || [], softwareTypeUpdate, ]) + mutateTraits() }) .catch(() => { toast.error('Error adding new software') @@ -211,6 +216,7 @@ const Software: FC = (props: SoftwareProps) => { .then(() => { toast.success('Software deleted successfully') setSoftwareTypesData(updatedSoftwareTypesData) + mutateTraits() }) .catch(() => { toast.error('Error deleting software') diff --git a/src/apps/accounts/src/settings/tabs/tools/subscriptions/Subscriptions.tsx b/src/apps/accounts/src/settings/tabs/tools/subscriptions/Subscriptions.tsx index f21cecb28..acb16c740 100644 --- a/src/apps/accounts/src/settings/tabs/tools/subscriptions/Subscriptions.tsx +++ b/src/apps/accounts/src/settings/tabs/tools/subscriptions/Subscriptions.tsx @@ -1,9 +1,10 @@ import { Dispatch, FC, MutableRefObject, SetStateAction, useEffect, useRef, useState } from 'react' import { bind, isEmpty, reject, trim } from 'lodash' import { toast } from 'react-toastify' +import { KeyedMutator } from 'swr' import classNames from 'classnames' -import { createMemberTraitsAsync, updateMemberTraitsAsync, UserProfile, UserTrait } from '~/libs/core' +import { createMemberTraitsAsync, updateMemberTraitsAsync, useMemberTraits, UserProfile, UserTrait } from '~/libs/core' import { Button, Collapsible, ConfirmModal, IconOutline, InputText } from '~/libs/ui' import { SettingSection, SubscriptionsIcon } from '~/apps/accounts/src/lib' @@ -50,6 +51,8 @@ const Subscriptions: FC = (props: SubscriptionsProps) => { const [itemToRemove, setItemToRemove]: [UserTrait | undefined, Dispatch>] = useState() + const { mutate: mutateTraits }: { mutate: KeyedMutator } = useMemberTraits(props.profile.handle) + useEffect(() => { setSubscriptionsTypesData(props.subscriptionsTrait?.traits.data) }, [props.subscriptionsTrait]) @@ -129,6 +132,7 @@ const Subscriptions: FC = (props: SubscriptionsProps) => { ...updatedSubscriptionsTypesData || [], softwareTypeUpdate, ]) + mutateTraits() }) .catch(() => { toast.error('Error updating subscription') @@ -157,6 +161,7 @@ const Subscriptions: FC = (props: SubscriptionsProps) => { ...subscriptionsTypesData || [], softwareTypeUpdate, ]) + mutateTraits() }) .catch(() => { toast.error('Error adding new subscription') @@ -196,6 +201,7 @@ const Subscriptions: FC = (props: SubscriptionsProps) => { .then(() => { toast.success('Subscription deleted successfully') setSubscriptionsTypesData(updatedSubscriptionsTypesData) + mutateTraits() }) .catch(() => { toast.error('Error deleting subscription') diff --git a/src/libs/core/lib/profile/data-providers/useMemberTraits.ts b/src/libs/core/lib/profile/data-providers/useMemberTraits.ts index 3d0cd5983..e1c400bd6 100644 --- a/src/libs/core/lib/profile/data-providers/useMemberTraits.ts +++ b/src/libs/core/lib/profile/data-providers/useMemberTraits.ts @@ -1,10 +1,18 @@ -import useSWR, { SWRResponse } from 'swr' +import useSWR, { KeyedMutator, SWRResponse } from 'swr' import { getProfileUrl } from '../profile-functions' import { UserTraits } from '../user-traits.model' -export function useMemberTraits(handle?: string): UserTraits[] | undefined { - const { data }: SWRResponse = useSWR(handle ? `${getProfileUrl(handle)}/traits` : undefined) +export interface MemberTraitsAPI { + data: UserTraits[] | undefined + mutate: KeyedMutator +} + +export function useMemberTraits(handle?: string): MemberTraitsAPI { + const { data, mutate }: SWRResponse = useSWR(handle ? `${getProfileUrl(handle)}/traits` : undefined) - return data + return { + data, + mutate, + } }