Skip to content

Commit

Permalink
feat(admin): ability to diff chart revisions (#3460)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelgerber committed Apr 9, 2024
1 parent a49f0e0 commit abbc4e5
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 17 deletions.
3 changes: 2 additions & 1 deletion adminSiteClient/ChartEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
PostReference,
ChartRedirect,
DimensionProperty,
Json,
} from "@ourworldindata/utils"
import { computed, observable, runInAction, when } from "mobx"
import { BAKED_GRAPHER_URL } from "../settings/clientSettings.js"
Expand All @@ -39,7 +40,7 @@ export interface Dataset {
export interface Log {
userId: number
userName: string
config: string
config: Json
createdAt: string
}

Expand Down
93 changes: 79 additions & 14 deletions adminSiteClient/EditorHistoryTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,66 @@ import React from "react"
import { observer } from "mobx-react"
import { ChartEditor, Log } from "./ChartEditor.js"
import { Section, Timeago } from "./Forms.js"
import { computed, action } from "mobx"
import { computed, action, observable } from "mobx"
import { copyToClipboard } from "@ourworldindata/utils"
import { dump } from "js-yaml"
import { notification } from "antd"
import { notification, Modal } from "antd"
import ReactDiffViewer, { DiffMethod } from "react-diff-viewer"

function LogCompareModal({
log,
previousLog,
isOpen,
onClose,
}: {
log: Log
previousLog: Log
isOpen: boolean
onClose: () => void
}) {
const titleForLog = (log: Log) => {
const user = log.userName || log.userId.toString()
return <Timeago time={log.createdAt} by={user} />
}

return (
<Modal
open={isOpen}
centered
width="80vw"
onOk={onClose}
onCancel={onClose}
cancelButtonProps={{ style: { display: "none" } }}
>
<div style={{ maxHeight: "50vh", overflowY: "auto" }}>
<ReactDiffViewer
newValue={JSON.stringify(log.config, null, 2)}
oldValue={JSON.stringify(previousLog.config, null, 2)}
leftTitle={titleForLog(previousLog)}
rightTitle={titleForLog(log)}
compareMethod={DiffMethod.WORDS_WITH_SPACE}
styles={{
contentText: {
wordBreak: "break-word",
},
}}
extraLinesSurroundingDiff={2}
/>
</div>
</Modal>
)
}

@observer
class LogRenderer extends React.Component<{
log: Log
previousLog: Log | undefined
applyConfig: (config: any) => void
}> {
@computed get prettyConfig() {
const { log } = this.props
return JSON.stringify(JSON.parse(log.config), undefined, 2)
}
@observable isCompareModalOpen = false

@computed get title() {
const { log } = this.props

const user = log.userName || log.userId.toString()
return (
<>
Expand All @@ -31,16 +73,38 @@ class LogRenderer extends React.Component<{
render() {
const { log } = this.props
const { title } = this
const hasCompareButton = !!this.props.previousLog

return (
<li className="list-group-item d-flex justify-content-between">
<li
className="list-group-item d-flex justify-content-between"
style={{ alignItems: "center" }}
>
{hasCompareButton && (
<LogCompareModal
log={log}
previousLog={this.props.previousLog}
isOpen={this.isCompareModalOpen}
onClose={() => (this.isCompareModalOpen = false)}
/>
)}
<span>{title}</span>
<button
className="align-self-end btn btn-danger"
onClick={() => this.props.applyConfig(log.config)}
>
Restore
</button>
<div className="d-flex" style={{ gap: 6 }}>
{hasCompareButton && (
<button
className="btn btn-secondary"
onClick={() => (this.isCompareModalOpen = true)}
>
Compare <br /> to previous
</button>
)}
<button
className="btn btn-danger"
onClick={() => this.props.applyConfig(log.config)}
>
Restore
</button>
</div>
</li>
)
}
Expand Down Expand Up @@ -94,6 +158,7 @@ export class EditorHistoryTab extends React.Component<{ editor: ChartEditor }> {
<ul key={i} className="list-group">
<LogRenderer
log={log}
previousLog={this.logs[i + 1]} // Needed for comparison, might be undefined
applyConfig={this.applyConfig}
></LogRenderer>
</ul>
Expand Down
8 changes: 6 additions & 2 deletions adminSiteServer/apiRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
TaggableType,
DbChartTagJoin,
pick,
Json,
} from "@ourworldindata/utils"
import {
DbPlainDatasetTag,
Expand Down Expand Up @@ -183,7 +184,7 @@ async function getLogsByChartId(
): Promise<
{
userId: number
config: string
config: Json
userName: string
createdAt: Date
}[]
Expand All @@ -203,7 +204,10 @@ async function getLogsByChartId(
LIMIT 50`,
[chartId]
)
return logs
return logs.map((log) => ({
...log,
config: JSON.parse(log.config),
}))
}

const getReferencesByChartId = async (
Expand Down

0 comments on commit abbc4e5

Please sign in to comment.