diff --git a/packages/marketplace/src/components/pages/developer-app-detail.tsx b/packages/marketplace/src/components/pages/developer-app-detail.tsx index f87f8d4176..4cc25d2bd4 100644 --- a/packages/marketplace/src/components/pages/developer-app-detail.tsx +++ b/packages/marketplace/src/components/pages/developer-app-detail.tsx @@ -1,24 +1,66 @@ import * as React from 'react' -import { Grid, GridItem } from '@reapit/elements' +import { Dispatch } from 'redux' +import { ReduxState } from '@/types/core' +import { useDispatch, useSelector } from 'react-redux' +import { RouteComponentProps } from 'react-router' +import { appDetailRequestData } from '@/actions/app-detail' +import { selectAppDetailData, selectAppDetailLoading } from '@/selector/developer-app-detail' +import { selectLoginType } from '@/selector/auth' +import { LoginType } from '@reapit/cognito-auth' +import { AppDetailModel } from '@reapit/foundations-ts-definitions' +import AppHeader from '../ui/developer-app-detail/app-header' +import AppContent from '../ui/developer-app-detail/app-content' +import { Loader } from '@reapit/elements' +import styles from '@/styles/pages/developer-app-detail.scss?mod' -type DeveloperAppDetailProps = {} +export type DeveloperAppDetailProps = {} & RouteComponentProps<{ id: string }> +export type MapState = { + appDetailData: AppDetailModel & { + apiKey?: string | undefined + } + isLoadingAppDetail: boolean + loginType: LoginType +} + +export const mapState = (state: ReduxState): MapState => { + return { + appDetailData: selectAppDetailData(state), + isLoadingAppDetail: selectAppDetailLoading(state), + loginType: selectLoginType(state), + } +} + +export const fetchDeveloperAppDetail = (dispatch: Dispatch, appId: string) => { + return () => { + dispatch( + appDetailRequestData({ + id: appId, + }), + ) + } +} + +const DeveloperAppDetail: React.FC = props => { + const dispatch = useDispatch() + + const { + match: { params }, + } = props + const { id: appId } = params + React.useEffect(fetchDeveloperAppDetail(dispatch, appId), [dispatch, appId]) + const { appDetailData, isLoadingAppDetail, loginType } = useSelector(mapState) + const { developer, media, name } = appDetailData + const appIcon = media?.filter(({ type }) => type === 'icon')[0] + + if (isLoadingAppDetail) { + return + } -const DeveloperAppDetail: React.FC = () => { return ( - - - - Avatar - Title - - - - - SideBar - Description - - - +
+ + +
) } diff --git a/packages/marketplace/src/components/ui/analytics/detailed/detailed-tab.tsx b/packages/marketplace/src/components/ui/analytics/detailed/detailed-tab.tsx index e41af98861..000c764b37 100644 --- a/packages/marketplace/src/components/ui/analytics/detailed/detailed-tab.tsx +++ b/packages/marketplace/src/components/ui/analytics/detailed/detailed-tab.tsx @@ -9,7 +9,7 @@ import { httpTrafficPerDayRequestData } from '@/actions/app-http-traffic-event' import { appInstallationsRequestData, appInstallationsFilterRequestData } from '@/actions/app-installations' import { InstallationModel, AppSummaryModel } from '@reapit/foundations-ts-definitions' import { getAppUsageStats, getAppHttpTraffic } from '@/selector/analytics' -import { getDevelopers } from '@/selector/developer' +import { selectDeveloper } from '@/selector/developer' import { getInstallations } from '@/selector/installations' import { FlexContainerBasic, Grid, GridItem, FlexContainerResponsive, DATE_TIME_FORMAT } from '@reapit/elements' @@ -120,7 +120,7 @@ export type MapState = { export const mapState = (state: ReduxState): MapState => { return { installations: getInstallations(state), - developer: getDevelopers(state), + developer: selectDeveloper(state), appUsageStats: getAppUsageStats(state), appHttpTraffic: getAppHttpTraffic(state), } diff --git a/packages/marketplace/src/components/ui/developer-app-detail/app-content/app-content.tsx b/packages/marketplace/src/components/ui/developer-app-detail/app-content/app-content.tsx new file mode 100644 index 0000000000..df4aab6827 --- /dev/null +++ b/packages/marketplace/src/components/ui/developer-app-detail/app-content/app-content.tsx @@ -0,0 +1,175 @@ +import * as React from 'react' +import { CopyToClipboard } from 'react-copy-to-clipboard' +import Slider, { Settings } from 'react-slick' +import ChevronLeftIcon from '@/components/svg/chevron-left' +import { FaCheck, FaTimes, FaCopy } from 'react-icons/fa' +import { Grid, GridItem, SubTitleH6, H6, GridThreeColItem } from '@reapit/elements' +import { AppDetailModel } from '@reapit/foundations-ts-definitions' +import AuthFlow from '@/constants/app-auth-flow' +import AppAuthenticationDetail from '../../app-authentication-detail' +import styles from '@/styles/blocks/app-detail.scss?mod' +import carouselStyles from '@/styles/elements/carousel.scss?mod' + +type AppContentProps = { + appDetailData: AppDetailModel & { + apiKey?: string | undefined + } + loginType: string +} + +export const SlickButtonNav = ({ children, ...props }) => + +export type HandleShowApiKey = { + setIsShowApikey: React.Dispatch> + isShowApiKey: boolean +} + +export const handleShowApiKey = ({ setIsShowApikey, isShowApiKey }: HandleShowApiKey) => () => { + setIsShowApikey(!isShowApiKey) +} + +export const handleCopyAlert = () => alert('Copied') + +export type RenderShowApiKeyForWebComponent = HandleShowApiKey & { + isWebComponent?: boolean + isCurrentLoggedUserDeveloper: boolean + apiKey?: string +} + +export const renderShowApiKeyForWebComponent = ({ + isWebComponent, + setIsShowApikey, + isShowApiKey, + apiKey, + isCurrentLoggedUserDeveloper, +}: RenderShowApiKeyForWebComponent) => { + const isShow = isWebComponent && !isCurrentLoggedUserDeveloper + if (!isShow) { + return null + } + return ( + <> + API key +

+ To obtain your unique API key, click on 'Show API Key' below. For installation instructions, please + click here +

+ Authentication:  + + + {!isShowApiKey ? 'Show' : 'Hide'} API key + + + {isShowApiKey && ( + +
+ + + + +
+
+ )} + + ) +} + +const AppContent: React.FC = ({ appDetailData, loginType }) => { + const { + externalId, + developer, + isListed, + id, + authFlow, + isWebComponent, + apiKey, + media = [], + scopes = [], + description, + installedOn, + } = appDetailData + const [isShowApiKey, setIsShowApikey] = React.useState(false) + + const isCurrentLoggedUserClient = loginType === 'CLIENT' + const isCurrentLoggedUserDeveloper = loginType === 'DEVELOPER' + const carouselImages = media.filter(({ type }) => type === 'image') + const settings: Settings = { + dots: false, + speed: 500, + variableWidth: true, + prevArrow: ( + // @ts-ignore + + + + ), + nextArrow: ( + // @ts-ignore + + + + ), + } + + return ( + + +
+
{developer}
+
+ {isCurrentLoggedUserDeveloper && ( + <> +

App Information

+
+

Client ID:

+

{externalId}

+
+
+

Status:

+

{isListed ? 'Listed' : 'Not listed'}

+ {isListed ? : } +
+ {authFlow === AuthFlow.CLIENT_SECRET && id && } + + )} + {renderShowApiKeyForWebComponent({ + isWebComponent, + isShowApiKey, + setIsShowApikey, + apiKey: apiKey, + isCurrentLoggedUserDeveloper, + })} +
+ + {carouselImages.length > 0 && ( +
+ + {carouselImages.map(({ uri }, index) => ( +
+ +
+ ))} +
+
+ )} +
+

{description}

+
+ +
+ {isCurrentLoggedUserDeveloper && 'Permissions requested'} + {isCurrentLoggedUserClient && (installedOn ? 'Permissions granted' : 'Permissions required')} +
+ + {scopes.map(item => ( + +
  • {item.description}
  • +
    + ))} +
    +
    +
    + ) +} + +export default AppContent diff --git a/packages/marketplace/src/components/ui/developer-app-detail/app-content/index.ts b/packages/marketplace/src/components/ui/developer-app-detail/app-content/index.ts new file mode 100644 index 0000000000..67df188973 --- /dev/null +++ b/packages/marketplace/src/components/ui/developer-app-detail/app-content/index.ts @@ -0,0 +1,2 @@ +import AppContent from './app-content' +export default AppContent diff --git a/packages/marketplace/src/components/ui/developer-app-detail/app-header/app-header.tsx b/packages/marketplace/src/components/ui/developer-app-detail/app-header/app-header.tsx new file mode 100644 index 0000000000..af39de5f03 --- /dev/null +++ b/packages/marketplace/src/components/ui/developer-app-detail/app-header/app-header.tsx @@ -0,0 +1,32 @@ +import * as React from 'react' +import { Grid, GridItem, H3 } from '@reapit/elements' +import { MediaModel } from '@reapit/foundations-ts-definitions' +import styles from '@/styles/pages/developer-app-detail.scss?mod' + +type AppHeaderProps = { + appIcon?: MediaModel + appName?: string + developer?: string +} + +const AppHeader: React.FC = ({ appIcon, appName, developer }) => { + return ( + + +
    + {name} +
    +
    + +

    {appName}

    +

    {developer}

    +
    +
    + ) +} + +export default AppHeader diff --git a/packages/marketplace/src/components/ui/developer-app-detail/app-header/index.ts b/packages/marketplace/src/components/ui/developer-app-detail/app-header/index.ts new file mode 100644 index 0000000000..15d550d48c --- /dev/null +++ b/packages/marketplace/src/components/ui/developer-app-detail/app-header/index.ts @@ -0,0 +1,2 @@ +import AppHeader from './app-header' +export default AppHeader diff --git a/packages/marketplace/src/core/private-route.tsx b/packages/marketplace/src/core/private-route.tsx index 72da797f20..e7c5189d71 100644 --- a/packages/marketplace/src/core/private-route.tsx +++ b/packages/marketplace/src/core/private-route.tsx @@ -38,7 +38,7 @@ export const PrivateRoute = ({ } const Component = component - return + return }} /> ) diff --git a/packages/marketplace/src/selector/developer-app-detail.ts b/packages/marketplace/src/selector/developer-app-detail.ts new file mode 100644 index 0000000000..9082a00c8a --- /dev/null +++ b/packages/marketplace/src/selector/developer-app-detail.ts @@ -0,0 +1,13 @@ +import { ReduxState } from '@/types/core' + +export const selectAppDetailData = (state: ReduxState) => { + return state.appDetail.appDetailData?.data || {} +} + +export const selectAppDetailAuthentication = (state: ReduxState) => { + return state.appDetail.authentication +} + +export const selectAppDetailLoading = (state: ReduxState) => { + return state.appDetail.loading +} diff --git a/packages/marketplace/src/selector/developer.ts b/packages/marketplace/src/selector/developer.ts index 87699d885a..4ead6ae59e 100644 --- a/packages/marketplace/src/selector/developer.ts +++ b/packages/marketplace/src/selector/developer.ts @@ -8,6 +8,6 @@ export const selectDeveloperEmail = (state: ReduxState) => { return state?.settings?.developerInfomation?.email } -export const getDevelopers = (state: ReduxState) => { +export const selectDeveloper = (state: ReduxState) => { return state.developer } diff --git a/packages/marketplace/src/styles/pages/developer-app-detail.scss b/packages/marketplace/src/styles/pages/developer-app-detail.scss new file mode 100644 index 0000000000..d49001a843 --- /dev/null +++ b/packages/marketplace/src/styles/pages/developer-app-detail.scss @@ -0,0 +1,18 @@ +@import '../base/colors.scss'; + +.appDetailContainer { + width: 100%; + max-width: 1012px; + margin: 0 auto; + padding: 0 16px; +} +.appIconContainer { + width: 128px; + height: 128px; + display: flex; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 50%; + margin: 0 auto; +}