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;
}