Skip to content

Commit

Permalink
feat: #1125: Update new redux flow for developer apps
Browse files Browse the repository at this point in the history
  • Loading branch information
nphivu414 committed May 11, 2020
1 parent 64c6709 commit e35560c
Show file tree
Hide file tree
Showing 10 changed files with 503 additions and 72 deletions.
1 change: 1 addition & 0 deletions packages/marketplace/src/actions/revision-detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FormState } from '@/types/core'
export interface RevisionDetailRequestParams {
appId: string
appRevisionId: string
callback?: () => void
}

export interface RevisionReceiveDataParams extends RevisionDetailItem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,79 @@ import * as React from 'react'
import { useSelector } from 'react-redux'
import { selectAppDetailState, selectAppDetailData, selectAppDetailLoading } from '@/selector/developer-app-detail'
import { selectLoginType } from '@/selector/auth'
import { Loader } from '@reapit/elements'
import AppHeader from '@/components/ui/app-detail/app-header'
import AppContent from '@/components/ui/app-detail/app-content'
import DeveloperAppDetailButtonGroup from '@/components/ui/developer-app-detail/developer-app-detail-button-group'
import AppDelete from '@/components/ui/app-delete'
import AppInstallations from '@/components/ui/app-installations/app-installations-modal'

import { Loader } from '@reapit/elements'
import routes from '@/constants/routes'
import styles from '@/styles/pages/developer-app-detail.scss?mod'
import AppRevisionModal from '@/components/ui/developer-app-detail/app-revision-modal'
import { useHistory } from 'react-router'

export type DeveloperAppDetailProps = {}

export const handleOnDeleteAppSuccess = history => {
return () => {
history.replace(routes.DEVELOPER_MY_APPS)
}
}

const DeveloperAppDetail: React.FC<DeveloperAppDetailProps> = () => {
const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState(false)
const [isInstallationsModalOpen, setIsInstallationsModalOpen] = React.useState(false)
const [isAppRevisionComparisionModalOpen, setIsAppRevisionComparisionModalOpen] = React.useState(false)

const history = useHistory()
const appDetailState = useSelector(selectAppDetailState)
const appDetailData = useSelector(selectAppDetailData)
const isLoadingAppDetail = useSelector(selectAppDetailLoading)
const loginType = useSelector(selectLoginType)

if (!appDetailData.id || isLoadingAppDetail) {
return <Loader />
}
const { id, name } = appDetailData

return (
<div className={styles.appDetailContainer}>
<AppHeader
appDetailData={appDetailData}
buttonGroup={<DeveloperAppDetailButtonGroup appDetailState={appDetailState} />}
buttonGroup={
id && (
<DeveloperAppDetailButtonGroup
appDetailState={appDetailState}
setIsAppRevisionComparisionModalOpen={setIsAppRevisionComparisionModalOpen}
setIsDeleteModalOpen={setIsDeleteModalOpen}
setIsInstallationsModalOpen={setIsInstallationsModalOpen}
/>
)
}
/>
<AppContent appDetailData={appDetailData} loginType={loginType} />
<AppDelete
appId={id || ''}
appName={name || ''}
afterClose={() => setIsDeleteModalOpen(false)}
visible={isDeleteModalOpen}
onDeleteSuccess={handleOnDeleteAppSuccess(history)}
/>

<AppInstallations
appId={id || ''}
appName={name || ''}
visible={isInstallationsModalOpen}
afterClose={() => setIsInstallationsModalOpen(false)}
onUninstallSuccess={() => {
setIsInstallationsModalOpen(false)
}}
/>

<AppRevisionModal
visible={isAppRevisionComparisionModalOpen}
appId={id || ''}
appDetailState={appDetailState}
afterClose={() => setIsAppRevisionComparisionModalOpen(false)}
/>
{isLoadingAppDetail && <Loader />}
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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, GridThreeColItem } from '@reapit/elements'
import { Grid, GridItem, SubTitleH6, GridThreeColItem, HTMLRender } from '@reapit/elements'
import { AppDetailModel } from '@reapit/foundations-ts-definitions'
import AuthFlow from '@/constants/app-auth-flow'
import AppAuthenticationDetail from '../../app-authentication-detail'
Expand Down Expand Up @@ -86,7 +86,7 @@ const AppContent: React.FC<AppContentProps> = ({ appDetailData, loginType }) =>
apiKey,
media = [],
scopes = [],
description,
description = '',
} = appDetailData
const [isShowApiKey, setIsShowApikey] = React.useState<boolean>(false)

