Skip to content

Commit

Permalink
feat(admin): allow tagging on the chart basic tab
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelgerber committed Jan 30, 2025
1 parent 0b15689 commit a774ccc
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 8 deletions.
11 changes: 11 additions & 0 deletions adminSiteClient/ChartEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
omit,
CHART_VIEW_PROPS_TO_OMIT,
} from "@ourworldindata/utils"
import { DbChartTagJoin } from "@ourworldindata/types"
import { action, computed, observable, runInAction } from "mobx"
import { BAKED_GRAPHER_URL } from "../settings/clientSettings.js"
import {
Expand Down Expand Up @@ -48,6 +49,8 @@ export interface ChartEditorManager extends AbstractChartEditorManager {
references: References | undefined
redirects: ChartRedirect[]
pageviews?: RawPageview
tags?: DbChartTagJoin[]
availableTags?: DbChartTagJoin[]
}

export class ChartEditor extends AbstractChartEditor<ChartEditorManager> {
Expand All @@ -71,6 +74,14 @@ export class ChartEditor extends AbstractChartEditor<ChartEditorManager> {
return this.manager.pageviews
}

@computed get tags() {
return this.manager.tags
}

@computed get availableTags() {
return this.manager.availableTags
}

/** parent variable id, derived from the config */
@computed get parentVariableId(): number | undefined {
return getParentVariableIdFromChartConfig(this.liveConfig)
Expand Down
26 changes: 25 additions & 1 deletion adminSiteClient/ChartEditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
getParentVariableIdFromChartConfig,
RawPageview,
} from "@ourworldindata/utils"
import { GrapherInterface, ChartRedirect } from "@ourworldindata/types"
import {
GrapherInterface,
ChartRedirect,
MinimalTagWithIsTopic,
DbChartTagJoin,
} from "@ourworldindata/types"
import { Admin } from "./Admin.js"
import {
ChartEditor,
Expand All @@ -32,6 +37,8 @@ export class ChartEditorPage
@observable references: References | undefined = undefined
@observable redirects: ChartRedirect[] = []
@observable pageviews?: RawPageview = undefined
@observable tags?: DbChartTagJoin[] = undefined
@observable availableTags?: MinimalTagWithIsTopic[] = undefined

patchConfig: GrapherInterface = {}
parentConfig: GrapherInterface | undefined = undefined
Expand Down Expand Up @@ -114,6 +121,21 @@ export class ChartEditorPage
runInAction(() => (this.pageviews = json.pageviews))
}

async fetchTags(): Promise<void> {
const { grapherId } = this.props
const { admin } = this.context
const json =
grapherId === undefined
? {}
: await admin.getJSON(`/api/charts/${grapherId}.tags.json`)
runInAction(() => (this.tags = json.tags))
}

async fetchAvailableTags() {
const json = (await this.admin.getJSON("/api/tags.json")) as any
this.availableTags = json.tags
}

@computed get admin(): Admin {
return this.context.admin
}
Expand All @@ -129,6 +151,8 @@ export class ChartEditorPage
void this.fetchRefs()
void this.fetchRedirects()
void this.fetchPageviews()
void this.fetchTags()
void this.fetchAvailableTags()
}

componentDidMount(): void {
Expand Down
66 changes: 65 additions & 1 deletion adminSiteClient/EditorBasicTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
ALL_GRAPHER_CHART_TYPES,
GrapherChartType,
GRAPHER_CHART_TYPES,
DbChartTagJoin,
TaggableType,
} from "@ourworldindata/types"
import {
DimensionSlot,
Expand Down Expand Up @@ -41,12 +43,14 @@ import {
} from "react-beautiful-dnd"
import { AbstractChartEditor } from "./AbstractChartEditor.js"
import { EditorDatabase } from "./ChartEditorView.js"
import { isChartEditorInstance } from "./ChartEditor.js"
import { ChartEditor, isChartEditorInstance } from "./ChartEditor.js"
import { ErrorMessagesForDimensions } from "./ChartEditorTypes.js"
import {
IndicatorChartEditor,
isIndicatorChartEditorInstance,
} from "./IndicatorChartEditor.js"
import { EditableTags } from "./EditableTags.js"
import { AdminAppContext, AdminAppContextType } from "./AdminAppContext.js"

@observer
class DimensionSlotView<
Expand Down Expand Up @@ -353,6 +357,41 @@ class VariablesSection<
}
}

