From 10a32ef67364d86daf501b6319ecb5873047f1d1 Mon Sep 17 00:00:00 2001 From: Kervy Cantos Date: Mon, 14 Aug 2023 19:34:32 +0800 Subject: [PATCH] feat: photo upload via cloudinary --- packages/react-todo/db.json | 14 +- packages/react-todo/index.html | 5 + packages/react-todo/package.json | 4 + packages/react-todo/pnpm-lock.yaml | 131 ++++++++++++++++++ .../inputs/CloudinaryUploadWidget.jsx | 64 +++++++++ .../src/components/modals/todoModal.jsx | 24 +++- packages/react-todo/src/components/todo.jsx | 2 +- 7 files changed, 236 insertions(+), 8 deletions(-) create mode 100644 packages/react-todo/src/components/inputs/CloudinaryUploadWidget.jsx diff --git a/packages/react-todo/db.json b/packages/react-todo/db.json index e76c048..067e6b5 100644 --- a/packages/react-todo/db.json +++ b/packages/react-todo/db.json @@ -15,18 +15,26 @@ ], "todos": [ { - "groupId": 1, + "groupId": 2, "description": "", "name": "asdasd", "id": "affac351-87fa-4a26-a971-47cb05642c91", - "updatedAt": "2023-08-09T09:10:42.357Z" + "updatedAt": "2023-08-09T09:10:42.357Z", + "userPhoto": "https://res.cloudinary.com/dmm1qisfm/image/upload/v1692012721/TodoUsers/dimmf9bljwqttpmv7uxb.jpg" }, { "groupId": 1, - "description": "

asdasd

", + "description": "

asdasdasd

", "name": "asdasd", "id": "16fcc608-82fd-438f-969b-b18011002abc", "updatedAt": "2023-08-09T09:10:46.685Z" + }, + { + "groupId": 1, + "description": "", + "name": "asdad", + "id": "efe7d67b-3f94-4b73-b54c-cf0060a92b93", + "updatedAt": "2023-08-13T18:35:45.882Z" } ], "users": [ diff --git a/packages/react-todo/index.html b/packages/react-todo/index.html index 9f91eaf..5ecfb07 100644 --- a/packages/react-todo/index.html +++ b/packages/react-todo/index.html @@ -6,7 +6,12 @@ + Todo App - FWD diff --git a/packages/react-todo/package.json b/packages/react-todo/package.json index 0d37bf4..b97ba3d 100644 --- a/packages/react-todo/package.json +++ b/packages/react-todo/package.json @@ -15,12 +15,16 @@ }, "dependencies": { "@ant-design/icons": "^5.2.5", + "@cloudinary/react": "^1.11.2", + "@cloudinary/url-gen": "^1.11.1", "@tanstack/react-query": "^4.32.6", "antd": "^5.8.2", + "cloudinary": "^1.40.0", "draft-js": "^0.11.7", "moment": "^2.29.4", "prop-types": "^15.8.1", "react": "^18.2.0", + "react-cloudinary-upload-widget": "^2.0.0", "react-dom": "^18.2.0", "react-hook-form": "^7.45.4", "react-loader-spinner": "^5.3.4", diff --git a/packages/react-todo/pnpm-lock.yaml b/packages/react-todo/pnpm-lock.yaml index 9029cb7..fdec06c 100644 --- a/packages/react-todo/pnpm-lock.yaml +++ b/packages/react-todo/pnpm-lock.yaml @@ -8,12 +8,21 @@ dependencies: '@ant-design/icons': specifier: ^5.2.5 version: 5.2.5(react-dom@18.2.0)(react@18.2.0) + '@cloudinary/react': + specifier: ^1.11.2 + version: 1.11.2(react@18.2.0) + '@cloudinary/url-gen': + specifier: ^1.11.1 + version: 1.11.1 '@tanstack/react-query': specifier: ^4.32.6 version: 4.32.6(react-dom@18.2.0)(react@18.2.0) antd: specifier: ^5.8.2 version: 5.8.2(moment@2.29.4)(react-dom@18.2.0)(react@18.2.0) + cloudinary: + specifier: ^1.40.0 + version: 1.40.0 draft-js: specifier: ^0.11.7 version: 0.11.7(react-dom@18.2.0)(react@18.2.0) @@ -26,6 +35,9 @@ dependencies: react: specifier: ^18.2.0 version: 18.2.0 + react-cloudinary-upload-widget: + specifier: ^2.0.0 + version: 2.0.0(axios@0.21.4)(react@18.2.0) react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) @@ -379,6 +391,39 @@ packages: '@babel/helper-validator-identifier': 7.22.5 to-fast-properties: 2.0.0 + /@cloudinary/html@1.11.2: + resolution: {integrity: sha512-IibBZliI7MOK3dxvz0WlsEyTIsqZhY3SiCBQM0CNwCKxn3N1TO8OgetfU26Now1lJbdoF5JQ8S7Cn6ZWNuU1KA==} + dependencies: + '@types/lodash.clonedeep': 4.5.7 + '@types/lodash.debounce': 4.0.7 + '@types/node': 14.18.54 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + typescript: 4.9.5 + dev: false + + /@cloudinary/react@1.11.2(react@18.2.0): + resolution: {integrity: sha512-ZnVthMW7TExLMPmyven4N7EVr2oX2yZqnHn6EHBlni9iM0AZd6qtR8R5/SscxNLNwgBieWuvmejUlUT4LqPpMg==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.3.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@cloudinary/html': 1.11.2 + react: 18.2.0 + dev: false + + /@cloudinary/transformation-builder-sdk@1.5.1: + resolution: {integrity: sha512-7pTKgyrX49T3Qk0+bE/RN7bijGVRPusDXjRHJyO5a7H6CnL1sZ/TvTMAKvXweFLTOpWzWFW6nS0Bah3kBvTb7w==} + dependencies: + '@cloudinary/url-gen': 1.11.1 + dev: false + + /@cloudinary/url-gen@1.11.1: + resolution: {integrity: sha512-j5nYZOMeB4Ht1W8C6RXEvVNJVME1sObxtdgtnJDiGcBQmwI1EsCedSQSzkJoely3jhx+yI6WGCLGniF/PE+Idw==} + dependencies: + '@cloudinary/transformation-builder-sdk': 1.5.1 + dev: false + /@ctrl/tinycolor@3.6.0: resolution: {integrity: sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==} engines: {node: '>=10'} @@ -929,6 +974,26 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false + /@types/lodash.clonedeep@4.5.7: + resolution: {integrity: sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==} + dependencies: + '@types/lodash': 4.14.197 + dev: false + + /@types/lodash.debounce@4.0.7: + resolution: {integrity: sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==} + dependencies: + '@types/lodash': 4.14.197 + dev: false + + /@types/lodash@4.14.197: + resolution: {integrity: sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==} + dev: false + + /@types/node@14.18.54: + resolution: {integrity: sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw==} + dev: false + /@types/parse-json@4.0.0: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: false @@ -1189,6 +1254,14 @@ packages: engines: {node: '>= 0.4'} dev: true + /axios@0.21.4: + resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} + dependencies: + follow-redirects: 1.15.2 + transitivePeerDependencies: + - debug + dev: false + /babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} @@ -1315,6 +1388,24 @@ packages: resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} dev: false + /cloudinary-core@2.13.0(lodash@4.17.21): + resolution: {integrity: sha512-Nt0Q5I2FtenmJghtC4YZ3MZZbGg1wLm84SsxcuVwZ83OyJqG9CNIGp86CiI6iDv3QobaqBUpOT7vg+HqY5HxEA==} + peerDependencies: + lodash: '>=4.0' + dependencies: + lodash: 4.17.21 + dev: false + + /cloudinary@1.40.0: + resolution: {integrity: sha512-Fifkl8NRw/M+Enw4cKCXc6e0Or28c5y6RVGYS3OCLzT1W8EfBt416FURhLuuL/S4BCVv8bSilmnM746kCtth3g==} + engines: {node: '>=0.6'} + dependencies: + cloudinary-core: 2.13.0(lodash@4.17.21) + core-js: 3.32.0 + lodash: 4.17.21 + q: 1.5.1 + dev: false + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -1909,6 +2000,16 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true + /follow-redirects@1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: @@ -2362,6 +2463,14 @@ packages: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: false + /lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: false + + /lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: false + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -2761,6 +2870,11 @@ packages: engines: {node: '>=6'} dev: true + /q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: false + /qrcode.react@3.1.0(react@18.2.0): resolution: {integrity: sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==} peerDependencies: @@ -3298,6 +3412,17 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /react-cloudinary-upload-widget@2.0.0(axios@0.21.4)(react@18.2.0): + resolution: {integrity: sha512-q+0qJLUUUnAkbGjrwh2SYHCN466dYYSd9C1pR/qB/epAKD9iqs3qUZoYXVwFGPdfmlnUO9LDk9htN+lfK6xFvA==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + axios: ^0.21.4 + react: ^16.9.0 || ^17.0.0 || ^18.0.0 + dependencies: + axios: 0.21.4 + react: 18.2.0 + dev: false + /react-dom@18.2.0(react@18.2.0): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -3875,6 +4000,12 @@ packages: is-typed-array: 1.1.12 dev: true + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: false + /ua-parser-js@0.7.35: resolution: {integrity: sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==} dev: false diff --git a/packages/react-todo/src/components/inputs/CloudinaryUploadWidget.jsx b/packages/react-todo/src/components/inputs/CloudinaryUploadWidget.jsx new file mode 100644 index 0000000..46bde9e --- /dev/null +++ b/packages/react-todo/src/components/inputs/CloudinaryUploadWidget.jsx @@ -0,0 +1,64 @@ +import React from "react"; + +import { WidgetLoader, Widget } from "react-cloudinary-upload-widget"; + +export const CloudinaryUploadWidget = ({ onUpload }) => { + return ( + <> + + { + return onUpload(result.info); + }} + onFailure={(error) => error} + logging={false} + customPublicId={"sample"} + eager={"w_400,h_300,c_pad|w_260,h_200,c_crop"} + use_filename={false} + destroy={true} + widgetStyles={{ + palette: { + window: "#737373", + windowBorder: "#FFFFFF", + tabIcon: "#FF9600", + menuIcons: "#D7D7D8", + textDark: "#DEDEDE", + textLight: "#FFFFFF", + link: "#0078FF", + action: "#FF620C", + inactiveTabIcon: "#B3B3B3", + error: "#F44235", + inProgress: "#0078FF", + complete: "#20B832", + sourceBg: "#909090", + }, + fonts: { + default: null, + "'Fira Sans', sans-serif": { + url: "https://fonts.googleapis.com/css?family=Fira+Sans", + active: true, + }, + }, + }} + /> + + ); +}; diff --git a/packages/react-todo/src/components/modals/todoModal.jsx b/packages/react-todo/src/components/modals/todoModal.jsx index 3e827e1..ffcaf43 100644 --- a/packages/react-todo/src/components/modals/todoModal.jsx +++ b/packages/react-todo/src/components/modals/todoModal.jsx @@ -6,6 +6,8 @@ import { useQueryClient } from "@tanstack/react-query"; import RichTextInput from "../inputs/richText"; import { message } from "antd"; import { v4 as uuidv4 } from "uuid"; +import { Cloudinary } from "@cloudinary/url-gen"; +import { CloudinaryUploadWidget } from "../inputs/CloudinaryUploadWidget"; function TodoModal({ isOpen, onClose, todoData }) { if (!isOpen) return null; @@ -16,6 +18,7 @@ function TodoModal({ isOpen, onClose, todoData }) { const [editDescription, setEditDescription] = useState(false); const [editName, setEditName] = useState(todoData ? false : true); const [defaultGroup, setDefaultGroup] = useState(""); + const [imageUrl, setImageUrl] = useState(todoData?.userPhoto ?? avatar); const initialValues = { groupId: todoData?.groupId ?? "", @@ -28,6 +31,12 @@ function TodoModal({ isOpen, onClose, todoData }) { defaultValues: initialValues, }); + const cld = new Cloudinary({ + cloud: { + cloudName: "TodoUsers", + }, + }); + const fetchGroups = () => { fetch("http://localhost:3000/Groups") .then((response) => response.json()) @@ -75,6 +84,10 @@ function TodoModal({ isOpen, onClose, todoData }) { } return newData; }; + const onUpload = (result) => { + setValue("userPhoto", result.secure_url); + setImageUrl(result.secure_url); + }; const onSubmit = async () => { const formData = getValues(); @@ -222,15 +235,18 @@ function TodoModal({ isOpen, onClose, todoData }) {

- John Doe -

+ > + +
+ +

John Doe

+
diff --git a/packages/react-todo/src/components/todo.jsx b/packages/react-todo/src/components/todo.jsx index d027003..14b03b3 100644 --- a/packages/react-todo/src/components/todo.jsx +++ b/packages/react-todo/src/components/todo.jsx @@ -46,7 +46,7 @@ function Todo({ todo }) { >
{todo.name}
- +