UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

306 lines (304 loc) 11.1 kB
/* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import React, { useEffect, useState } from 'react'; import { makeStyles } from 'tss-react/mui'; import Grid from '@mui/material/Grid'; import TextField from '@mui/material/TextField'; import GitForm from './GitForm'; import { defineMessages, useIntl } from 'react-intl'; import PluginFormEngine from '../PluginFormBuilder'; import { fetchAll } from '../../services/sites'; const useStyles = makeStyles()(() => ({ form: { maxWidth: '600px', margin: '0 auto' } })); const messages = defineMessages({ siteId: { id: 'createSiteDialog.siteId', defaultMessage: 'Project ID' }, siteName: { id: 'createSiteDialog.siteName', defaultMessage: 'Project Name' }, description: { id: 'createSiteDialog.description', defaultMessage: 'Description' }, siteFormat: { id: 'createSiteDialog.siteFormat', defaultMessage: 'Max length: 50 characters, consisting of: lowercase letters, numbers & dash (-).' }, idExist: { id: 'createSiteDialog.idExist', defaultMessage: 'The ID already exists.' }, nameExist: { id: 'createSiteDialog.nameExist', defaultMessage: 'The name already exists.' }, fieldMaxLength: { id: 'createSiteDialog.fieldMaxLength', defaultMessage: 'Max length: {maxLength} characters.' }, required: { id: 'createSiteDialog.required', defaultMessage: '{name} is required.' }, cantStart: { id: 'createSiteDialog.cantStart', defaultMessage: 'Project names may not start with zeros, dashes (-) or underscores (_).' }, gitBranch: { id: 'createSiteDialog.gitBranch', defaultMessage: 'Git Branch' }, gitBranchDescription: { id: 'createSiteDialog.gitBranchDescription', defaultMessage: 'Name of the branch this project will track. Pull operations will be done against this branch.' } }); function BlueprintForm(props) { const { classes, cx } = useStyles(); const { inputs, setInputs, onSubmit, blueprint, onCheckNameExist, classes: classesProp } = props; const [sites, setSites] = useState(null); const { formatMessage } = useIntl(); const maxLength = 4000; const siteNameMaxLength = 255; const siteIdMaxLength = 50; useEffect(() => { if (sites === null) { fetchAll({ limit: 1000, offset: 0 }).subscribe(setSites); } }, [sites]); const handleInputChange = (e, type) => { e.persist(); if (e.target.type === 'checkbox') { setInputs({ [e.target.name]: e.target.checked, submitted: false }); } else if (e.target.name === 'siteId') { const invalidSiteId = e.target.value.startsWith('0') || e.target.value.startsWith('-') || e.target.value.startsWith('_'); const siteId = e.target.value .replace(/[^a-zA-Z0-9-_]/g, '') .replace(/_/g, '-') .toLowerCase(); setInputs({ [e.target.name]: siteId, invalidSiteId: invalidSiteId }); } else if (e.target.name === 'siteName') { const currentSiteNameParsed = getSiteId(inputs.siteName); // if current siteId has been edited directly (different to siteName processed) // or if siteId is empty -> do not change it. if (inputs.siteId === currentSiteNameParsed || inputs.siteId === '') { const siteId = getSiteId(e.target.value); const invalidSiteId = siteId.startsWith('0') || siteId.startsWith('-') || siteId.startsWith('_'); const siteIdExist = Boolean(sites.find((site) => site.id === siteId)); setInputs({ [e.target.name]: e.target.value, siteId, invalidSiteId, siteIdExist }); } else { setInputs({ [e.target.name]: e.target.value }); } } else if (type === 'fields') { let parameters = Object.assign(Object.assign({}, inputs.blueprintFields), { [e.target.name]: e.target.value }); setInputs({ blueprintFields: parameters }); } else if (e.target.name === 'gitBranch') { const escapedValue = e.target.value .replace(/\s+|[~^:?*[@\\]/g, '') // It cannot have two or more consecutive dots anywhere. .replace(/\.{2,}/g, '.') // It cannot have two or more consecutive slashes anywhere. .replace(/\/{2,}/g, '/'); setInputs({ [e.target.name]: escapedValue }); } else if (e.target.name === 'repoUrl') { const escapedValue = e.target.value.replace(/\s+/g, ''); setInputs({ [e.target.name]: escapedValue }); } else { setInputs({ [e.target.name]: e.target.value }); } }; const onKeyPress = (event) => { if (event.key === 'Enter') { onSubmit(event); } }; function checkSites(event) { if (sites && sites.find((site) => site.id === event.target.value)) { setInputs({ siteIdExist: true }); } else { setInputs({ siteIdExist: false }); } } function checkSiteNames(event) { if (sites && sites.find((site) => site.name === event.target.value)) { setInputs({ siteNameExist: true }); } else { setInputs({ siteNameExist: false }); } } function renderHelperText(name, value = '', helperText, required, submitted, siteIdExist) { if (value.startsWith('0') || value.startsWith('-') || value.startsWith('_')) { return formatMessage(messages.cantStart); } if (siteIdExist) { return formatMessage(messages.idExist); } else if (required && !value && submitted) { return formatMessage(messages.required, { name: name }); } else { return helperText; } } function getSiteId(siteName) { let siteId = siteName .replace(/[^a-zA-Z0-9_\s-]/g, '') .replace(/[_\s]/g, '-') .toLowerCase(); if (siteId.startsWith('0') || siteId.startsWith('-') || siteId.startsWith('_')) { siteId = siteId.replace(/0|-|_/, ''); } // Site id max length differs from the site name max length, so the id needs to be trimmed to // its max length return siteId.substring(0, siteIdMaxLength); } return React.createElement( 'form', { className: cx(classes.form, classesProp === null || classesProp === void 0 ? void 0 : classesProp.root) }, React.createElement( Grid, { container: true, spacing: 3 }, React.createElement( Grid, { item: true, xs: 12 }, React.createElement(TextField, { id: 'siteName', name: 'siteName', label: formatMessage(messages.siteName), placeholder: 'e.g. Corporate Website', required: true, autoFocus: true, fullWidth: true, onBlur: (event) => checkSiteNames(event), onKeyPress: onKeyPress, onKeyUp: (event) => checkSiteNames(event), onChange: (event) => handleInputChange(event), value: inputs.siteName, inputProps: { maxLength: siteNameMaxLength }, error: (inputs.submitted && !inputs.siteName) || inputs.siteNameExist, helperText: inputs.submitted && !inputs.siteName ? formatMessage(messages.required, { name: formatMessage(messages.siteName) }) : inputs.siteNameExist ? formatMessage(messages.nameExist) : formatMessage(messages.fieldMaxLength, { maxLength: siteNameMaxLength }) }) ), React.createElement( Grid, { item: true, xs: 12 }, React.createElement(TextField, { id: 'siteId', name: 'siteId', label: formatMessage(messages.siteId), placeholder: 'e.g. corporate-website', required: true, fullWidth: true, onBlur: () => onCheckNameExist(inputs.siteId), onKeyPress: onKeyPress, onKeyUp: (event) => checkSites(event), onChange: (event) => handleInputChange(event), value: inputs.siteId, inputProps: { maxLength: siteIdMaxLength }, error: (inputs.submitted && !inputs.siteId) || inputs.siteIdExist || inputs.invalidSiteId, helperText: renderHelperText( formatMessage(messages.siteId), inputs.siteId, formatMessage(messages.siteFormat), true, inputs.submitted, inputs.siteIdExist ) }) ), React.createElement( Grid, { item: true, xs: 12 }, React.createElement(TextField, { id: 'description', fullWidth: true, name: 'description', label: formatMessage(messages.description), multiline: true, onChange: (event) => handleInputChange(event), value: inputs.description, inputProps: { maxLength: maxLength }, helperText: formatMessage(messages.fieldMaxLength, { maxLength: maxLength }) }) ), React.createElement( Grid, { item: true, xs: 12 }, React.createElement(TextField, { id: 'sandboxBranch', name: 'gitBranch', label: formatMessage(messages.gitBranch), fullWidth: true, onKeyPress: onKeyPress, onChange: (event) => handleInputChange(event), placeholder: 'master', value: inputs.gitBranch, helperText: formatMessage(messages.gitBranchDescription) }) ), blueprint.parameters && React.createElement(PluginFormEngine, { parameters: blueprint.parameters, handleInputChange: handleInputChange, submitted: inputs.submitted, fields: inputs.blueprintFields, onKeyPress: onKeyPress }), blueprint.id === 'GIT' && React.createElement(GitForm, { inputs: inputs, setInputs: setInputs, handleInputChange: handleInputChange, onKeyPress: onKeyPress }) ) ); } export default BlueprintForm;