From d74646d600beb0e99a00225aedcddebb403a1247 Mon Sep 17 00:00:00 2001 From: Jorge Martinez Date: Tue, 17 Dec 2019 09:01:45 -0500 Subject: [PATCH] Support for multiple languages in project edit view --- .../components/projectEdit/descriptionForm.js | 38 ++----- .../src/components/projectEdit/inputLocale.js | 105 ++++++++++++++++++ .../projectEdit/instructionsForm.js | 27 ++--- .../components/projectEdit/settingsForm.js | 16 ++- frontend/src/views/projectEdit.js | 55 ++++++--- 5 files changed, 181 insertions(+), 60 deletions(-) create mode 100644 frontend/src/components/projectEdit/inputLocale.js diff --git a/frontend/src/components/projectEdit/descriptionForm.js b/frontend/src/components/projectEdit/descriptionForm.js index b6ed34387c..f93cd471bf 100644 --- a/frontend/src/components/projectEdit/descriptionForm.js +++ b/frontend/src/components/projectEdit/descriptionForm.js @@ -1,8 +1,9 @@ import React, { useContext } from 'react'; import { StateContext, styleClasses } from '../../views/projectEdit'; +import { InputLocale } from './inputLocale'; -export const DescriptionForm = () => { +export const DescriptionForm = ({ languages }) => { const { projectInfo, setProjectInfo } = useContext(StateContext); const handleChange = event => { @@ -59,36 +60,19 @@ export const DescriptionForm = () => {
- - + + +
- - + + +
- - + + +
); diff --git a/frontend/src/components/projectEdit/inputLocale.js b/frontend/src/components/projectEdit/inputLocale.js new file mode 100644 index 0000000000..fbe6be4d9a --- /dev/null +++ b/frontend/src/components/projectEdit/inputLocale.js @@ -0,0 +1,105 @@ +import React, { useState, useLayoutEffect, useCallback, useContext } from 'react'; +import { htmlFromMarkdown } from '../../utils/htmlFromMarkdown'; +import { StateContext, styleClasses } from '../../views/projectEdit'; + +export const InputLocale = props => { + const { projectInfo, setProjectInfo } = useContext(StateContext); + const [language, setLanguage] = useState(null); + const [value, setValue] = useState(''); + const [preview, setPreview] = useState(null); + + const locales = projectInfo.projectInfoLocales; + + const updateState = e => { + let selected = locales.filter(f => f.locale === language); + let data = null; + if (selected.length === 0) { + data = { locale: language, [e.target.name]: e.target.value }; + } else { + data = selected[0]; + data[e.target.name] = e.target.value; + } + // create element with new locale. + let newLocales = locales.filter(f => f.locale !== language); + newLocales.push(data); + setProjectInfo({ ...projectInfo, projectInfoLocales: newLocales }); + }; + + const handleChange = e => { + setValue(e.target.value); + if (props.preview !== false) { + const html = htmlFromMarkdown(e.target.value); + setPreview(html); + } + }; + + // Resets preview when language changes. + useLayoutEffect(() => { + setPreview(null); + }, [language]); + + const getValue = useCallback(() => { + const data = locales.filter(l => l.locale === language); + if (data.length > 0) { + return data[0][props.name]; + } else { + return ''; + } + }, [language, locales, props.name]); + + useLayoutEffect(() => { + if (language === null) { + if (projectInfo.defaultLocale) { + setLanguage(projectInfo.defaultLocale); + } + } + }, [projectInfo, language]); + + useLayoutEffect(() => { + const fieldValue = getValue(); + setValue(fieldValue); + }, [getValue]); + + return ( +
+ + {props.children} + {props.type === 'text' ? ( + + ) : ( + + )} + + {preview ?
: null} +
+ ); +}; diff --git a/frontend/src/components/projectEdit/instructionsForm.js b/frontend/src/components/projectEdit/instructionsForm.js index d50e38dc9c..ed1da2d786 100644 --- a/frontend/src/components/projectEdit/instructionsForm.js +++ b/frontend/src/components/projectEdit/instructionsForm.js @@ -1,8 +1,9 @@ import React, { useContext } from 'react'; import { StateContext, styleClasses } from '../../views/projectEdit'; +import { InputLocale } from './inputLocale'; -export const InstructionsForm = () => { +export const InstructionsForm = ({ languages }) => { const { projectInfo, setProjectInfo } = useContext(StateContext); const handleChange = event => { const localesFields = ['instructions', 'perTaskInstructions']; @@ -52,26 +53,14 @@ export const InstructionsForm = () => {

- - + + +
- - + + +

Put here anything that can be useful to users while taking a task. {'{x}'}, {'{y}'} and{' '} {'{z}'} diff --git a/frontend/src/components/projectEdit/settingsForm.js b/frontend/src/components/projectEdit/settingsForm.js index 63056e7a74..1f5ebdf526 100644 --- a/frontend/src/components/projectEdit/settingsForm.js +++ b/frontend/src/components/projectEdit/settingsForm.js @@ -3,7 +3,7 @@ import React, { useContext } from 'react'; import { getEditors } from '../../utils/editorsList'; import { StateContext, styleClasses, handleCheckButton } from '../../views/projectEdit'; -export const SettingsForm = () => { +export const SettingsForm = ({ languages, defaultLocale }) => { const { projectInfo, setProjectInfo } = useContext(StateContext); const handleMappingEditors = event => { @@ -18,9 +18,23 @@ export const SettingsForm = () => { setProjectInfo({ ...projectInfo, validationEditors: editors }); }; + const updateDefaultLocale = event => { + setProjectInfo({ ...projectInfo, defaultLocale: event.target.value }); + }; + const editors = getEditors(); return (

+
+ + +
{editors.map(elm => ( diff --git a/frontend/src/views/projectEdit.js b/frontend/src/views/projectEdit.js index cf1c713aa3..e53ec8aec7 100644 --- a/frontend/src/views/projectEdit.js +++ b/frontend/src/views/projectEdit.js @@ -12,7 +12,6 @@ import { SettingsForm } from '../components/projectEdit/settingsForm'; import { ActionsForm } from '../components/projectEdit/actionsForm'; import { Button } from '../components/button'; import { API_URL } from '../config'; -import { pushToLocalJSONAPI } from '../network/genericJSONRequest'; export const StateContext = React.createContext(); @@ -20,7 +19,7 @@ export const styleClasses = { divClass: 'w-70 pb5 mb4 bb b--grey-light', labelClass: 'f4 barlow-condensed db mb3', pClass: 'db mb3 f5', - inputClass: 'w-80 pa2', + inputClass: 'w-80 pa2 db mb2', numRows: '4', buttonClass: 'bg-blue-dark dib white', modalTitleClass: 'f3 barlow-condensed pb3 bb', @@ -44,6 +43,7 @@ export function ProjectEdit({ id }) { const token = useSelector(state => state.auth.get('token')); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); + const [languages, setLanguages] = useState(null); const [option, setOption] = useState('description'); const [projectInfo, setProjectInfo] = useState({ mappingTypes: [], @@ -61,6 +61,15 @@ export function ProjectEdit({ id }) { ], }); + useLayoutEffect(() => { + async function getSupportedLanguages() { + const res = await fetch(`${API_URL}system/languages/`); + let resp_json = await res.json(); + setLanguages(resp_json.supportedLanguages); + } + getSupportedLanguages(); + }, []); + useLayoutEffect(() => { async function fetchData() { const res = await fetch(`${API_URL}projects/${id}/`); @@ -69,6 +78,7 @@ export function ProjectEdit({ id }) { resp_json = { ...resp_json, projectInfoLocales: array }; setProjectInfo(resp_json); } + fetchData(); }, [id]); @@ -112,9 +122,9 @@ export function ProjectEdit({ id }) { const renderForm = option => { switch (option) { case 'description': - return ; + return ; case 'instructions': - return ; + return ; case 'metadata': return ; case 'imagery': @@ -122,7 +132,7 @@ export function ProjectEdit({ id }) { case 'permissions': return ; case 'settings': - return ; + return ; case 'priority_areas': return ; case 'actions': @@ -133,9 +143,29 @@ export function ProjectEdit({ id }) { }; const saveChanges = () => { - pushToLocalJSONAPI(`projects/${id}/`, JSON.stringify(projectInfo), token, 'PATCH') - .then(res => setSuccess(true)) - .catch(e => setError(e)); + const updateProject = async () => { + const url = `${API_URL}projects/${id}/`; + const headers = { + 'Content-Type': 'application/json', + 'Accept-Language': 'en', + Authorization: `Token ${token}`, + }; + + const options = { + method: 'PATCH', + headers: headers, + body: JSON.stringify(projectInfo), + }; + + const res = await fetch(url, options); + if (res.status !== 200) { + setError(true); + } else { + setSuccess(true); + } + }; + + updateProject(); }; return ( @@ -146,14 +176,13 @@ export function ProjectEdit({ id }) { -

- {success && Project updated successfully} + {success && ( + Project updated successfully + )} {error && Project update failed: {error}}