Skip to content

Commit

Permalink
🚧 wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Jul 17, 2024
1 parent 2b84012 commit 4cc1e3c
Show file tree
Hide file tree
Showing 14 changed files with 1,563 additions and 304 deletions.
93 changes: 93 additions & 0 deletions adminSiteClient/AbstractChartEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Grapher } from "@ourworldindata/grapher"
import { computed, observable, runInAction, when } from "mobx"

Check warning on line 2 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 2 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 2 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 { 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 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 interface AbstractChartEditorManager {
admin: Admin
grapher: Grapher
database: EditorDatabase
}

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 }
@observable.ref previewMode: "mobile" | "desktop"
@observable.ref showStaticPreview = false
@observable.ref savedGrapherJson: string = ""

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

@computed get isModified(): boolean {
return JSON.stringify(this.grapher.object) !== this.savedGrapherJson
}

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

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

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

abstract saveGrapher(props: { onError?: () => void }): Promise<void>
}
16 changes: 16 additions & 0 deletions adminSiteClient/AdminApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { BulkGrapherConfigEditorPage } from "./BulkGrapherConfigEditor.js"
import { GdocsIndexPage, GdocsMatchProps } from "./GdocsIndexPage.js"
import { GdocsPreviewPage } from "./GdocsPreviewPage.js"
import { GdocsStoreProvider } from "./GdocsStore.js"
import { IndicatorChartEditorPage } from "./IndicatorChartEditorPage.js"

@observer
class AdminErrorMessage extends React.Component<{ admin: Admin }> {
Expand Down Expand Up @@ -154,6 +155,21 @@ export class AdminApp extends React.Component<{
path="/charts"
component={ChartIndexPage}
/>
<Route
exact
path="/indicator-charts/create"
component={IndicatorChartEditorPage}
/>
<Route
exact
path="/indicator-charts/:indicatorId/edit"
component={IndicatorChartEditorPage}
/>
{/* <Route
exact
path="/configs/:uuid/edit"
component={ChartEditorPage}
/> */}
<Route
exact
path={`/${EXPLORERS_ROUTE_FOLDER}/:slug`}
Expand Down
133 changes: 18 additions & 115 deletions adminSiteClient/ChartEditor.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
/* ChartEditor.ts
* ================
*
* Mobx store that represents the current editor state and governs non-UI-related operations.
*
*/

import { Grapher } from "@ourworldindata/grapher"
import {
type DetailDictionary,
type RawPageview,
Expand All @@ -17,26 +9,13 @@ import {
} from "@ourworldindata/utils"
import { computed, observable, runInAction, when } from "mobx"

Check warning on line 10 in adminSiteClient/ChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

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

Check warning on line 10 in adminSiteClient/ChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

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

Check warning on line 10 in adminSiteClient/ChartEditor.ts

View workflow job for this annotation

GitHub Actions / eslint

'when' is defined but never used. Allowed unused vars must match /^_/u
import { BAKED_GRAPHER_URL } from "../settings/clientSettings.js"
import { Admin } from "./Admin.js"
import { EditorFeatures } from "./EditorFeatures.js"
import {
AbstractChartEditor,
AbstractChartEditorManager,
} from "./AbstractChartEditor.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
Expand All @@ -50,37 +29,6 @@ export interface References {
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"
Expand All @@ -93,10 +41,7 @@ export interface DimensionErrorMessage {
displayName?: string
}

export interface ChartEditorManager {
admin: Admin
grapher: Grapher
database: EditorDatabase
export interface ChartEditorManager extends AbstractChartEditorManager {
logs: Log[]
references: References | undefined
redirects: ChartRedirect[]
Expand All @@ -111,49 +56,16 @@ export interface ChartEditorManager {
>
}

interface VariableIdUsageRecord {
variableId: number
usageCount: number
}

export class ChartEditor {
manager: ChartEditorManager
// 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 }
@observable.ref previewMode: "mobile" | "desktop"
@observable.ref showStaticPreview = false
@observable.ref savedGrapherJson: string = ""
export class ChartEditor extends AbstractChartEditor<ChartEditorManager> {
// TODO: necessary?
constructor(props: { manager: ChartEditorManager }) {
super(props)
}

// 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: ChartEditorManager }) {
this.manager = props.manager
this.previewMode =
localStorage.getItem("editorPreviewMode") === "mobile"
? "mobile"
: "desktop"
when(
() => this.grapher.isReady,
() => (this.savedGrapherJson = JSON.stringify(this.grapher.object))
)
}

@computed get isModified(): boolean {
return JSON.stringify(this.grapher.object) !== this.savedGrapherJson
}

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

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

@computed get logs() {
return this.manager.logs
}
Expand Down Expand Up @@ -193,23 +105,6 @@ export class ChartEditor {
return this.grapher.id === undefined
}

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

async loadVariableUsageCounts(): Promise<void> {
const data = (await this.manager.admin.getJSON(
`/api/variables.usages.json`
)) as VariableIdUsageRecord[]
const finalData = new Map(
data.map(({ variableId, usageCount }: VariableIdUsageRecord) => [
variableId,
+usageCount,
])
)
runInAction(() => (this.database.variableUsageCounts = finalData))
}

async saveGrapher({
onError,
}: { onError?: () => void } = {}): Promise<void> {
Expand Down Expand Up @@ -294,3 +189,11 @@ export class ChartEditor {
}
}
}

export const getFullReferencesCount = (references: References): number => {
return (
references.postsWordpress.length +
references.postsGdocs.length +
references.explorers.length
)
}
16 changes: 14 additions & 2 deletions adminSiteClient/ChartEditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@ import { Grapher } from "@ourworldindata/grapher"
import { Admin } from "./Admin.js"
import {
ChartEditor,
EditorDatabase,
Log,
References,
ChartEditorManager,
Dataset,
getFullReferencesCount,
DetailReferences,
FieldWithDetailReferences,
} from "./ChartEditor.js"
import { Dataset, EditorDatabase } from "./AbstractChartEditor.js"
import { EditorBasicTab } from "./EditorBasicTab.js"
import { EditorDataTab } from "./EditorDataTab.js"
import { EditorTextTab } from "./EditorTextTab.js"
Expand Down Expand Up @@ -202,6 +201,19 @@ export class ChartEditorPage
datasets: groupedByNamespace[namespace] as Dataset[],
})
}

const usageData = (await this.context.admin.getJSON(
`/api/variables.usages.json`
)) as {
variableId: number
usageCount: number
}[]
this.database.variableUsageCounts = new Map(
usageData.map(({ variableId, usageCount }) => [
variableId,
+usageCount,
])
)
}

async fetchLogs(): Promise<void> {
Expand Down
6 changes: 3 additions & 3 deletions adminSiteClient/EditorFeatures.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { computed } from "mobx"
import { ChartEditor } from "./ChartEditor.js"
import { AbstractChartEditor } from "./AbstractChartEditor.js"

// Responsible for determining what parts of the editor should be shown, based on the
// type of chart being edited
export class EditorFeatures {
editor: ChartEditor
constructor(editor: ChartEditor) {
editor: AbstractChartEditor
constructor(editor: AbstractChartEditor) {
this.editor = editor
}

Expand Down
Loading

0 comments on commit 4cc1e3c

Please sign in to comment.