Expand Down Expand Up @@ -140,7 +140,7 @@ const AppContent: React.FC<AppContentProps> = ({ appDetailData, loginType }) =>
})}
</GridItem>
<GridItem className="is-8">
<p>{description}</p>
<HTMLRender className={styles.description} html={description} />
<br />
{carouselImages.length > 0 && (
<div className={carouselStyles.container}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import * as React from 'react'
import { RevisionDetailState } from '@/reducers/revision-detail'
import { AppRevisionModel, MediaModel, ScopeModel } from '@reapit/foundations-ts-definitions'
import DiffMedia from '@/components/ui/diff-media'
import { AppDetailModel } from '@/types/marketplace-api-schema'
import { DesktopIntegrationTypeModel, PagedResultDesktopIntegrationTypeModel_ } from '@/actions/app-integration-types'
import DiffCheckbox from '../diff-checkbox'
import DiffViewer from '../diff-viewer'
import DiffRenderHTML from '../diff-render-html'
import { AppDetailData } from '@/reducers/developer'

export type AppRevisionComparisionProps = {
revisionDetailState: RevisionDetailState
appDetailData: AppDetailData | null
}

export type DiffMediaModel = {
currentMedia?: string
changedMedia?: string
order: number
type: string
}

const diffStringList: { [k in keyof AppRevisionModel]: string } = {
name: 'Name',
category: 'Category',
homePage: 'Home page',
launchUri: 'Launch URI',
supportEmail: 'Support Email',
telephone: 'Telephone',
summary: 'Summary',
description: 'Description',
redirectUris: 'Redirect URIs',
signoutUris: 'Signout URIs',
limitToClientIds: 'Private Apps',
desktopIntegrationTypeIds: 'Integration Type',
}

export const isAppearInScope = (nameNeedToFind: string | undefined, scopes: ScopeModel[] = []): boolean => {
if (!nameNeedToFind || scopes.length === 0) {
return false
}
const result = scopes.find((item: ScopeModel) => {
return item.name === nameNeedToFind
})
return !!result
}

export const renderCheckboxesDiff = ({
scopes,
appScopes,
revisionScopes,
}: {
scopes: ScopeModel[]
appScopes: ScopeModel[] | undefined
revisionScopes: ScopeModel[] | undefined
}) => {
return scopes.map((scope: ScopeModel) => {
const isCheckedInAppDetail = isAppearInScope(scope.name, appScopes)
const isCheckedInRevision = isAppearInScope(scope.name, revisionScopes)
return (
<div className="mb-3" key={scope.name}>
<h4 className="mb-2">{scope.description}</h4>
<DiffCheckbox currentChecked={isCheckedInAppDetail} changedChecked={isCheckedInRevision} />
</div>
)
})
}

export const getChangedMediaList = ({ app, revision }): DiffMediaModel[] => {
const { media: revisionMedia } = revision
const { media: appMedia } = app
if (!revisionMedia || !appMedia) {
return [
{
order: 0,
type: 'media',
},
]
}
// Check the longest array to compare
const isNewMediaMoreItemThanOldOne = revisionMedia.length >= appMedia.length
if (isNewMediaMoreItemThanOldOne) {
return revisionMedia.map((revisionMedia: MediaModel, index: number) => ({
changedMedia: revisionMedia?.uri,
currentMedia: appMedia[index]?.uri,
order: revisionMedia?.order || 0,
type: revisionMedia?.type || '',
}))
}

return appMedia.map((currentMedia: MediaModel, index: number) => ({
changedMedia: revisionMedia[index]?.uri,
currentMedia: currentMedia?.uri,
order: currentMedia?.order || 0,
type: currentMedia?.type || 'media',
}))
}

export const mapIntegrationIdArrayToNameArray = (
desktopIntegrationTypeIds?: string[],
desktopIntegrationTypesArray?: DesktopIntegrationTypeModel[],
): string[] => {
if (!desktopIntegrationTypeIds || !desktopIntegrationTypesArray) {
return []
}
const result = desktopIntegrationTypeIds.map((id: string) => {
const matchedIntegration = desktopIntegrationTypesArray.find(
(integration: DesktopIntegrationTypeModel) => integration.id === id,
)
return matchedIntegration?.name ?? ''
})
const filteredResult = result.filter(r => r)
return filteredResult
}

export type RenderDiffContentParams = {
key: string
revision: AppRevisionModel
app: AppDetailModel & { desktopIntegrationTypeIds?: string[] }
desktopIntegrationTypes: PagedResultDesktopIntegrationTypeModel_
}

export const renderDiffContent = ({ key, revision, app, desktopIntegrationTypes }: RenderDiffContentParams) => {
if (key === 'category') {
return (
<DiffViewer currentString={app.category?.name || ''} changedString={revision.category?.name || ''} type="words" />
)
}
if (key === 'description') {
return <DiffRenderHTML currentString={app.description || ''} changedString={revision.description || ''} />
}
if (key === 'desktopIntegrationTypeIds') {
const oldIntegrationTypeArray = mapIntegrationIdArrayToNameArray(
app.desktopIntegrationTypeIds,
desktopIntegrationTypes.data,
)
const newIntegrationTypeArray = mapIntegrationIdArrayToNameArray(
revision.desktopIntegrationTypeIds,
desktopIntegrationTypes.data,
)
const sortedOldArray = [...oldIntegrationTypeArray].sort()
const sortedNewArray = [...newIntegrationTypeArray].sort()
return (
<DiffViewer
currentString={sortedOldArray.join(', ')}
changedString={sortedNewArray.join(', ')}
type="wordsWithSpace"
/>
)
}
if (['redirectUris', 'signoutUris', 'limitToClientIds', 'desktopIntegrationTypeIds'].includes(key)) {
const currentString = Array.isArray(app[key]) ? app[key].join(' ') : ''
const changedString = Array.isArray(revision[key]) ? revision[key].join(' ') : ''
return <DiffViewer currentString={currentString} changedString={changedString} type="words" />
}
return <DiffViewer currentString={app[key] || ''} changedString={revision[key] || ''} type="words" />
}

export const AppRevisionComparision: React.FC<AppRevisionComparisionProps> = ({
revisionDetailState,
appDetailData,
}) => {
if (!revisionDetailState.revisionDetailData || !appDetailData) {
return null
}
const { data: revision, scopes, desktopIntegrationTypes } = revisionDetailState.revisionDetailData
// const app = appDetailData

return (
<div>
{Object.keys(diffStringList).map(key => {
return (
<div className="mb-3" key={key}>
<h4 className="mb-2">{diffStringList[key]}</h4>
{renderDiffContent({ key, app: appDetailData, desktopIntegrationTypes, revision })}
</div>
)
})}
{renderCheckboxesDiff({ scopes, appScopes: appDetailData.scopes, revisionScopes: revision.scopes })}
<div className="mb-3">
<h4 data-test="chkIsListed" className="mb-2">
Is listed
</h4>
<DiffCheckbox
currentChecked={Boolean(appDetailData.isListed)}
changedChecked={Boolean(revision.isListed)}
dataTest="revision-diff-isListed"
/>
</div>
<div className="mb-3">
<h4 data-test="chkIsDirectApi" className="mb-2">
Is Direct API
</h4>
<DiffCheckbox
currentChecked={Boolean(appDetailData.isDirectApi)}
changedChecked={Boolean(revision.isDirectApi)}
dataTest="revision-diff-isDirectApi"
/>
</div>
{getChangedMediaList({ app: appDetailData, revision }).map(media => (
<div className="mb-3" key={media.order}>
<h4 className="mb-2 capitalize">
{media.type} {media.order > 0 && <span>{media.order}</span>}
</h4>
<DiffMedia changedMedia={media.changedMedia} currentMedia={media.currentMedia} type={media.type} />
</div>
))}
</div>
)
}

export default AppRevisionComparision
Loading

0 comments on commit e35560c

Please sign in to comment.