From 29b4b5dd80b0e112166a875c8df4bd5bc82d98ff Mon Sep 17 00:00:00 2001 From: David Douglas Date: Mon, 14 Aug 2017 18:58:10 +0100 Subject: [PATCH] Create dashboard form validation errors messages --- client/src/actions/ConfigurationsActions.ts | 2 +- client/src/components/Home/index.tsx | 24 ++++++++++++++++++--- client/src/stores/ConfigurationsStore.ts | 11 +++++++++- server/routes/api.js | 6 +++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/client/src/actions/ConfigurationsActions.ts b/client/src/actions/ConfigurationsActions.ts index 2825c89..4339799 100644 --- a/client/src/actions/ConfigurationsActions.ts +++ b/client/src/actions/ConfigurationsActions.ts @@ -8,7 +8,7 @@ interface IConfigurationsActions { createDashboard(dashboard: IDashboardConfig): any; loadTemplate(id: string): any; saveConfiguration(dashboard: IDashboardConfig): any; - failure(error: any): void; + failure(error: any): any; submitDashboardFile(content: string, fileName: string): void; convertDashboardToString(dashboard: IDashboardConfig): string; deleteDashboard(id: string): any; diff --git a/client/src/components/Home/index.tsx b/client/src/components/Home/index.tsx index 293d431..7649a2f 100644 --- a/client/src/components/Home/index.tsx +++ b/client/src/components/Home/index.tsx @@ -43,6 +43,7 @@ const styles = { interface IHomeState extends ISetupConfig { loaded?: boolean; + errors?: any; templates?: IDashboardConfig[]; selectedTemplateId?: string; template?: IDashboardConfig; @@ -68,6 +69,7 @@ export default class Home extends React.Component { clientSecret: '', issuer: '', loaded: false, + errors: null, templates: [], selectedTemplateId: null, @@ -106,11 +108,17 @@ export default class Home extends React.Component { this.downloadTemplate = this.downloadTemplate.bind(this); } - updateConfiguration(state: {templates: IDashboardConfig[], template: IDashboardConfig, creationState: string}) { + updateConfiguration(state: { + templates: IDashboardConfig[], + template: IDashboardConfig, + creationState: string, + errors: any + }) { this.setState({ templates: state.templates || [], template: state.template, - creationState: state.creationState + creationState: state.creationState, + errors: state.errors, }); if (this.state.stage === 'requestDownloadTemplate') { this.downloadTemplate(this.state.template); @@ -235,7 +243,7 @@ export default class Home extends React.Component { } render() { - let { loaded, redirectUrl, templates, selectedTemplateId, template } = this.state; + let { errors, loaded, redirectUrl, templates, selectedTemplateId, template } = this.state; let { importVisible } = this.state; let { importedFileContent, fileName } = this.state; let { infoVisible, infoHtml, infoTitle } = this.state; @@ -252,6 +260,14 @@ export default class Home extends React.Component { return null; } + let error = false; + let errorText = null; + const isBadId = errors && errors.error && errors.error.message && errors.error.type && errors.error.type === 'id'; + if (isBadId) { + errorText = errors.error.message; + error = true; + } + let createCard = (tmpl, index) => (
{ defaultValue={template && template.id || ''} lineDirection="center" placeholder="Choose an ID for the dashboard (will be used in the url)" + error={error} + errorText={errorText} /> implements IConfigurationsStoreState { @@ -26,6 +27,7 @@ class ConfigurationsStore extends AbstractStoreModel connections: IDictionary; connectionsMissing: boolean; loaded: boolean; + errors: any; constructor() { super(); @@ -38,12 +40,14 @@ class ConfigurationsStore extends AbstractStoreModel this.connections = {}; this.connectionsMissing = false; this.loaded = false; + this.errors = null; this.bindListeners({ loadConfiguration: configurationActions.loadConfiguration, loadDashboard: configurationActions.loadDashboard, loadTemplate: configurationActions.loadTemplate, - createDashboard: configurationActions.createDashboard + createDashboard: configurationActions.createDashboard, + failure: configurationActions.failure }); configurationActions.loadConfiguration(); @@ -84,6 +88,7 @@ class ConfigurationsStore extends AbstractStoreModel } createDashboard(result: { dashboard: IDashboardConfig }) { + this.errors = null; this.creationState = 'successful'; } @@ -103,6 +108,10 @@ class ConfigurationsStore extends AbstractStoreModel } } + failure(errors: any) { + this.errors = errors; + } + private getConnections(dashboard: IDashboardConfig): any { let requiredParameters = {}; let dataSources = DataSourceConnector.getDataSources(); diff --git a/server/routes/api.js b/server/routes/api.js index e3d43ee..72ccd7e 100644 --- a/server/routes/api.js +++ b/server/routes/api.js @@ -269,12 +269,12 @@ router.put('/templates/:id', (req, res) => { }); }); -router.put('/dashboards/:id', (req, res) => { +router.put('/dashboards/:id?', (req, res) => { let { id } = req.params || {}; let { script } = req.body || {}; if (!id || !script) { - return res.end({ error: 'No id or script were supplied for the new dashboard' }); + return res.json({ errors: {message: 'No id or script were supplied for the new dashboard', type: 'id'}} ); } const { privateDashboard } = paths(); @@ -283,7 +283,7 @@ router.put('/dashboards/:id', (req, res) => { let dashboardExists = fs.existsSync(dashboardPath); if (dashboardFile || dashboardExists) { - return res.json({ errors: ['Dashboard id or filename already exists'] }); + return res.json({ errors: {message: 'Dashboard id or filename already exists', type: 'id'}} ); } fs.writeFile(dashboardPath, script, err => {