// eslint-disable-next-line react-refresh/only-export-components
const TagsSection = (props: {
chartId: number | undefined
editor: ChartEditor
onSaveTags: (tags: DbChartTagJoin[]) => void
}) => {
const { tags, availableTags } = props.editor
const canTag = !!props.chartId && tags && availableTags
return (
<Section name="Tags">
{canTag ? (
<>
<EditableTags
onSave={props.onSaveTags}
tags={tags}
suggestions={availableTags}
hasKeyChartSupport
hasSuggestionsSupport
taggable={{
type: TaggableType.Charts,
id: props.chartId,
}}
/>
<small className="form-text text-muted">
Changes to tags will be applied instantly, without the
need to save the chart.
</small>
</>
) : (
<p>Can't tag this chart. Maybe it hasn't been saved yet?</p>
)}
</Section>
)
}

@observer
export class EditorBasicTab<
Editor extends AbstractChartEditor,
Expand All @@ -361,6 +400,9 @@ export class EditorBasicTab<
database: EditorDatabase
errorMessagesForDimensions: ErrorMessagesForDimensions
}> {
static contextType = AdminAppContext
context!: AdminAppContextType

private chartTypeOptionNone = "None"

@action.bound private updateParentConfig() {
Expand Down Expand Up @@ -451,6 +493,20 @@ export class EditorBasicTab<
}
}

@action.bound onSaveTags(tags: DbChartTagJoin[]) {
void this.saveTags(tags)
}

async saveTags(tags: DbChartTagJoin[]) {
const { editor } = this.props
const { grapher } = editor
await this.context.admin.requestJSON(
`/api/charts/${grapher.id}/setTags`,
{ tags },
"POST"
)
}

render() {
const { editor } = this.props
const { grapher } = editor
Expand Down Expand Up @@ -493,6 +549,14 @@ export class EditorBasicTab<
}
/>
)}

{isChartEditorInstance(editor) && (
<TagsSection
chartId={grapher.id}
editor={editor}
onSaveTags={this.onSaveTags}
/>
)}
</div>
)
}
Expand Down
6 changes: 6 additions & 0 deletions adminSiteServer/apiRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ import {
setChartTagsHandler,
updateChart,
deleteChart,
getChartTagsJson,
} from "./apiRoutes/charts.js"

const apiRouter = new FunctionalRouter()
Expand Down Expand Up @@ -183,6 +184,11 @@ getRouteWithROTransaction(
"/charts/:chartId.pageviews.json",
getChartPageviewsJson
)
getRouteWithROTransaction(
apiRouter,
"/charts/:chartId.tags.json",
getChartTagsJson
)
postRouteWithRWTransaction(apiRouter, "/charts", createChart)
postRouteWithRWTransaction(
apiRouter,
Expand Down
21 changes: 21 additions & 0 deletions adminSiteServer/apiRoutes/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DbInsertChartRevision,
DbRawChartConfig,
ChartConfigsTableName,
DbChartTagJoin,
} from "@ourworldindata/types"
import {
diffGrapherConfigs,
Expand Down Expand Up @@ -685,6 +686,26 @@ export async function getChartPageviewsJson(
}
}

export async function getChartTagsJson(
req: Request,
res: e.Response<any, Record<string, any>>,
trx: db.KnexReadonlyTransaction
) {
const chartId = expectInt(req.params.chartId)
const chartTags = await db.knexRaw<DbChartTagJoin>(
trx,
`-- sql
SELECT ct.tagId as id, ct.keyChartLevel, ct.isApproved, t.name
FROM chart_tags ct
JOIN charts c ON c.id=ct.chartId
JOIN tags t ON t.id=ct.tagId
WHERE ct.chartId = ?
`,
[chartId]
)
return { tags: chartTags }
}

export async function createChart(
req: Request,
res: e.Response<any, Record<string, any>>,
Expand Down
7 changes: 1 addition & 6 deletions db/model/Chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,12 +365,7 @@ export async function assignTagsForCharts(
knex: db.KnexReadonlyTransaction,
charts: {
id: number
tags?: {
id: number
name: string
keyChartLevel: number
isApproved: boolean
}[]
tags?: DbChartTagJoin[]
}[]
): Promise<void> {
const chartTags = await db.knexRaw<{
Expand Down

0 comments on commit a774ccc

Please sign in to comment.