Skip to content

Commit

Permalink
🚧 wip admin
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Jul 18, 2024
1 parent f308acc commit ed9f620
Show file tree
Hide file tree
Showing 27 changed files with 1,858 additions and 749 deletions.
297 changes: 297 additions & 0 deletions adminSiteClient/AbstractChartEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
/* ChartEditor.ts
* ================
*
* Mobx store that represents the current editor state and governs non-UI-related operations.
*
*/

import { Grapher } from "@ourworldindata/grapher"
import {
type DetailDictionary,

Check warning on line 10 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'DetailDictionary' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 10 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'DetailDictionary' is defined but never used. Allowed unused vars must match /^_/u
type RawPageview,

Check warning on line 11 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'RawPageview' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 11 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'RawPageview' is defined but never used. Allowed unused vars must match /^_/u
Topic,

Check warning on line 12 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'Topic' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 12 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'Topic' is defined but never used. Allowed unused vars must match /^_/u
PostReference,
ChartRedirect,

Check warning on line 14 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'ChartRedirect' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 14 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'ChartRedirect' is defined but never used. Allowed unused vars must match /^_/u
DimensionProperty,

Check warning on line 15 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'DimensionProperty' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 15 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'DimensionProperty' is defined but never used. Allowed unused vars must match /^_/u
Json,
GrapherInterface,
diffGrapherConfigs,
isEqual,
omit,
} from "@ourworldindata/utils"
import { computed, observable, runInAction, when } from "mobx"

Check warning on line 22 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'runInAction' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 22 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'runInAction' is defined but never used. Allowed unused vars must match /^_/u
import { BAKED_GRAPHER_URL } from "../settings/clientSettings.js"

Check warning on line 23 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'BAKED_GRAPHER_URL' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 23 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'BAKED_GRAPHER_URL' is defined but never used. Allowed unused vars must match /^_/u
import { Admin } from "./Admin.js"
import { EditorFeatures } from "./EditorFeatures.js"

type EditorTab = string

interface Variable {
id: number
name: string
}

export interface Dataset {
id: number
name: string
namespace: string
version: string | undefined
variables: Variable[]
isPrivate: boolean
nonRedistributable: boolean
}

export interface Log {
userId: number
userName: string
config: Json
createdAt: string
}

export interface References {
postsWordpress: PostReference[]
postsGdocs: PostReference[]
explorers: string[]
}

export const getFullReferencesCount = (references: References): number => {
return (
references.postsWordpress.length +
references.postsGdocs.length +
references.explorers.length
)
}

export interface Namespace {
name: string
description?: string
isArchived: boolean
}

// This contains the dataset/variable metadata for the entire database
// Used for variable selector interface

export interface NamespaceData {
datasets: Dataset[]
}

export class EditorDatabase {
@observable.ref namespaces: Namespace[]
@observable.ref variableUsageCounts: Map<number, number> = new Map()
@observable dataByNamespace: Map<string, NamespaceData> = new Map()

constructor(json: any) {
this.namespaces = json.namespaces
}
}

export type FieldWithDetailReferences =
| "subtitle"
| "note"
| "axisLabelX"
| "axisLabelY"

export type DetailReferences = Record<FieldWithDetailReferences, string[]>

export interface DimensionErrorMessage {
displayName?: string
}

export interface AbstractChartEditorManager {
admin: Admin
grapher: Grapher
baseGrapherConfig: GrapherInterface

// logs: Log[]
// references: References | undefined
// redirects: ChartRedirect[]
// pageviews?: RawPageview

// database: EditorDatabase
// allTopics: Topic[]
// details: DetailDictionary
// TODO
// invalidDetailReferences: DetailReferences
// errorMessages: Partial<Record<FieldWithDetailReferences, string>>
// errorMessagesForDimensions: Record<
// DimensionProperty,
// DimensionErrorMessage[]
// >
}

interface VariableIdUsageRecord {

Check warning on line 122 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'VariableIdUsageRecord' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 122 in adminSiteClient/AbstractChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'VariableIdUsageRecord' is defined but never used. Allowed unused vars must match /^_/u
variableId: number
usageCount: number
}

