diff --git a/client/default.config.js b/client/default.config.js
index 4a04c729..8442456b 100644
--- a/client/default.config.js
+++ b/client/default.config.js
@@ -33,7 +33,7 @@ module.exports = {
APPROVED: 'Approved',
},
appTypeToDisplayName: {
- APP: 'Standard',
+ APP: 'Application',
DASHBOARD_WIDGET: 'Dashboard',
TRACKER_DASHBOARD_WIDGET: 'Tracker Dashboard',
},
diff --git a/client/src/components/PluginTag/PluginTag.js b/client/src/components/PluginTag/PluginTag.js
index d22d53af..f3b776fc 100644
--- a/client/src/components/PluginTag/PluginTag.js
+++ b/client/src/components/PluginTag/PluginTag.js
@@ -6,7 +6,7 @@ const PluginTag = ({ hasPlugin, pluginType }) => {
}
const tagString = pluginType ? `Plugin: ${pluginType}` : 'Plugin'
- return {tagString}
+ return {tagString}
}
export default PluginTag
diff --git a/client/src/components/Versions/ChangeLogViewer.js b/client/src/components/Versions/ChangeLogViewer.js
index 59e482fb..180c17fe 100644
--- a/client/src/components/Versions/ChangeLogViewer.js
+++ b/client/src/components/Versions/ChangeLogViewer.js
@@ -1,45 +1,29 @@
import {
CircularLoader,
- DataTable,
- DataTableCell,
- DataTableColumnHeader,
- DataTableHead,
- DataTableRow,
- Modal,
- ModalContent,
- ModalTitle,
+ Table,
+ TableCell,
+ TableRow,
SingleSelectField,
SingleSelectOption,
} from '@dhis2/ui'
-import { useQuery } from 'src/api'
-
-import React from 'react'
-import Changelog from '../../utils/changelog'
import classnames from 'classnames'
+import PropTypes from 'prop-types'
+import React, { useState } from 'react'
+import Changelog from '../../utils/changelog'
import styles from './ChangeLogViewer.module.css'
+import { useQuery } from 'src/api'
-const ChangeLogViewer = ({
- appId,
- onCloseChangelog,
- baseVersion,
- setBaseVersion,
- setCompareVersion,
- compareVersion,
-}) => {
+const ChangeLogViewer = ({ appId, latestVersion }) => {
const { data, loading } = useQuery(`apps/${appId}/changelog`)
- if (!data || loading) {
- return
- }
+ const changelog = new Changelog(data?.changelog)
- if (!data.changelog) {
- return null
- }
+ const allVersions = changelog?.data?.map(({ version }) => version)
+ const changesCount = allVersions?.length
- const changelog = new Changelog(data.changelog)
+ const [baseVersion, setBaseVersion] = useState(latestVersion)
- const allVersions = changelog.data.map(({ version }) => version)
- const changesCount = allVersions.length
+ const [compareVersion, setCompareVersion] = useState()
const firstVersionToShow = changelog.data.findIndex(
(change) => change.version === baseVersion
@@ -63,50 +47,47 @@ const ChangeLogViewer = ({
)
const availableVersionsToCompareWith = allVersions.slice(
- firstVersionToShow + 1,
- changelog.data?.length - firstVersionToShow
+ firstVersionToShow + 1
)
- const getType = (type) => {
+ const getFormattedChangeType = ({ type, isTranslation }) => {
+ if (isTranslation) {
+ return null
+ }
if (type === 'Bug Fixes') {
- return 'Fix'
+ return 'Fix: '
}
if (type == 'Features') {
- return 'Feature'
+ return 'Feature: '
}
- return type
+ return type + ': '
+ }
+
+ if (!data || loading || !baseVersion) {
+ return
+ }
+
+ if (!data.changelog) {
+ return (
+
+
+ No change log available for this app.
+
+
+ )
}
+
return (
-
-
-
-
Changes in
-
-
- setBaseVersion(selected)
- }
- selected={baseVersion}
- dense
- >
- {allVersions.map((version) => {
- return (
-
- )
- })}
-
-
+
+
+
Show changes included from:
+
setCompareVersion(selected)}
- selected={versionToCompareWith}
+ onChange={({ selected }) => setBaseVersion(selected)}
+ selected={baseVersion}
dense
>
- {availableVersionsToCompareWith.map((version) => {
+ {allVersions.map((version) => {
return (
-
-
-
-
-
-
-
- Version
-
-
- Changes
-
-
- {changesToShow.map((entry) => {
- const { version } = entry
-
- return (
-
-
- {version}
-
-
-
- {entry.changeSummary.map(
- (change, i) => {
- if (
- change.isTranslation
- ) {
- return (
- - to
+
setCompareVersion(selected)}
+ selected={versionToCompareWith}
+ dense
+ >
+ {availableVersionsToCompareWith.map((version) => {
+ return (
+
+ )
+ })}
+
+
+
+
+ {changesToShow.map((entry) => {
+ const { version } = entry
+
+ return (
+
+
+ Version {version}
+
+
+
+ {entry.changeSummary.map(
+ (change, i) => {
+ return (
+ // todo: find a better key than i
+ -
+
+ {getFormattedChangeType(
+ change
+ )}
+
+ {change.isTranslation ? (
+
+ Translations
+ sync
+
+ ) : (
+ change.text
+ )}
+ {change.link ? (
+ <>
+ {' | '}
+
- Translations
- update
-
- )
- }
- return (
- -
-
- {getType(
- change.type
- )}
-
- : {change.text}
- {change.link ? (
- <>
- {' | '}
-
- link
-
- >
- ) : null}
-
- )
- }
- )}
-
-
-
- )
- })}
-
-
-
-
-
+ link
+
+ >
+ ) : null}
+
+ )
+ }
+ )}
+
+
+
+ )
+ })}
+
+
+
)
}
+ChangeLogViewer.propTypes = {
+ appId: PropTypes.string,
+ latestVersion: PropTypes.string,
+}
export default ChangeLogViewer
diff --git a/client/src/components/Versions/ChangeLogViewer.module.css b/client/src/components/Versions/ChangeLogViewer.module.css
index 31e9ceb9..636e6edb 100644
--- a/client/src/components/Versions/ChangeLogViewer.module.css
+++ b/client/src/components/Versions/ChangeLogViewer.module.css
@@ -1,6 +1,13 @@
-.cardContainer {
- margin: var(--spacers-dp8);
- padding: var(--spacers-dp8);
+@value m-medium from 'src/styles/breakpoints.css';
+
+.container {
+ padding: var(--spacers-dp12);
+}
+
+@media m-medium {
+ .container {
+ padding: var(--spacers-dp24);
+ }
}
.header {
@@ -37,6 +44,7 @@
.versionHeader {
font-size: 1.1em;
+ font-weight: bold;
}
.translation {
@@ -45,10 +53,19 @@
a.changeLink {
color: var(--colors-blue600);
+ text-decoration: underline;
}
a.changeLink:hover,
a.changeLink:focus {
color: var(--colors-blue700);
- text-decoration: underline;
+ text-decoration: none;
+}
+
+.disabledText {
+ color: var(--colors-grey700);
}
+
+.changelogContainer :global(table) {
+ border: 0 !important
+}
\ No newline at end of file
diff --git a/client/src/components/Versions/Filters/Filters.js b/client/src/components/Versions/Filters/Filters.js
index 6d380d5d..ae6a9096 100644
--- a/client/src/components/Versions/Filters/Filters.js
+++ b/client/src/components/Versions/Filters/Filters.js
@@ -37,17 +37,26 @@ const Filters = ({
)}
-
+
- setDhisVersionFilter(selected)
- }
+ onChange={({ selected }) => {
+ if (!selected) {
+ setDhisVersionFilter('-1')
+ } else {
+ setDhisVersionFilter(selected)
+ }
+ }}
>
+
{dhisVersions.map((dhisVersion) => (
{
return { availableChannels, channelsFilter, setChannelsFilter }
}
-const Versions = ({
- appId,
- renderDeleteVersionButton,
- showDownloadCount,
- hasChangelog,
-}) => {
+const Versions = ({ appId, renderDeleteVersionButton, showDownloadCount }) => {
const { availableChannels, channelsFilter, setChannelsFilter } =
useChannels(appId)
- const [dhisVersionFilter, setDhisVersionFilter] = useState('')
+ const [dhisVersionFilter, setDhisVersionFilter] = useState('-1')
const params = useMemo(
() => ({
pageSize: 5,
- minDhisVersion: dhisVersionFilter
- ? `lte:${dhisVersionFilter}`
- : undefined,
- maxDhisVersion: dhisVersionFilter
- ? `gte:${dhisVersionFilter}`
- : undefined,
+ minDhisVersion:
+ dhisVersionFilter != '-1'
+ ? `lte:${dhisVersionFilter}`
+ : undefined,
+ maxDhisVersion:
+ dhisVersionFilter != '-1'
+ ? `gte:${dhisVersionFilter}`
+ : undefined,
channel: Array.from(channelsFilter).join(),
}),
[dhisVersionFilter, Array.from(channelsFilter).join()]
@@ -79,23 +75,6 @@ const Versions = ({
const versions = data ?? []
- const [changelogVisible, setChangelogVisible] = useState(false)
- const showChangeLog = () => {
- setChangelogVisible(!changelogVisible)
- }
-
- const onCloseChangelog = () => {
- setChangelogVisible(false)
- }
-
- const [compareVersion, setCompareVersion] = useState()
- const [baseVersion, setBaseVersion] = useState(versions?.[0]?.version)
-
- const changeBaseVersion = (version) => {
- setBaseVersion(version)
- setCompareVersion()
- }
-
if (error) {
return (
@@ -113,15 +92,13 @@ const Versions = ({
}
return (
-
+
{versions.length > 0 ? (
)}
- {changelogVisible && (
-
- )}
)
}
@@ -164,7 +131,6 @@ const Versions = ({
Versions.propTypes = {
appId: PropTypes.string.isRequired,
renderDeleteVersionButton: PropTypes.func,
- hasChangelog: PropTypes.bool,
}
export default Versions
diff --git a/client/src/components/Versions/Versions.module.css b/client/src/components/Versions/Versions.module.css
index 2bcb0934..4413193f 100644
--- a/client/src/components/Versions/Versions.module.css
+++ b/client/src/components/Versions/Versions.module.css
@@ -1,7 +1,3 @@
-.versionsContainer {
- border: 1px solid var(--colors-grey300);
-}
-
.noVersions {
display: inline-block;
padding: var(--spacers-dp8);
diff --git a/client/src/components/Versions/VersionsTable/VersionsTable.js b/client/src/components/Versions/VersionsTable/VersionsTable.js
index 6842ff3f..401c3ea4 100644
--- a/client/src/components/Versions/VersionsTable/VersionsTable.js
+++ b/client/src/components/Versions/VersionsTable/VersionsTable.js
@@ -48,9 +48,9 @@ const VersionsTable = ({
Version
- Channel
- DHIS2 version compatibility
- Upload date
+ Date released
+ DHIS2 compatibility
+ Release channel
{showDownloadCount && (
Downloads
)}
@@ -61,8 +61,12 @@ const VersionsTable = ({
{versions.map((version) => (
{version.version}
-
- {appChannelToDisplayName[version.channel]}
+
+
+ {new Date(
+ version.createdAt
+ ).toLocaleDateString()}
+
{renderDhisVersionsCompatibility(
@@ -70,13 +74,10 @@ const VersionsTable = ({
version.maxDhisVersion
)}
-
-
- {new Date(
- version.createdAt
- ).toLocaleDateString()}
-
+
+ {appChannelToDisplayName[version.channel]}
+
{showDownloadCount && (
{version.downloadCount}
@@ -84,13 +85,12 @@ const VersionsTable = ({
)}
-
+ Download
{renderDeleteVersionButton &&
renderDeleteVersionButton(version)}
diff --git a/client/src/components/Versions/VersionsTable/VersionsTable.module.css b/client/src/components/Versions/VersionsTable/VersionsTable.module.css
index 30bb7259..39b8608e 100644
--- a/client/src/components/Versions/VersionsTable/VersionsTable.module.css
+++ b/client/src/components/Versions/VersionsTable/VersionsTable.module.css
@@ -12,3 +12,16 @@
.channelNameCell {
color: var(--colors-grey700);
}
+
+
+a.link {
+ color: var(--colors-blue600);
+ text-decoration: underline;
+}
+
+a.link:hover,
+a.link:focus {
+ color: var(--colors-blue700);
+ text-decoration: none;
+}
+
diff --git a/client/src/pages/AppView/AppView.js b/client/src/pages/AppView/AppView.js
index eceb3cd2..923ff05a 100644
--- a/client/src/pages/AppView/AppView.js
+++ b/client/src/pages/AppView/AppView.js
@@ -3,12 +3,18 @@ import {
CircularLoader,
NoticeBox,
Card,
- Tag,
Divider,
Button,
+ TabBar,
+ Tab,
+ IconUser16,
+ IconTerminalWindow16,
} from '@dhis2/ui'
import classnames from 'classnames'
import PropTypes from 'prop-types'
+import { useHistory } from 'react-router-dom'
+import PluginTag from '../../components/PluginTag/PluginTag'
+import ChangeLogViewer from '../../components/Versions/ChangeLogViewer'
import styles from './AppView.module.css'
import config from 'config'
import { useQueryV1 } from 'src/api'
@@ -17,7 +23,6 @@ import AppIcon from 'src/components/AppIcon/AppIcon'
import Screenshots from 'src/components/Screenshots/Screenshots'
import Versions from 'src/components/Versions/Versions'
import { renderDhisVersionsCompatibility } from 'src/lib/render-dhis-versions-compatibility'
-import PluginTag from '../../components/PluginTag/PluginTag'
const HeaderSection = ({
appName,
@@ -27,43 +32,93 @@ const HeaderSection = ({
hasPlugin,
pluginType,
organisationSlug,
-}) => (
-
-
-
-
{appName}
-
- by{' '}
-
- {appDeveloper}
-
-
-
-
{appType}
- {hasPlugin && (
-
+ latestVersion,
+ sourceUrl,
+}) => {
+ const history = useHistory()
+ return (
+
+
+
+
{appName}
+
+
+
+
+ {appType}
+
+ {hasPlugin && (
+
+ )}
+
+
+
+
+
+
+ {
+ config.ui.appChannelToDisplayName[
+ latestVersion.channel
+ ]
+ }{' '}
+ release v{latestVersion.version}.
+
+
+ Compatible with DHIS2{' '}
+ {renderDhisVersionsCompatibility(
+ latestVersion.minDhisVersion,
+ latestVersion.maxDhisVersion
+ )}
+ .
+
+
+ {sourceUrl && (
+ <>
+
+ Source code
+
+ >
)}
-
-
-)
+
+ )
+}
HeaderSection.propTypes = {
appDeveloper: PropTypes.string.isRequired,
appName: PropTypes.string.isRequired,
appType: PropTypes.string.isRequired,
- pluginType: PropTypes.string,
+ organisationSlug: PropTypes.string.isRequired,
hasPlugin: PropTypes.bool,
+ latestVersion: PropTypes.object,
logoSrc: PropTypes.string,
- organisationSlug: PropTypes.string.isRequired,
+ pluginType: PropTypes.string,
+ sourceUrl: PropTypes.string,
}
-const AboutSection = ({ appDescription, latestVersion, sourceUrl }) => (
+const AboutSection = ({ appDescription }) => (
About this app
@@ -78,40 +133,22 @@ const AboutSection = ({ appDescription, latestVersion, sourceUrl }) => (
)}
-
-
-
-
-
-
- {config.ui.appChannelToDisplayName[latestVersion.channel]}{' '}
- release v{latestVersion.version}.
-
-
- Compatible with DHIS2{' '}
- {renderDhisVersionsCompatibility(
- latestVersion.minDhisVersion,
- latestVersion.maxDhisVersion
- )}
- .
-
-
- {sourceUrl && (
- <>
-
-
- Source code
-
- >
- )}
-
)
+AboutSection.propTypes = {
+ appDescription: PropTypes.string,
+}
+
const AppView = ({ match }) => {
const { appId } = match.params
const { data: app, error } = useQueryV1(`apps/${appId}`)
+ const history = useHistory()
+
+ const selectedTab =
+ new URLSearchParams(window.location.search).get('tab') ?? 'about'
+
if (error) {
return (
@@ -136,6 +173,10 @@ const AppView = ({ match }) => {
const versions = app.versions.sort((a, b) => b.created - a.created)
const latestVersion = versions[0]
+ const selectTab = (tabName) => () => {
+ history.push('?tab=' + tabName)
+ }
+
return (
{
logoSrc={logoSrc}
hasPlugin={app.hasPlugin}
pluginType={app.pluginType}
- />
-
-
-
- {screenshots.length > 0 && (
+
+
+ About
+
+
+ Previous releases
+
+
+ Change log
+
+ {/*
+ More apps by {appDeveloper}
+ */}
+
+ {selectedTab === 'about' && (
<>
-
-
+ {screenshots.length > 0 && (
+ <>
+
+ >
+ )}
+
>
)}
-
-
- All versions of this application
-
-
-
+
+ {selectedTab === 'previous-releases' && (
+
+ )}
+
+ {selectedTab === 'changelog' && (
+
+
+
+ )}
)
}
diff --git a/client/src/pages/AppView/AppView.module.css b/client/src/pages/AppView/AppView.module.css
index 1108b54f..d10b97f2 100644
--- a/client/src/pages/AppView/AppView.module.css
+++ b/client/src/pages/AppView/AppView.module.css
@@ -18,7 +18,7 @@
.appCardHeader {
display: grid;
- grid-template-columns: 72px auto 67px;
+ grid-template-columns: 72px auto 267px;
grid-gap: var(--spacers-dp12);
}
@@ -29,25 +29,36 @@
margin-bottom: 6px;
}
-.appCardDeveloper {
- display: block;
+.appTags {
+ flex-direction: row;
+ display: flex;
+ align-items: center;
+ gap: var(--spacers-dp16)
+}
+
+.tagWithIcon {
+ display: flex;
+ gap: var(--spacers-dp4);
font-size: 16px;
- margin-bottom: var(--spacers-dp8);
color: var(--colors-grey800);
}
+.topActionButtons {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacers-dp4);
+}
+
+.topActionButtons :global(button) {
+ width: 100%;
+}
+
.appCardType {
font-size: 13px;
color: var(--colors-grey700);
margin-right: 5px;
}
-.appCardTypeContainer {
- flex-direction: 'row';
- display: 'flex';
- align-items: 'center';
-}
-
.appCardHeading {
font-weight: 500;
font-size: 16px;
@@ -77,7 +88,7 @@
.latestVersionDescription {
margin-top: var(--spacers-dp8);
- margin-bottom: 0;
+ margin-bottom: var(--spacers-dp8);
}
.latestVersionDescription span {
@@ -109,3 +120,11 @@
.sourceUrl:focus {
color: var(--colors-grey900);
}
+
+.organisationLink {
+ color: var(--colors-grey800);
+}
+
+.organisationLink:hover {
+ text-decoration: underline;
+}
\ No newline at end of file