Skip to content

Commit

Permalink
feat: view changes between app versions
Browse files Browse the repository at this point in the history
  • Loading branch information
kabaros committed Dec 19, 2024
1 parent aaec458 commit b60bf5f
Show file tree
Hide file tree
Showing 12 changed files with 875 additions and 114 deletions.
203 changes: 192 additions & 11 deletions client/src/components/Versions/ChangeLogViewer.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,204 @@
import { CenteredContent, CircularLoader, Modal, ModalContent } from '@dhis2/ui'
import {
CircularLoader,
DataTable,
DataTableCell,
DataTableColumnHeader,
DataTableHead,
DataTableRow,
Modal,
ModalContent,
ModalTitle,
SingleSelectField,
SingleSelectOption,
} from '@dhis2/ui'
import { useQuery } from 'src/api'

import React from 'react'
import ReactMarkdown from 'react-markdown'
import Changelog from '../../utils/changelog'
import classnames from 'classnames'
import styles from './ChangeLogViewer.module.css'

// ToDo: this is a just a placeholder for a basic Changelog viewer
const ChangeLogViewer = ({ appId, onCloseChangelog }) => {
const ChangeLogViewer = ({
appId,
onCloseChangelog,
baseVersion,
setBaseVersion,
setCompareVersion,
compareVersion,
}) => {
const { data, loading } = useQuery(`apps/${appId}/changelog`)

if (!data || loading) {
return <CircularLoader large />
}

if (!data.changelog) {
return null
}

const changelog = new Changelog(data.changelog)

const allVersions = changelog.data.map(({ version }) => version)
const changesCount = allVersions.length

const firstVersionToShow = changelog.data.findIndex(
(change) => change.version === baseVersion
)

const versionToCompareWith =
compareVersion ??
changelog.data[
firstVersionToShow + 5 < changesCount
? firstVersionToShow + 5
: changesCount - 2
]?.version

const secondVersionToShow = changelog.data.findIndex(
(change) => change.version === versionToCompareWith
)

const changesToShow = changelog.data.slice(
firstVersionToShow,
secondVersionToShow + 1
)

const availableVersionsToCompareWith = allVersions.slice(
firstVersionToShow + 1,
changelog.data?.length - firstVersionToShow
)

const getType = (type) => {
if (type === 'Bug Fixes') {
return 'Fix'
}
if (type == 'Features') {
return 'Feature'
}
return type
}
return (
<Modal onClose={onCloseChangelog}>
<ModalTitle>
<div className={styles.header}>
<div>Changes in</div>
<div className={styles.headerTitle}>
<SingleSelectField
onChange={({ selected }) =>
setBaseVersion(selected)
}
selected={baseVersion}
dense
>
{allVersions.map((version) => {
return (
<SingleSelectOption
key={version}
label={version}
value={version}
/>
)
})}
</SingleSelectField>
</div>
<SingleSelectField
prefix="compared to"
onChange={({ selected }) => setCompareVersion(selected)}
selected={versionToCompareWith}
dense
>
{availableVersionsToCompareWith.map((version) => {
return (
<SingleSelectOption
key={version}
label={version}
value={version}
/>
)
})}
</SingleSelectField>
</div>
</ModalTitle>
<ModalContent>
<div>
{loading && (
<CenteredContent>
<CircularLoader large />
</CenteredContent>
)}
<ReactMarkdown>{data?.changelog}</ReactMarkdown>
<div className={styles.cardContainer}>
<div className={styles.changelogContainer}>
<DataTable>
<DataTableHead>
<DataTableColumnHeader>
Version
</DataTableColumnHeader>
<DataTableColumnHeader>
Changes
</DataTableColumnHeader>
</DataTableHead>
{changesToShow.map((entry) => {
const { version } = entry

return (
<DataTableRow
key={version}
className={classnames({
[styles.breaking]: entry.isBreaking,
})}
>
<DataTableCell
className={styles.versionHeader}
>
{version}
</DataTableCell>
<DataTableCell>
<ul className={styles.list}>
{entry.changeSummary.map(
(change, i) => {
if (
change.isTranslation
) {
return (
<li
key={i} // ToDo: find a better key
className={
styles.translation
}
>
Translations
update
</li>
)
}
return (
<li key={i}>
<strong>
{getType(
change.type
)}
</strong>
: {change.text}
{change.link ? (
<>
{' | '}
<a
className={
styles.changeLink
}
target="_blank"
href={
change.link
}
>
link
</a>
</>
) : null}
</li>
)
}
)}
</ul>
</DataTableCell>
</DataTableRow>
)
})}
</DataTable>
</div>
</div>
</ModalContent>
</Modal>
Expand Down
54 changes: 54 additions & 0 deletions client/src/components/Versions/ChangeLogViewer.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.cardContainer {
margin: var(--spacers-dp8);;
padding: var(--spacers-dp8);;
}

.header {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}

.headerTitle {
font-weight: bold;
}

.list {
list-style: none;
padding-inline-start: 0px;
}

.changelogContainer {
line-height: 1.4em;
/* max-height: 250px; */
/* overflow-y: auto; */
}

.changeType {
font-style: italic;
}

.breaking {
color: var(--colors-red600);
font-weight: bolder;
}

.versionHeader {
font-size: 1.1em;
}

.translation {
font-style: italic;
}

a.changeLink {
color: var(--colors-blue600);
}

a.changeLink:hover,
a.changeLink:focus {
color: var(--colors-blue700);
text-decoration: underline;
}
59 changes: 41 additions & 18 deletions client/src/components/Versions/Filters/Filters.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { SingleSelectField, SingleSelectOption } from '@dhis2/ui'
import {
Button,
IconClockHistory16,
SingleSelectField,
SingleSelectOption,
} from '@dhis2/ui'
import PropTypes from 'prop-types'
import ChannelCheckbox from '../ChannelCheckbox/ChannelCheckbox'
import styles from './Filters.module.css'
Expand All @@ -12,6 +17,8 @@ const Filters = ({
setChannelsFilter,
dhisVersionFilter,
setDhisVersionFilter,
showChangeLog,
hasChangelog,
}) => {
return (
<div className={styles.versionsFilters}>
Expand All @@ -29,23 +36,37 @@ const Filters = ({
))}
</div>
)}
<div className={styles.dhisVersionSelect}>
<SingleSelectField
dense
placeholder="Select a version"
label="Compatible with DHIS2 version"
clearable
selected={dhisVersionFilter}
onChange={({ selected }) => setDhisVersionFilter(selected)}
>
{dhisVersions.map((dhisVersion) => (
<SingleSelectOption
key={dhisVersion}
label={dhisVersion}
value={dhisVersion}
/>
))}
</SingleSelectField>
<div className={styles.filtersWrapper}>
<div className={styles.dhisVersionSelect}>
<SingleSelectField
dense
placeholder="Select a version"
label="Compatible with DHIS2 version"
clearable
selected={dhisVersionFilter}
onChange={({ selected }) =>
setDhisVersionFilter(selected)
}
>
{dhisVersions.map((dhisVersion) => (
<SingleSelectOption
key={dhisVersion}
label={dhisVersion}
value={dhisVersion}
/>
))}
</SingleSelectField>
</div>
{hasChangelog && (
<div>
<Button
icon={<IconClockHistory16 />}
onClick={showChangeLog}
>
View version changes
</Button>
</div>
)}
</div>
</div>
)
Expand All @@ -57,6 +78,8 @@ Filters.propTypes = {
dhisVersionFilter: PropTypes.string.isRequired,
setChannelsFilter: PropTypes.func.isRequired,
setDhisVersionFilter: PropTypes.func.isRequired,
hasChangelog: PropTypes.bool,
showChangeLog: PropTypes.func,
}

export default Filters
7 changes: 7 additions & 0 deletions client/src/components/Versions/Filters/Filters.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
margin-bottom: var(--spacers-dp8);
}

.filtersWrapper {
display: flex;
flex-direction: row;
align-items: end;
justify-content: space-between;
}

.versionsFilters {
padding: var(--spacers-dp16) var(--spacers-dp8);
}
Expand Down
Loading

0 comments on commit b60bf5f

Please sign in to comment.