From fe9acee9edb03f3f454b723e1f0da2c9e7a80483 Mon Sep 17 00:00:00 2001 From: brunoravera <bruno@process.st> Date: Sat, 9 Mar 2024 17:31:15 -0300 Subject: [PATCH] add pattern form --- package.json | 5 +- web/package.json | 5 +- web/src/design/theme.ts | 7 +- .../add-pattern/add-pattern-button.tsx | 129 ++++++++++++++++++ web/src/pages/Patterns/add-pattern/index.ts | 1 + web/src/pages/Patterns/add-pattern/schema.ts | 18 +++ web/src/pages/Patterns/patterns.tsx | 15 +- yarn.lock | 108 ++++++++++++++- 8 files changed, 279 insertions(+), 9 deletions(-) create mode 100644 web/src/pages/Patterns/add-pattern/add-pattern-button.tsx create mode 100644 web/src/pages/Patterns/add-pattern/index.ts create mode 100644 web/src/pages/Patterns/add-pattern/schema.ts diff --git a/package.json b/package.json index e08e218..2d45e38 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,10 @@ "{}": false } } - ] + ], + "jsx-a11y/no-autofocus": [ 2, { + "ignoreNonDOM": true + }] } }, "engines": { diff --git a/web/package.json b/web/package.json index 592925f..2e0939f 100644 --- a/web/package.json +++ b/web/package.json @@ -18,13 +18,16 @@ "@redwoodjs/forms": "6.5.1", "@redwoodjs/router": "6.5.1", "@redwoodjs/web": "6.5.1", + "@types/yup": "0.29.7", "axios": "^1.6.7", + "formik": "^2.4.5", "framer-motion": "^9", "prop-types": "15.8.1", "react": "18.2.0", "react-dom": "18.2.0", "react-query": "^3.39.3", - "ts-pattern": "^5.0.8" + "ts-pattern": "^5.0.8", + "yup": "0.29.3" }, "devDependencies": { "@chakra-ui/storybook-addon": "^5.1.0", diff --git a/web/src/design/theme.ts b/web/src/design/theme.ts index cd118ad..ede6bc7 100644 --- a/web/src/design/theme.ts +++ b/web/src/design/theme.ts @@ -1,4 +1,4 @@ -import { ThemeConfig, extendTheme } from '@chakra-ui/react' +import { ThemeConfig, extendTheme, Button } from '@chakra-ui/react' import { tableTheme } from './table-theme' @@ -15,7 +15,10 @@ export const theme = extendTheme({ currentColor: 'currentColor', current: 'currentColor', }, - components: { Table: tableTheme }, + components: { + Table: tableTheme, + Button, + }, radii: { none: '0', xs: '2px', diff --git a/web/src/pages/Patterns/add-pattern/add-pattern-button.tsx b/web/src/pages/Patterns/add-pattern/add-pattern-button.tsx new file mode 100644 index 0000000..8c8c6c5 --- /dev/null +++ b/web/src/pages/Patterns/add-pattern/add-pattern-button.tsx @@ -0,0 +1,129 @@ +import React from 'react' + +import { + Button, + Modal, + ModalHeader, + ButtonProps, + useDisclosure, + ModalCloseButton, + ModalBody, + ModalOverlay, + ModalContent, + Text, + HStack, + VStack, + FormControl, + FormLabel, + Input, + ModalFooter, + FormErrorMessage, + Textarea, +} from '@chakra-ui/react' +import { useFormik } from 'formik' + +import { CreatePatternSchema, createPatternSchema } from './schema' + +const FORM_ID = 'add-pattern-form' + +export const AddPatternButton: React.FC<ButtonProps> = () => { + const { isOpen, onClose, onOpen } = useDisclosure() + + const handleOnSubmit = (values: CreatePatternSchema) => { + console.log(values) + } + + const { + handleSubmit, + handleReset, + handleChange, + setTouched, + values, + isSubmitting, + errors, + touched, + isValid, + } = useFormik({ + enableReinitialize: true, + initialValues: { + title: '', + description: '', + }, + validateOnBlur: true, + validateOnChange: true, + validateOnMount: true, + validationSchema: createPatternSchema, + onSubmit: handleOnSubmit, + }) + return ( + <> + <Button variant="solid" colorScheme="blue" onClick={onOpen}> + Crear + </Button> + <Modal isOpen={isOpen} onClose={onClose}> + <ModalOverlay /> + <ModalContent> + <ModalHeader> + <Text fontSize="md">Crear un nuevo patron</Text> + </ModalHeader> + <ModalCloseButton /> + <form id={FORM_ID} onSubmit={handleSubmit} onReset={handleReset}> + <ModalBody> + <VStack> + <FormControl isInvalid={Boolean(errors.title) && touched.title}> + <FormLabel> + <Text fontSize="sm">Titulo</Text> + </FormLabel> + <Input + name="title" + autoFocus + value={values.title} + onChange={(e) => { + setTouched({ ...touched, title: true }) + handleChange(e) + }} + /> + <FormErrorMessage>{errors.title}</FormErrorMessage> + </FormControl> + <FormControl + isInvalid={Boolean(errors.description) && touched.description} + > + <FormLabel> + <Text fontSize="sm">Descripcion</Text> + </FormLabel> + <Textarea + name="description" + value={values.description} + onChange={(e) => { + setTouched({ ...touched, description: true }) + handleChange(e) + }} + /> + <FormErrorMessage>{errors.description}</FormErrorMessage> + </FormControl> + </VStack> + </ModalBody> + </form> + + <ModalFooter> + <HStack> + <Button variant="outline" onClick={onClose}> + Cancelar + </Button> + <Button + form={FORM_ID} + variant="solid" + colorScheme="blue" + type="submit" + isLoading={isSubmitting} + isDisabled={!isValid} + > + Guardar + </Button> + </HStack> + </ModalFooter> + </ModalContent> + </Modal> + </> + ) +} diff --git a/web/src/pages/Patterns/add-pattern/index.ts b/web/src/pages/Patterns/add-pattern/index.ts new file mode 100644 index 0000000..bd38f1b --- /dev/null +++ b/web/src/pages/Patterns/add-pattern/index.ts @@ -0,0 +1 @@ +export * from './add-pattern-button' diff --git a/web/src/pages/Patterns/add-pattern/schema.ts b/web/src/pages/Patterns/add-pattern/schema.ts new file mode 100644 index 0000000..6c38744 --- /dev/null +++ b/web/src/pages/Patterns/add-pattern/schema.ts @@ -0,0 +1,18 @@ +import * as yup from 'yup' + +import { Pattern } from 'src/models' + +export type CreatePatternSchema = Pick<Pattern, 'title' | 'description'> + +const PATTERN_TITLE_MAX_LENGHT = 255 + +export const createPatternSchema = yup.object().shape<CreatePatternSchema>({ + title: yup + .string() + .max( + PATTERN_TITLE_MAX_LENGHT, + `El titulo debe tener menos de ${PATTERN_TITLE_MAX_LENGHT} caracteres` + ) + .required('El titulo es requerido'), + description: yup.string().required('La descripcion es requerida'), +}) diff --git a/web/src/pages/Patterns/patterns.tsx b/web/src/pages/Patterns/patterns.tsx index 9897932..1e02260 100644 --- a/web/src/pages/Patterns/patterns.tsx +++ b/web/src/pages/Patterns/patterns.tsx @@ -24,6 +24,7 @@ import { Link, routes } from '@redwoodjs/router' import { Pattern } from 'src/models' +import { AddPatternButton } from './add-pattern' import { GetPatternsQuery } from './query-builder' const tableHeaders = ['Titulo', 'Descripcion', '#Implementaciones', ''] @@ -79,11 +80,22 @@ export const Patterns: React.FC = () => { </Tr> )) ) - .with({ status: 'loading' }, () => <Spinner />) + .with({ status: 'loading' }, () => ( + <Tr> + <Td colSpan={tableHeaders.length}> + <HStack justifyContent="center"> + <Spinner /> + </HStack> + </Td> + </Tr> + )) .otherwise(() => null)} </Tbody> </Table> </TableContainer> + <HStack w="full" justifyContent="flex-end"> + <AddPatternButton /> + </HStack> </VStack> ) } @@ -94,7 +106,6 @@ type ActionsRowProps = { const ActionsRow: React.FC<ActionsRowProps> = ({ pattern }) => { return ( <HStack spacing="0"> - {/* Linter yells but is fine https://github.com/redwoodjs/redwood/issues/1742 */} <Link to={routes.pattern({ id: pattern.id })}> <IconButton aria-label="view pattern" diff --git a/yarn.lock b/yarn.lock index 6998661..574d2ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1683,7 +1683,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.2": +"@babel/runtime@npm:^7.10.5, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.2": version: 7.24.0 resolution: "@babel/runtime@npm:7.24.0" dependencies: @@ -8189,6 +8189,16 @@ __metadata: languageName: node linkType: hard +"@types/hoist-non-react-statics@npm:^3.3.1": + version: 3.3.5 + resolution: "@types/hoist-non-react-statics@npm:3.3.5" + dependencies: + "@types/react": "*" + hoist-non-react-statics: ^3.3.0 + checksum: 2a3b64bf3d9817d7830afa60ee314493c475fb09570a64e7737084cd482d2177ebdddf888ce837350bac51741278b077683facc9541f052d4bbe8487b4e3e618 + languageName: node + linkType: hard + "@types/html-minifier-terser@npm:^6.0.0": version: 6.1.0 resolution: "@types/html-minifier-terser@npm:6.1.0" @@ -8670,6 +8680,13 @@ __metadata: languageName: node linkType: hard +"@types/yup@npm:0.29.7": + version: 0.29.7 + resolution: "@types/yup@npm:0.29.7" + checksum: d04cbfbbd6907f83f8f9cf974fd1e019cbd68d1a2125ca67f7bea12623ea47cc015bcc01cc3952f0fba9dec46a5091026976181dd6081cfd6b85908b0303590b + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" @@ -12568,6 +12585,13 @@ __metadata: languageName: node linkType: hard +"deepmerge@npm:^2.1.1": + version: 2.2.1 + resolution: "deepmerge@npm:2.2.1" + checksum: 4379288cabd817587cee92a095ea65d18317b45e48010a2e0d87982b5f432239a144f9c8ebd4ab090cc21f0cb47e51ebfe32921f329b3b3084a2711d5d63e450 + languageName: node + linkType: hard + "default-browser-id@npm:3.0.0": version: 3.0.0 resolution: "default-browser-id@npm:3.0.0" @@ -14669,6 +14693,13 @@ __metadata: languageName: node linkType: hard +"fn-name@npm:~3.0.0": + version: 3.0.0 + resolution: "fn-name@npm:3.0.0" + checksum: ec02fe860c8afad4c77ed4412a2915513415ae84d266b06cead06da4aad4149f2297f4ada385359ec8778247cf5c7576577d014bbd994c4326222a6ead6dffa9 + languageName: node + linkType: hard + "focus-lock@npm:^1.0.0": version: 1.0.0 resolution: "focus-lock@npm:1.0.0" @@ -14758,6 +14789,24 @@ __metadata: languageName: node linkType: hard +"formik@npm:^2.4.5": + version: 2.4.5 + resolution: "formik@npm:2.4.5" + dependencies: + "@types/hoist-non-react-statics": ^3.3.1 + deepmerge: ^2.1.1 + hoist-non-react-statics: ^3.3.0 + lodash: ^4.17.21 + lodash-es: ^4.17.21 + react-fast-compare: ^2.0.1 + tiny-warning: ^1.0.2 + tslib: ^2.0.0 + peerDependencies: + react: ">=16.8.0" + checksum: 61f0d9eb092edd122f0d2988ca3d0a01073bde38af977e96ba9818382dc1fefd4cdb016cd61f08443055a748bbbbe2a95347d4528b81cc5c1c6f75865fc84927 + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -15615,7 +15664,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": +"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -17958,6 +18007,13 @@ __metadata: languageName: node linkType: hard +"lodash-es@npm:^4.17.11, lodash-es@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash-es@npm:4.17.21" + checksum: fb407355f7e6cd523a9383e76e6b455321f0f153a6c9625e21a8827d10c54c2a2341bd2ae8d034358b60e07325e1330c14c224ff582d04612a46a4f0479ff2f2 + languageName: node + linkType: hard + "lodash.chunk@npm:^4.2.0": version: 4.2.0 resolution: "lodash.chunk@npm:4.2.0" @@ -20655,6 +20711,13 @@ __metadata: languageName: node linkType: hard +"property-expr@npm:^2.0.2": + version: 2.0.6 + resolution: "property-expr@npm:2.0.6" + checksum: 69b7da15038a1146d6447c69c445306f66a33c425271235bb20507f1846dbf9577a8f9dfafe8acbfcb66f924b270157f155248308f026a68758f35fc72265b3c + languageName: node + linkType: hard + "proxy-addr@npm:^2.0.7, proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -21009,6 +21072,13 @@ __metadata: languageName: node linkType: hard +"react-fast-compare@npm:^2.0.1": + version: 2.0.4 + resolution: "react-fast-compare@npm:2.0.4" + checksum: f0300c677e95198b5f993cbb8a983dab09586157dc678f9e2b5b29ff941b6677a8776fbbdc425ce102fad86937e36bb45cfcfd797f006270b97ccf287ebfb885 + languageName: node + linkType: hard + "react-focus-lock@npm:^2.9.4": version: 2.9.6 resolution: "react-focus-lock@npm:2.9.6" @@ -23164,7 +23234,7 @@ __metadata: languageName: node linkType: hard -"synchronous-promise@npm:^2.0.15": +"synchronous-promise@npm:^2.0.13, synchronous-promise@npm:^2.0.15": version: 2.0.17 resolution: "synchronous-promise@npm:2.0.17" checksum: 1babe643d8417789ef6e5a2f3d4b8abcda2de236acd09bbe2c98f6be82c0a2c92ed21a6e4f934845fa8de18b1435a9cba1e8c3d945032e8a532f076224c024b1 @@ -23435,6 +23505,13 @@ __metadata: languageName: node linkType: hard +"tiny-warning@npm:^1.0.2": + version: 1.0.3 + resolution: "tiny-warning@npm:1.0.3" + checksum: ef8531f581b30342f29670cb41ca248001c6fd7975ce22122bd59b8d62b4fc84ad4207ee7faa95cde982fa3357cd8f4be650142abc22805538c3b1392d7084fa + languageName: node + linkType: hard + "title-case@npm:3.0.3, title-case@npm:^3.0.3": version: 3.0.3 resolution: "title-case@npm:3.0.3" @@ -23549,6 +23626,13 @@ __metadata: languageName: node linkType: hard +"toposort@npm:^2.0.2": + version: 2.0.2 + resolution: "toposort@npm:2.0.2" + checksum: ab9ca91fce4b972ccae9e2f539d755bf799a0c7eb60da07fd985fce0f14c159ed1e92305ff55697693b5bc13e300f5417db90e2593b127d421c9f6c440950222 + languageName: node + linkType: hard + "totalist@npm:^3.0.0": version: 3.0.1 resolution: "totalist@npm:3.0.1" @@ -24652,13 +24736,16 @@ __metadata: "@redwoodjs/web": 6.5.1 "@types/react": 18.2.37 "@types/react-dom": 18.2.15 + "@types/yup": 0.29.7 axios: ^1.6.7 + formik: ^2.4.5 framer-motion: ^9 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0 react-query: ^3.39.3 ts-pattern: ^5.0.8 + yup: 0.29.3 languageName: unknown linkType: soft @@ -25443,6 +25530,21 @@ __metadata: languageName: node linkType: hard +"yup@npm:0.29.3": + version: 0.29.3 + resolution: "yup@npm:0.29.3" + dependencies: + "@babel/runtime": ^7.10.5 + fn-name: ~3.0.0 + lodash: ^4.17.15 + lodash-es: ^4.17.11 + property-expr: ^2.0.2 + synchronous-promise: ^2.0.13 + toposort: ^2.0.2 + checksum: f49e621dcbdf8a8010df35d920025f27884d24702750cd124cf7839ab40fc6f7e94b00e6ff888dbdbc176b77e3acf89c6dcc11561052af9afee0b293c05fd210 + languageName: node + linkType: hard + "zen-observable-ts@npm:^1.2.5": version: 1.2.5 resolution: "zen-observable-ts@npm:1.2.5" -- GitLab