From 377e3169a73dbe72e24b19f5352f784254a9bb68 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt <fabian@ds-fs.de> Date: Fri, 4 Jun 2021 10:50:11 +0000 Subject: [PATCH] Feat/relation status input --- package-lock.json | 133 ++++++++++++------ package.json | 2 +- src/components/CreatableSelect.tsx | 38 +++++ src/components/Form/AddPoiForm.tsx | 23 ++- src/components/Form/SelectInput.tsx | 37 +++++ .../{MultiSelect.tsx => Select.tsx} | 11 +- src/components/Sidebar/SidebarListView.tsx | 5 +- src/generated/graphql.ts | 7 +- src/graphql/mutations.ts | 2 + src/hooks/usePoiData.ts | 1 + src/types/PointOfInterest.ts | 61 ++++---- 11 files changed, 234 insertions(+), 86 deletions(-) create mode 100644 src/components/CreatableSelect.tsx create mode 100644 src/components/Form/SelectInput.tsx rename src/components/{MultiSelect.tsx => Select.tsx} (77%) diff --git a/package-lock.json b/package-lock.json index 0ce504e..4cbc8f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,6 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, "requires": { "@babel/highlight": "^7.12.13" } @@ -136,7 +135,6 @@ "version": "7.13.12", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", - "dev": true, "requires": { "@babel/types": "^7.13.12" } @@ -169,8 +167,7 @@ "@babel/helper-plugin-utils": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", - "dev": true + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" }, "@babel/helper-replace-supers": { "version": "7.14.3", @@ -214,8 +211,7 @@ "@babel/helper-validator-identifier": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" }, "@babel/helper-validator-option": { "version": "7.12.17", @@ -238,7 +234,6 @@ "version": "7.13.10", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", @@ -296,7 +291,6 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.12.13" } @@ -546,12 +540,37 @@ "version": "7.14.2", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" } }, + "@emotion/babel-plugin": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.3.0.tgz", + "integrity": "sha512-UZKwBV2rADuhRp+ZOGgNWg2eYgbzKzQXfQPtJbu/PLy8onurxlNCLvxMQEvlr1/GudguPI5IU9qIY1+2z1M5bA==", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/runtime": "^7.13.10", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^2.6.1", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "^4.0.3" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + } + } + }, "@emotion/cache": { "version": "11.4.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz", @@ -564,6 +583,18 @@ "stylis": "^4.0.3" } }, + "@emotion/css": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.1.3.tgz", + "integrity": "sha512-RSQP59qtCNTf5NWD6xM08xsQdCZmVYnX/panPYvB6LQAPKQB6GL49Njf0EMbS3CyDtrlWsBcmqBtysFvfWT3rA==", + "requires": { + "@emotion/babel-plugin": "^11.0.0", + "@emotion/cache": "^11.1.3", + "@emotion/serialize": "^1.0.0", + "@emotion/sheet": "^1.0.0", + "@emotion/utils": "^1.0.0" + } + }, "@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", @@ -1756,8 +1787,7 @@ "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/prop-types": { "version": "15.7.3", @@ -2234,6 +2264,30 @@ "object.assign": "^4.1.0" } }, + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + } + } + }, "babel-plugin-syntax-trailing-function-commas": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", @@ -2463,8 +2517,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, "camel-case": { "version": "4.1.2", @@ -2803,7 +2856,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -3174,7 +3226,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -3701,6 +3752,11 @@ "to-regex-range": "^5.0.1" } }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -4233,7 +4289,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -4428,8 +4483,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-bigint": { "version": "1.0.2", @@ -4730,8 +4784,7 @@ "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema": { "version": "0.2.3", @@ -4911,8 +4964,7 @@ "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "listr": { "version": "0.14.3", @@ -6109,7 +6161,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "requires": { "callsites": "^3.0.0" } @@ -6155,7 +6206,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -6247,8 +6297,7 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "performance-now": { "version": "2.1.0", @@ -6653,12 +6702,13 @@ } }, "react-select": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-4.3.1.tgz", - "integrity": "sha512-HBBd0dYwkF5aZk1zP81Wx5UsLIIT2lSvAY2JiJo199LjoLHoivjn9//KsmvQMEFGNhe58xyuOITjfxKCcGc62Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-4.0.2.tgz", + "integrity": "sha512-BiihrRpRIBBvNqofNZIBpo08Kw8DBHb/kgpIDW4bxgkttk50Sxf0alEIKobns3U7UJXk/CA4rsFUueQEg9Pm5A==", "requires": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", + "@babel/runtime": "^7.4.4", + "@emotion/cache": "^11.0.0", + "@emotion/css": "^11.0.0", "@emotion/react": "^11.1.1", "memoize-one": "^5.0.0", "prop-types": "^15.6.0", @@ -6667,9 +6717,9 @@ } }, "react-transition-group": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", "requires": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -7051,8 +7101,7 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "resolve-pathname": { "version": "3.0.0", @@ -7142,8 +7191,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", @@ -7371,8 +7419,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-js": { "version": "0.6.2", @@ -7816,8 +7863,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-readable-stream": { "version": "1.0.0", @@ -8251,8 +8297,7 @@ "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yaml-ast-parser": { "version": "0.0.43", diff --git a/package.json b/package.json index e863f02..172c3c6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "react-dom": "^17.0.0", "react-leaflet": "^3.2.0", "react-router-dom": "^5.2.0", - "react-select": "^4.3.1", + "react-select": "4.0.2", "swr": "^0.5.6", "tailwindcss": "^2.0.3", "zustand": "^3.5.1" diff --git a/src/components/CreatableSelect.tsx b/src/components/CreatableSelect.tsx new file mode 100644 index 0000000..bfaa4f9 --- /dev/null +++ b/src/components/CreatableSelect.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import type { Theme } from 'react-select'; +import type { NamedProps, StylesConfig } from 'react-select'; +import Creatable from 'react-select/creatable'; + +const CreatableSelect = <OptionType, isMulti extends boolean>(props: NamedProps<OptionType, isMulti>): JSX.Element => { + const customStyles: StylesConfig<OptionType, isMulti> = { + control: (provided) => ({ ...provided, border: '0', borderRadius: '0.5em' }), + multiValue: (provided) => ({ ...provided, borderRadius: '999px', padding: '0 3px' }), + multiValueRemove: (provided) => ({ + ...provided, + color: 'hsl(0, 0%, 50%)', + '&:hover': { backgroundColor: 'initial', color: 'black' }, + }), + }; + + return ( + <div> + <Creatable + theme={(theme): Theme => ({ + ...theme, + // @ts-expect-error: ThemeConfig type from definitely-typed is not complete + borderRadius: '0.5em', + colors: { + ...theme.colors, + primary25: '#C7D2FE', + }, + })} + styles={customStyles} + name="pois" + className="hover:border-opacity-40 rounded-lg w-full border-2 border-black border-opacity-20 focus-within:border-indigo-300 focus-within:ring focus-within:ring-indigo-200 focus-within:ring-opacity-50 mt-1" + {...props} + /> + </div> + ); +}; + +export default CreatableSelect; diff --git a/src/components/Form/AddPoiForm.tsx b/src/components/Form/AddPoiForm.tsx index 6d40658..b0d2539 100644 --- a/src/components/Form/AddPoiForm.tsx +++ b/src/components/Form/AddPoiForm.tsx @@ -5,7 +5,7 @@ import { validateFile } from '../../util/file'; import TextInput from './TextInput'; import FileInput from './FileInput'; import TextAreaInput from './TextAreaInput'; -import { useStore, usePoiData } from '../../hooks'; +import { usePoiData, useStore } from '../../hooks'; import { useHistory } from 'react-router-dom'; import CoordinateInput from './CoordinateInput'; import TagInput from './TagInput'; @@ -13,6 +13,9 @@ import { removeDuplicateObjects } from '../../util/array'; import { createPoi, createTags } from '../../graphql/mutations'; import Spinner from '../Spinner'; import type { CreatePoiMutationMutationVariables, Mutation } from '../../generated/graphql'; +import SelectInput from './SelectInput'; + +type RelationStatusOption = { label: string; value: string }; const AddPoiForm: React.FC = () => { const [formData, setFormData] = useState<PointOfInterestFormData>({ @@ -24,6 +27,7 @@ const AddPoiForm: React.FC = () => { description: '', website: '', category: '', + relationStatus: '', image: null, tags: [], }); @@ -34,6 +38,7 @@ const AddPoiForm: React.FC = () => { const history = useHistory(); const { data } = usePoiData(); const [tagOptions, setTagOptions] = useState<Tag[]>([]); + const [relationStatusOptions, setRelationStatusOptions] = useState<RelationStatusOption[]>([]); const [selectedTags, setSelectedTags] = useState<Tag[]>([]); const setNotification = useStore((state) => state.setNotification); @@ -113,6 +118,11 @@ const AddPoiForm: React.FC = () => { }); const tags = removeDuplicateObjects(tagsWithDuplicates, 'id'); setTagOptions(tags); + + const relationStatuses = removeDuplicateObjects(data, 'relationStatus') + .filter((poi) => !!poi.relationStatus) + .map((poi) => ({ label: poi.relationStatus, value: poi.relationStatus })); + setRelationStatusOptions(relationStatuses); } }, [data]); @@ -150,6 +160,17 @@ const AddPoiForm: React.FC = () => { onChange={handleInputChange} required /> + <SelectInput + label={'Verhältnis zum Fab City Hamburg e.V.'} + name={'relationStatus'} + options={relationStatusOptions} + onChange={(selectedOption) => + setFormData((prev) => ({ + ...prev, + relationStatus: selectedOption ? (selectedOption as RelationStatusOption).value : '', + })) + } + /> <TextInput label={'Anschrift'} name={'address'} diff --git a/src/components/Form/SelectInput.tsx b/src/components/Form/SelectInput.tsx new file mode 100644 index 0000000..4895e29 --- /dev/null +++ b/src/components/Form/SelectInput.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import type { NamedProps } from 'react-select'; +import CreatableSelect from '../CreatableSelect'; + +interface Props<OptionType, isMulti extends boolean> extends NamedProps<OptionType, isMulti> { + label: string; + name: string; + required?: boolean; +} + +const SelectInput = <OptionType, isMulti extends boolean>({ + name, + label, + value, + onChange, + ...inputProps +}: Props<OptionType, isMulti>): JSX.Element => { + return ( + <label className="block mb-4"> + {!!label && ( + <span className="form-label"> + {label} + {inputProps?.required && `*`} + </span> + )} + <CreatableSelect + name={name} + value={value} + className="form-input form-input-custom" + onChange={onChange} + {...inputProps} + /> + </label> + ); +}; + +export default SelectInput; diff --git a/src/components/MultiSelect.tsx b/src/components/Select.tsx similarity index 77% rename from src/components/MultiSelect.tsx rename to src/components/Select.tsx index cf9e1cd..77ccdf9 100644 --- a/src/components/MultiSelect.tsx +++ b/src/components/Select.tsx @@ -1,9 +1,9 @@ import React from 'react'; import type { Theme } from 'react-select'; -import Select, { NamedProps, StylesConfig } from 'react-select'; +import ReactSelect, { NamedProps, StylesConfig } from 'react-select'; -const MultiSelect = <OptionType,>(props: NamedProps<OptionType, true>): JSX.Element => { - const customStyles: StylesConfig<OptionType, true> = { +const Select = <OptionType, isMulti extends boolean>(props: NamedProps<OptionType, isMulti>): JSX.Element => { + const customStyles: StylesConfig<OptionType, isMulti> = { control: (provided) => ({ ...provided, border: '0', borderRadius: '0.5em' }), multiValue: (provided) => ({ ...provided, borderRadius: '999px', padding: '0 3px' }), multiValueRemove: (provided) => ({ @@ -14,7 +14,7 @@ const MultiSelect = <OptionType,>(props: NamedProps<OptionType, true>): JSX.Elem }; return ( - <Select + <ReactSelect theme={(theme): Theme => ({ ...theme, // @ts-expect-error: ThemeConfig type from definitely-typed is not complete @@ -25,7 +25,6 @@ const MultiSelect = <OptionType,>(props: NamedProps<OptionType, true>): JSX.Elem }, })} styles={customStyles} - isMulti={true} name="pois" className="hover:border-opacity-40 rounded-lg w-full border-2 border-black border-opacity-20 focus-within:border-indigo-300 focus-within:ring focus-within:ring-indigo-200 focus-within:ring-opacity-50 mt-1" {...props} @@ -33,4 +32,4 @@ const MultiSelect = <OptionType,>(props: NamedProps<OptionType, true>): JSX.Elem ); }; -export default MultiSelect; +export default Select; diff --git a/src/components/Sidebar/SidebarListView.tsx b/src/components/Sidebar/SidebarListView.tsx index daad2ca..7b102af 100644 --- a/src/components/Sidebar/SidebarListView.tsx +++ b/src/components/Sidebar/SidebarListView.tsx @@ -5,7 +5,7 @@ import SidebarContainer from './SidebarContainer'; import { removeDuplicateObjects } from '../../util/array'; import { useFilteredPoiData } from '../../hooks/useFilteredPoiData'; import type { Tag } from '../../types/PointOfInterest'; -import MultiSelect from '../MultiSelect'; +import Select from '../Select'; const SidebarListView: React.FC = () => { const tagsToSelectOptions = (tags?: Tag[]) => tags?.map((tag) => ({ label: tag.displayName, value: tag })); @@ -27,8 +27,9 @@ const SidebarListView: React.FC = () => { return ( <SidebarContainer> <div className="p-4"> - <MultiSelect + <Select options={options} + isMulti={true} value={filterTags && tagsToSelectOptions(filterTags)} onChange={(selectedOptions) => setFilterTags(selectedOptions.map((opt) => opt.value))} /> diff --git a/src/generated/graphql.ts b/src/generated/graphql.ts index 89c2a82..d47435c 100644 --- a/src/generated/graphql.ts +++ b/src/generated/graphql.ts @@ -50,6 +50,7 @@ export type Poi = { address: Scalars['String']; image: Scalars['String']; category: Scalars['String']; + relationStatus: Scalars['String']; tags?: Maybe<Array<Maybe<Tag>>>; created_at?: Maybe<Scalars['DateTime']>; updated_at?: Maybe<Scalars['DateTime']>; @@ -64,6 +65,7 @@ export type PoiInput = { description?: Maybe<Scalars['String']>; address: Scalars['String']; category: Scalars['String']; + relationStatus: Scalars['String']; image: Scalars['Upload']; tagIds?: Maybe<Array<Maybe<Scalars['ID']>>>; }; @@ -154,6 +156,7 @@ export type CreatePoiMutationMutationVariables = Exact<{ description?: Maybe<Scalars['String']>; address: Scalars['String']; category: Scalars['String']; + relationStatus: Scalars['String']; image: Scalars['Upload']; tagIds?: Maybe<Array<Maybe<Scalars['ID']>> | Maybe<Scalars['ID']>>; }>; @@ -179,9 +182,9 @@ export type CreateTagsMutationMutation = ( export const CreatePoiMutationDocument = gql` - mutation createPoiMutation($name: String!, $email: String!, $lat: Float!, $lng: Float!, $website: String, $description: String, $address: String!, $category: String!, $image: Upload!, $tagIds: [ID]) { + mutation createPoiMutation($name: String!, $email: String!, $lat: Float!, $lng: Float!, $website: String, $description: String, $address: String!, $category: String!, $relationStatus: String!, $image: Upload!, $tagIds: [ID]) { createPoi( - poi: {name: $name, email: $email, lat: $lat, lng: $lng, website: $website, description: $description, address: $address, category: $category, image: $image, tagIds: $tagIds} + poi: {name: $name, email: $email, lat: $lat, lng: $lng, website: $website, description: $description, address: $address, category: $category, relationStatus: $relationStatus, image: $image, tagIds: $tagIds} ) } `; diff --git a/src/graphql/mutations.ts b/src/graphql/mutations.ts index a0b04ee..f5c9cf2 100644 --- a/src/graphql/mutations.ts +++ b/src/graphql/mutations.ts @@ -10,6 +10,7 @@ export const createPoi = gql` $description: String $address: String! $category: String! + $relationStatus: String! $image: Upload! $tagIds: [ID] ) { @@ -23,6 +24,7 @@ export const createPoi = gql` description: $description address: $address category: $category + relationStatus: $relationStatus image: $image tagIds: $tagIds } diff --git a/src/hooks/usePoiData.ts b/src/hooks/usePoiData.ts index 102ee5f..b5233b6 100644 --- a/src/hooks/usePoiData.ts +++ b/src/hooks/usePoiData.ts @@ -18,6 +18,7 @@ export const usePoiData = (): poiData => { lng image category + relationStatus tags { id displayName diff --git a/src/types/PointOfInterest.ts b/src/types/PointOfInterest.ts index b3be52d..2477323 100644 --- a/src/types/PointOfInterest.ts +++ b/src/types/PointOfInterest.ts @@ -1,30 +1,31 @@ -export interface PointOfInterestBase { - lat: number; - lng: number; - name: string; - description: string; - address: string; - website: string; - category: string; -} -export interface PointOfInterest extends PointOfInterestBase { - id: number; - image?: string; - tags: Tag[]; -} - -export interface PointOfInterestFormData extends PointOfInterestBase { - email: string; - image: File | null; - tags: number[]; -} - -export interface Tag { - id: string; - displayName: string; - color: string; -} - -export interface PointsOfInterestDTO { - pois: PointOfInterest[]; -} +export interface PointOfInterestBase { + lat: number; + lng: number; + name: string; + description: string; + address: string; + website: string; + category: string; + relationStatus: string; +} +export interface PointOfInterest extends PointOfInterestBase { + id: number; + image?: string; + tags: Tag[]; +} + +export interface PointOfInterestFormData extends PointOfInterestBase { + email: string; + image: File | null; + tags: number[]; +} + +export interface Tag { + id: string; + displayName: string; + color: string; +} + +export interface PointsOfInterestDTO { + pois: PointOfInterest[]; +} -- GitLab