export abstract class AbstractChartEditor<
Manager extends AbstractChartEditorManager = AbstractChartEditorManager,
> {
manager: Manager
// Whether the current chart state is saved or not
@observable.ref currentRequest: Promise<any> | undefined
@observable.ref tab: EditorTab = "basic"
@observable.ref errorMessage?: { title: string; content: string } // TODO: used for what?
@observable.ref previewMode: "mobile" | "desktop"
@observable.ref showStaticPreview = false
@observable.ref savedPatchConfig: GrapherInterface = {}

// This gets set when we save a new chart for the first time
// so the page knows to update the url
// @observable.ref newChartId?: number

constructor(props: { manager: Manager }) {
this.manager = props.manager
this.previewMode =
localStorage.getItem("editorPreviewMode") === "mobile"
? "mobile"
: "desktop"
when(
() => this.grapher.isReady,
() => (this.savedPatchConfig = this.patchConfig)
)
}

@computed get fullConfig(): GrapherInterface {
return this.grapher.object
}

@computed get patchConfig(): GrapherInterface {
const { baseGrapherConfig } = this.manager
if (!baseGrapherConfig) return this.fullConfig
return diffGrapherConfigs(this.fullConfig, baseGrapherConfig)
}

@computed get isModified(): boolean {
return !isEqual(
omit(this.patchConfig, "version"),
omit(this.savedPatchConfig, "version")
)
}

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

// @computed get logs() {
// return this.manager.logs
// }

// @computed get references() {
// return this.manager.references
// }

// @computed get redirects() {
// return this.manager.redirects
// }

// @computed get pageviews() {
// return this.manager.pageviews
// }

// @computed get availableTabs(): EditorTab[] {
// const tabs: EditorTab[] = ["basic", "data", "text", "customize"]
// if (this.grapher.hasMapTab) tabs.push("map")
// if (this.grapher.isScatter) tabs.push("scatter")
// if (this.grapher.isMarimekko) tabs.push("marimekko")
// tabs.push("revisions")
// tabs.push("refs")
// tabs.push("export")
// return tabs
// }

// @computed get isNewGrapher() {
// return this.grapher.id === undefined
// }

@computed get features() {
return new EditorFeatures(this)
}

abstract get availableTabs(): EditorTab[]
abstract saveGrapher(props?: { onError?: () => void }): Promise<void>

// async saveGrapher({
// onError,
// }: { onError?: () => void } = {}): Promise<void> {
// const { grapher, isNewGrapher } = this
// const currentGrapherObject = this.grapher.object

// // Chart title and slug may be autocalculated from data, in which case they won't be in props
// // But the server will need to know what we calculated in order to do its job
// if (!currentGrapherObject.title)
// currentGrapherObject.title = grapher.displayTitle

// if (!currentGrapherObject.slug)
// currentGrapherObject.slug = grapher.displaySlug

// const targetUrl = isNewGrapher
// ? "/api/charts"
// : `/api/charts/${grapher.id}`

// const json = await this.manager.admin.requestJSON(
// targetUrl,
// currentGrapherObject,
// isNewGrapher ? "POST" : "PUT"
// )

// if (json.success) {
// if (isNewGrapher) {
// this.newChartId = json.chartId
// this.grapher.id = json.chartId
// this.savedPatchConfig = json.savedPatch
// } else {
// runInAction(() => {
// grapher.version += 1
// this.logs.unshift(json.newLog)
// this.savedPatchConfig = json.savedPatch
// })
// }
// } else onError?.()
// }

// async saveAsNewGrapher(): Promise<void> {
// const currentGrapherObject = this.grapher.object

// const chartJson = { ...currentGrapherObject }
// delete chartJson.id
// delete chartJson.isPublished

// // Need to open intermediary tab before AJAX to avoid popup blockers
// const w = window.open("/", "_blank") as Window

// const json = await this.manager.admin.requestJSON(
// "/api/charts",
// chartJson,
// "POST"
// )
// if (json.success)
// w.location.assign(
// this.manager.admin.url(`charts/${json.chartId}/edit`)
// )
// }

// publishGrapher(): void {
// const url = `${BAKED_GRAPHER_URL}/${this.grapher.displaySlug}`

// if (window.confirm(`Publish chart at ${url}?`)) {
// this.grapher.isPublished = true
// void this.saveGrapher({
// onError: () => (this.grapher.isPublished = undefined),
// })
// }
// }

// unpublishGrapher(): void {
// const message =
// this.references && getFullReferencesCount(this.references) > 0
// ? "WARNING: This chart might be referenced from public posts, please double check before unpublishing. Try to remove the chart anyway?"
// : "Are you sure you want to unpublish this chart?"
// if (window.confirm(message)) {
// this.grapher.isPublished = undefined
// void this.saveGrapher({
// onError: () => (this.grapher.isPublished = true),
// })
// }
// }
}
11 changes: 11 additions & 0 deletions adminSiteClient/AdminApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ export class AdminApp extends React.Component<{
path="/charts"
component={ChartIndexPage}
/>
{/* TODO(inheritance) */}
{/* <Route
exact
path="/indicator-charts/create"
component={ChartEditorPage}
/>
<Route
exact
path="/indicator-charts/:indicatorId/edit"
component={ChartEditorPage}
/> */}
<Route
exact
path={`/${EXPLORERS_ROUTE_FOLDER}/:slug`}
Expand Down
Loading

0 comments on commit ed9f620

Please sign in to comment.