diff --git a/config/watcher.ts b/config/watcher.ts index 29c541528..22b8d0d89 100644 --- a/config/watcher.ts +++ b/config/watcher.ts @@ -16,15 +16,15 @@ const config: Config = { }, compileRelayStream: { paths: [ - "core/client/admin/**/*.ts", - "core/client/admin/**/*.tsx", - "core/client/admin/**/*.graphql", "core/client/stream/**/*.ts", "core/client/stream/**/*.tsx", "core/client/stream/**/*.graphql", "core/client/auth/**/*.ts", "core/client/auth/**/*.tsx", "core/client/auth/**/*.graphql", + "core/client/admin/**/*.ts", + "core/client/admin/**/*.tsx", + "core/client/admin/**/*.graphql", "core/server/**/*.graphql", ], ignore: [ @@ -37,6 +37,23 @@ const config: Config = { runOnInit: true, }), }, + compileRelayAdmin: { + paths: [ + "core/client/admin/**/*.ts", + "core/client/admin/**/*.tsx", + "core/client/admin/**/*.graphql", + "core/server/**/*.graphql", + ], + ignore: [ + "core/**/*.d.ts", + "core/**/*.graphql.ts", + "**/test/**/*", + "core/**/*.spec.*", + ], + executor: new CommandExecutor("npm run compile:relay-admin", { + runOnInit: true, + }), + }, compileRelayAuth: { paths: [ "core/client/auth/**/*.ts", @@ -83,6 +100,7 @@ const config: Config = { "compileCSSTypes", "compileRelayStream", "compileRelayAuth", + "compileRelayAdmin", "compileSchema", ], docz: ["runDocz", "compileCSSTypes"], diff --git a/package.json b/package.json index 9a804811f..def2392bc 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "compile:css-types": "tcm src/core/client/", "compile:relay-stream": "ts-node ./scripts/compileRelay --src ./src/core/client/stream --schema tenant", "compile:relay-auth": "ts-node ./scripts/compileRelay --src ./src/core/client/auth --schema tenant", + "compile:relay-admin": "ts-node ./scripts/compileRelay --src ./src/core/client/admin --schema tenant", "compile:schema": "node ./scripts/generateSchemaTypes.js", "docz": "docz", "start": "node dist/index.js", diff --git a/src/core/build/createWebpackConfig.ts b/src/core/build/createWebpackConfig.ts index 61aa28e3c..54cf4b3ca 100644 --- a/src/core/build/createWebpackConfig.ts +++ b/src/core/build/createWebpackConfig.ts @@ -239,6 +239,22 @@ export default function createWebpackConfig({ }, ], }, + { + test: paths.appAdminLocalesTemplate, + use: [ + // This is the locales loader that loads available locales + // from a particular target. + { + loader: "locales-loader", + options: { + ...localesOptions, + // Target specifies the prefix for fluent files to be loaded. + // ${target}-xyz.ftl and ${†arget}.ftl are loaded into the locales. + target: "admin", + }, + }, + ], + }, // Loader for our fluent files. { test: /\.ftl$/, diff --git a/src/core/client/admin/components/App.tsx b/src/core/client/admin/components/App.tsx index dfdcaa37c..1cefa80b6 100644 --- a/src/core/client/admin/components/App.tsx +++ b/src/core/client/admin/components/App.tsx @@ -1,6 +1,13 @@ import React, { StatelessComponent } from "react"; +import { HorizontalGutter } from "talk-ui/components"; +import NavigationContainer from "../containers/NavigationContainer"; -export type View = "MODERATE" | "%future added value"; +export type View = + | "MODERATE" + | "COMMUNITY" + | "STORIES" + | "CONFIGURE" + | "%future added value"; export interface AppProps { view: View; @@ -9,14 +16,23 @@ export interface AppProps { const renderView = (view: View) => { switch (view) { case "MODERATE": - return
Moderate
; + return
MODERATE
; + case "COMMUNITY": + return
COMMUNITY
; + case "STORIES": + return
STORIES
; + case "CONFIGURE": + return
CONFIGURE
; default: throw new Error(`Unknown view ${view}`); } }; const App: StatelessComponent = ({ view }) => ( -
{renderView(view)}
+ + + {renderView(view)} + ); export default App; diff --git a/src/core/client/admin/components/Navigation.tsx b/src/core/client/admin/components/Navigation.tsx new file mode 100644 index 000000000..ec92665ee --- /dev/null +++ b/src/core/client/admin/components/Navigation.tsx @@ -0,0 +1,26 @@ +import React, { StatelessComponent } from "react"; +import { Button, Flex, Typography } from "talk-ui/components"; + +export interface AppProps { + goToModerate: () => void; + goToCommunity: () => void; + goToStories: () => void; + goToConfigure: () => void; +} + +const Navigation: StatelessComponent = ({ + goToModerate, + goToCommunity, + goToStories, + goToConfigure, +}) => ( + + Talk + + + + + +); + +export default Navigation; diff --git a/src/core/client/admin/containers/NavigationContainer.tsx b/src/core/client/admin/containers/NavigationContainer.tsx new file mode 100644 index 000000000..fb3834364 --- /dev/null +++ b/src/core/client/admin/containers/NavigationContainer.tsx @@ -0,0 +1,26 @@ +import React, { Component } from "react"; +import Navigation from "../components/Navigation"; +import { SetViewMutation, withSetViewMutation } from "../mutations"; + +interface NavigationContainerProps { + setView: SetViewMutation; +} + +class NavigationContainer extends Component { + private goToModerate = () => this.props.setView({ view: "MODERATE" }); + private goToCommunity = () => this.props.setView({ view: "COMMUNITY" }); + private goToStories = () => this.props.setView({ view: "STORIES" }); + private goToConfigure = () => this.props.setView({ view: "CONFIGURE" }); + public render() { + return ( + + ); + } +} + +export default withSetViewMutation(NavigationContainer); diff --git a/src/core/client/admin/mutations/SetViewMutation.ts b/src/core/client/admin/mutations/SetViewMutation.ts new file mode 100644 index 000000000..0fe13291a --- /dev/null +++ b/src/core/client/admin/mutations/SetViewMutation.ts @@ -0,0 +1,19 @@ +import { commitLocalUpdate, Environment } from "relay-runtime"; + +import { createMutationContainer } from "talk-framework/lib/relay"; +import { LOCAL_ID } from "talk-framework/lib/relay/withLocalStateContainer"; + +export interface SetViewInput { + view: "MODERATE" | "COMMUNITY" | "STORIES" | "CONFIGURE"; +} + +export type SetViewMutation = (input: SetViewInput) => Promise; + +export async function commit(environment: Environment, input: SetViewInput) { + return commitLocalUpdate(environment, store => { + const record = store.get(LOCAL_ID)!; + record.setValue(input.view, "view"); + }); +} + +export const withSetViewMutation = createMutationContainer("setView", commit); diff --git a/src/core/client/admin/mutations/index.ts b/src/core/client/admin/mutations/index.ts new file mode 100644 index 000000000..50d8d059b --- /dev/null +++ b/src/core/client/admin/mutations/index.ts @@ -0,0 +1 @@ +export { withSetViewMutation, SetViewMutation } from "./SetViewMutation"; diff --git a/src/core/server/app/router.ts b/src/core/server/app/router.ts index ff98c1c45..62237690a 100644 --- a/src/core/server/app/router.ts +++ b/src/core/server/app/router.ts @@ -50,6 +50,9 @@ async function createTenantRouter(app: AppOptions, options: RouterOptions) { // Setup auth routes. router.use("/auth", createNewAuthRouter(app, options)); + // Handle the admin handler. + router.get("/admin", adminHandler); + // Tenant API router.use( "/graphql", @@ -154,8 +157,5 @@ export async function createRouter(app: AppOptions, options: RouterOptions) { // Handle the stream handler. router.get("/embed/stream", cacheHeadersMiddleware("1h"), streamHandler); - // Handle the stream handler. - router.get("/admin", adminHandler); - return router; }