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
18 changes: 12 additions & 6 deletions src/apps/wallet/src/home/tabs/WalletTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { PageTitle, TabsNavbar, TabsNavItem } from '~/libs/ui'
import { getHashFromTabId, getTabIdFromHash, WalletTabsConfig, WalletTabViews } from './config'
import { WinningsTab } from './winnings'
import { HomeTab } from './home'
import { PayoutTab } from './payout'
import styles from './WalletTabs.module.scss'

interface WalletHomeProps {
Expand All @@ -16,15 +17,16 @@ interface WalletHomeProps {
const WalletTabs: FC<WalletHomeProps> = (props: WalletHomeProps) => {
const { hash }: { hash: string } = useLocation()

const activeTabHash: string = useMemo<string>(() => getTabIdFromHash(hash), [hash])
const activeTabHash: WalletTabViews = useMemo<WalletTabViews>(() => getTabIdFromHash(hash), [hash])

const [activeTab, setActiveTab]: [string, Dispatch<SetStateAction<string>>] = useState<string>(activeTabHash)
const [activeTab, setActiveTab]: [WalletTabViews, Dispatch<SetStateAction<WalletTabViews>>]
= useState<WalletTabViews>(activeTabHash)

useEffect(() => {
setActiveTab(activeTabHash)
}, [activeTabHash])

function handleTabChange(tabId: string): void {
function handleTabChange(tabId: WalletTabViews): void {
setActiveTab(tabId)
window.location.hash = getHashFromTabId(tabId)
}
Expand All @@ -34,14 +36,18 @@ const WalletTabs: FC<WalletHomeProps> = (props: WalletHomeProps) => {
<TabsNavbar defaultActive={activeTab} onChange={handleTabChange} tabs={WalletTabsConfig} />

<PageTitle>
{[WalletTabsConfig.find((tab: TabsNavItem) => tab.id === activeTab)?.title, 'Wallet', 'Topcoder'].join(
' | ',
)}
{[
WalletTabsConfig.find((tab: TabsNavItem<WalletTabViews>) => tab.id === activeTab)?.title,
'Wallet',
'Topcoder',
].join(' | ')}
</PageTitle>

{activeTab === WalletTabViews.winnings && <WinningsTab profile={props.profile} />}

{activeTab === WalletTabViews.home && <HomeTab profile={props.profile} />}

{activeTab === WalletTabViews.payout && <PayoutTab profile={props.profile} />}
</div>
)
}
Expand Down
21 changes: 14 additions & 7 deletions src/apps/wallet/src/home/tabs/config/wallet-tabs-config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { TabsNavItem } from '~/libs/ui'

export enum WalletTabViews {
home = '0',
winnings = '1',
home,
winnings,
payout,
}

export const WalletTabsConfig: TabsNavItem[] = [
export const WalletTabsConfig: TabsNavItem<WalletTabViews>[] = [
{
id: WalletTabViews.home,
title: 'Wallet',
Expand All @@ -14,23 +15,29 @@ export const WalletTabsConfig: TabsNavItem[] = [
id: WalletTabViews.winnings,
title: 'Winnings',
},
{
id: WalletTabViews.payout,
title: 'Payout',
},
]

export function getHashFromTabId(tabId: string): string {
export function getHashFromTabId(tabId: WalletTabViews): string {
switch (tabId) {
case WalletTabViews.home:
return '#home'
case WalletTabViews.winnings:
return '#winnings'
case WalletTabViews.payout:
return '#payout'
default:
return '#home'
}
}

export function getTabIdFromHash(hash: string): string {
export function getTabIdFromHash(hash: string): WalletTabViews {
switch (hash) {
case '#winnings':
return WalletTabViews.winnings
case '#payout':
return WalletTabViews.payout
default:
return WalletTabViews.home
}
Expand Down
8 changes: 8 additions & 0 deletions src/apps/wallet/src/home/tabs/payout/PayoutTab.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@import '@libs/ui/styles/includes';

.iframe {
width: 100%;
height: 100%;
border: none;
height: 90vh;
}
65 changes: 65 additions & 0 deletions src/apps/wallet/src/home/tabs/payout/PayoutTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { FC, MutableRefObject, useEffect, useRef } from 'react'

import { UserProfile } from '~/libs/core'
import { EnvironmentConfig } from '~/config'

import { getTrolleyPortalLink } from '../../../lib/services/wallet'

import styles from './PayoutTab.module.scss'

interface PayoutTabProps {
profile: UserProfile
}

const PayoutTab: FC<PayoutTabProps> = props => {
const loading = useRef<number>()
const frameRef: MutableRefObject<HTMLElement | any> = useRef()

useEffect(() => {
if (!props.profile.userId || props.profile.userId === loading.current) {
return
}

loading.current = props.profile.userId
getTrolleyPortalLink()
.then((link: string) => {
frameRef.current.src = link
})
}, [props.profile.userId])

useEffect(() => {
if (!frameRef.current) {
return undefined
}

const handleEvent: (event: any) => void = (event: any) => {
const { data: widgetEvent, origin }: { data: { event: string, data: number }, origin: string } = event

if (origin.indexOf(EnvironmentConfig.TROLLEY_WIDGET_ORIGIN) === -1) {
return
}

// resize iframe based on the reported content height
if (widgetEvent.event === 'document.height') {
Object.assign(frameRef.current.style, { height: `${widgetEvent.data}px` })
}
}

window.addEventListener('message', handleEvent, false)
return (): void => {
window.removeEventListener('message', handleEvent, false)
}
}, [frameRef.current?.src])

return (
<div className={styles.wrap}>
<iframe
className={styles.iframe}
ref={frameRef}
title='Trolley'
/>
</div>
)
}

export default PayoutTab
1 change: 1 addition & 0 deletions src/apps/wallet/src/home/tabs/payout/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as PayoutTab } from './PayoutTab'
17 changes: 17 additions & 0 deletions src/apps/wallet/src/lib/services/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,20 @@ export async function resendOtp(transactionId: string): Promise<TransactionRespo
throw new Error('Failed to resend OTP.')
}
}

/**
* Fetches the Trolley portal link from the server.
*
* @returns {Promise<string>} A promise that resolves to the Trolley portal link.
* @throws {Error} If the response does not contain a valid link.
*/
export async function getTrolleyPortalLink(): Promise<string> {
const url = `${baseUrl}/trolley/portal-link`
const response = await xhrGetAsync<{ link: string }>(url)

if (!response.link) {
throw new Error('Error fetching Trolley portal link')
}

return response.link
}
2 changes: 2 additions & 0 deletions src/config/environments/default.env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,5 @@ export const USERFLOW_SURVEYS = {
PROFILES: getReactEnv<string>('USERFLOW_SURVEY_PROFILES', '5cfae36f-0700-41c4-8938-0add4037acb2'),
TALENTSEARCH: getReactEnv<string>('USERFLOW_SURVEY_TALENTSEARCH', 'd1030c93-dd36-4ae0-b5d0-95004b8e9d32'),
}

export const TROLLEY_WIDGET_ORIGIN = getReactEnv<string>('TROLLEY_WIDGET_ORIGIN', 'https://widget.trolley.com')
1 change: 1 addition & 0 deletions src/config/environments/global-config.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ export interface GlobalConfig {
PROFILES: string
TALENTSEARCH: string
}
TROLLEY_WIDGET_ORIGIN: string
}
33 changes: 16 additions & 17 deletions src/libs/ui/lib/components/tabs-navbar/TabsNavbar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
Dispatch,
FC,
MutableRefObject,
ReactNode,
SetStateAction,
Expand All @@ -19,28 +18,28 @@ import { ActiveTabTipIcon, IconOutline } from '../svgs'
import { TabsNavItem } from './tabs-nav-item.model'
import styles from './TabsNavbar.module.scss'

export interface TabsNavbarProps {
defaultActive: string
onChange: (active: string) => void
tabs: ReadonlyArray<TabsNavItem>
export interface TabsNavbarProps<T> {
defaultActive: T
onChange: (active: T) => void
tabs: ReadonlyArray<TabsNavItem<T>>
}

const TabsNavbar: FC<TabsNavbarProps> = (props: TabsNavbarProps) => {
const TabsNavbar = <T, >(props: TabsNavbarProps<T>): JSX.Element => {
const query: URLSearchParams = new URLSearchParams(window.location.search)
const initialTab: MutableRefObject<string | null> = useRef<string|null>(query.get('tab'))
const initialTab: MutableRefObject<T | undefined> = useRef<T|undefined>(query.get('tab') as T)

const [tabOpened, setTabOpened]: [string | undefined, Dispatch<SetStateAction<string | undefined>>]
= useState<string | undefined>(props.defaultActive)
const [tabOpened, setTabOpened]: [T | undefined, Dispatch<SetStateAction<T | undefined>>]
= useState<T | undefined>(props.defaultActive)
const tabRefs: MutableRefObject<Array<HTMLElement>> = useRef([] as Array<HTMLElement>)
const [offset, setOffset]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0)
const [menuIsVisible, setMenuIsVisible]: [boolean, Dispatch<SetStateAction<boolean>>] = useState(false)
const triggerRef: MutableRefObject<any> = useRef(undefined)

const activeTab: TabsNavItem = useMemo(() => (
props.tabs.find(tab => tab.id === tabOpened) as TabsNavItem
const activeTab: TabsNavItem<T> = useMemo(() => (
props.tabs.find(tab => tab.id === tabOpened) as TabsNavItem<T>
), [tabOpened, props.tabs])

const updateOffset: (tabId: string) => void = useCallback((tabId: string) => {
const updateOffset: (tabId: T) => void = useCallback((tabId: T) => {

const index: number = props.tabs.findIndex(tab => tab.id === tabId)
if (index === -1) {
Expand All @@ -54,7 +53,7 @@ const TabsNavbar: FC<TabsNavbarProps> = (props: TabsNavbarProps) => {
props.tabs,
])

const handleActivateTab: (tabId: string) => () => void = useCallback((tabId: string) => () => {
const handleActivateTab: (tabId: T) => () => void = useCallback((tabId: T) => () => {
setTabOpened(tabId)
props.onChange.call(undefined, tabId)
updateOffset(tabId)
Expand All @@ -74,7 +73,7 @@ const TabsNavbar: FC<TabsNavbarProps> = (props: TabsNavbarProps) => {
&& props.tabs.find(tab => tab.id === initialTab.current)
) {
handleActivateTab(initialTab.current)()
initialTab.current = ''
initialTab.current = undefined
} else if (props.defaultActive) {
setTabOpened(props.defaultActive)
updateOffset(props.defaultActive)
Expand All @@ -87,7 +86,7 @@ const TabsNavbar: FC<TabsNavbarProps> = (props: TabsNavbarProps) => {
])

const renderTabItem: (
tab: TabsNavItem,
tab: TabsNavItem<T>,
activeTabId?: string,
ref?: (el: HTMLElement | null) => void
) => ReactNode = (
Expand Down Expand Up @@ -122,7 +121,7 @@ const TabsNavbar: FC<TabsNavbarProps> = (props: TabsNavbarProps) => {
<div
ref={ref}
className={classNames(styles['tab-item'], activeTabId === tab.id && 'active')}
key={tab.id}
key={tab.id as string}
onClick={handleActivateTab(tab.id)}
>
{tabContent}
Expand Down Expand Up @@ -151,7 +150,7 @@ const TabsNavbar: FC<TabsNavbarProps> = (props: TabsNavbarProps) => {

<div className={classNames(styles['menu-wrapper'])}>
{props.tabs.map((tab, i) => (
renderTabItem(tab, tabOpened, el => { tabRefs.current[i] = el as HTMLElement })
renderTabItem(tab, tabOpened as string, el => { tabRefs.current[i] = el as HTMLElement })
))}
</div>
<div
Expand Down
4 changes: 2 additions & 2 deletions src/libs/ui/lib/components/tabs-navbar/tabs-nav-item.model.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { TabsNavItemBadge } from './tab-nav-item-badge.model'

export interface TabsNavItem {
export interface TabsNavItem<T = string> {
badges?: Array<TabsNavItemBadge>
id: string
id: T
title: string
url?: string
}