edit profile feature wip

This commit is contained in:
Nuwan 2024-01-31 08:40:19 +05:30
parent b563f22a32
commit 51a64e60db
6 changed files with 841 additions and 408 deletions

237
jam-ui/package-lock.json generated
View File

@ -3301,6 +3301,11 @@
"prop-types": "^15.6.1"
}
},
"@filestack/loader": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@filestack/loader/-/loader-1.0.9.tgz",
"integrity": "sha512-zvXbZSgeybT1p3ds5NZ5GQYPVnKacgb2YGWe7psdPs/JE1v3SL1j2SXYaHA/f/Qwc8Y1fjzz53maKP0vwDHrvA=="
},
"@fortawesome/fontawesome-common-types": {
"version": "0.2.35",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz",
@ -3853,6 +3858,61 @@
}
}
},
"@sentry/hub": {
"version": "6.19.7",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.19.7.tgz",
"integrity": "sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==",
"requires": {
"@sentry/types": "6.19.7",
"@sentry/utils": "6.19.7",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/minimal": {
"version": "6.19.7",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.19.7.tgz",
"integrity": "sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==",
"requires": {
"@sentry/hub": "6.19.7",
"@sentry/types": "6.19.7",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/types": {
"version": "6.19.7",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz",
"integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg=="
},
"@sentry/utils": {
"version": "6.19.7",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz",
"integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==",
"requires": {
"@sentry/types": "6.19.7",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
@ -3968,6 +4028,11 @@
"loader-utils": "^1.2.3"
}
},
"@tokenizer/token": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
},
"@types/babel__core": {
"version": "7.1.14",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz",
@ -4688,6 +4753,11 @@
}
}
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@ -7887,6 +7957,11 @@
"integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==",
"dev": true
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
},
"diff-sequences": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
@ -9632,6 +9707,14 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
"fast-xml-parser": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz",
"integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==",
"requires": {
"strnum": "^1.0.5"
}
},
"fastest-levenshtein": {
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
@ -9721,6 +9804,16 @@
"tslib": "^2.0.1"
}
},
"file-type": {
"version": "16.5.4",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
"integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
"requires": {
"readable-web-to-node-stream": "^3.0.0",
"strtok3": "^6.2.4",
"token-types": "^4.1.1"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@ -9732,6 +9825,48 @@
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz",
"integrity": "sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg=="
},
"filestack-js": {
"version": "3.30.0",
"resolved": "https://registry.npmjs.org/filestack-js/-/filestack-js-3.30.0.tgz",
"integrity": "sha512-t8eBumzitZhdWEw+WqUY5QFf5uAEEObj3qsZRvb6g5AdluNtiWhF5ujqrES1l4xwzDp1v+dzItaAwzAwYcyu5w==",
"requires": {
"@babel/runtime": "^7.8.4",
"@filestack/loader": "^1.0.9",
"@sentry/minimal": "^6.19.7",
"abab": "^2.0.6",
"debug": "^4.3.4",
"eventemitter3": "^5.0.0",
"fast-xml-parser": "^4.2.4",
"file-type": "^16.5.4",
"follow-redirects": "^1.15.2",
"isutf8": "^4.0.0",
"jsonschema": "^1.4.1",
"lodash.clonedeep": "^4.5.0",
"p-queue": "^6.6.2",
"spark-md5": "^3.0.2",
"ts-node": "^8.10.2"
},
"dependencies": {
"abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"requires": {
"ms": "2.1.2"
}
},
"eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
}
}
},
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@ -12178,6 +12313,11 @@
"html-escaper": "^2.0.0"
}
},
"isutf8": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/isutf8/-/isutf8-4.0.0.tgz",
"integrity": "sha512-mJtsQGFfAphKdVuRitEpc0eon4v5fuaB6v9ZJIrLnIyybh02sIIwJ2RQbLMp6UICVCfquezllupZIVcqzGzCPg=="
},
"jest": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz",
@ -12838,6 +12978,11 @@
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
},
"jsonschema": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
"integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ=="
},
"jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@ -13396,6 +13541,11 @@
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -13531,6 +13681,11 @@
}
}
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"make-iterator": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
@ -14989,6 +15144,22 @@
"aggregate-error": "^3.0.0"
}
},
"p-queue": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
"integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
"requires": {
"eventemitter3": "^4.0.4",
"p-timeout": "^3.2.0"
},
"dependencies": {
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
}
}
},
"p-reduce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz",
@ -15002,6 +15173,14 @@
"retry": "^0.12.0"
}
},
"p-timeout": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
"requires": {
"p-finally": "^1.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@ -15226,6 +15405,11 @@
"sha.js": "^2.4.8"
}
},
"peek-readable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
"integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@ -17595,6 +17779,14 @@
"util-deprecate": "^1.0.1"
}
},
"readable-web-to-node-stream": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
"integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
"requires": {
"readable-stream": "^3.6.0"
}
},
"readdirp": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
@ -18984,6 +19176,11 @@
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"spark-md5": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz",
"integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw=="
},
"sparkles": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz",
@ -19491,6 +19688,20 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
},
"strnum": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
},
"strtok3": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
"integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
"requires": {
"@tokenizer/token": "^0.3.0",
"peek-readable": "^4.1.0"
}
},
"style-loader": {
"version": "0.23.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
@ -19988,6 +20199,15 @@
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"dev": true
},
"token-types": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
"integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
"requires": {
"@tokenizer/token": "^0.3.0",
"ieee754": "^1.2.1"
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@ -20020,6 +20240,18 @@
"glob": "^7.1.2"
}
},
"ts-node": {
"version": "8.10.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz",
"integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==",
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
}
},
"ts-pnp": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz",
@ -21781,6 +22013,11 @@
"decamelize": "^1.2.0"
}
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
},
"zoom-level": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/zoom-level/-/zoom-level-2.5.0.tgz",

View File

@ -30,6 +30,7 @@
"element-resize-event": "^3.0.3",
"emoji-mart": "^3.0.0",
"faker": "^5.5.3",
"filestack-js": "^3.30.0",
"fuse.js": "^6.4.3",
"google-maps-react": "^2.0.6",
"i18next": "^21.3.3",

View File

@ -4,6 +4,8 @@ import Select from 'react-select';
import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next';
import JKCurrentUserAvatar from '../navbar/JKCurrentUserAvatar';
import JKModalDialog from '../common/JKModalDialog';
import JKAvatarUpload from '../profile/JKAvatarUpload';
import { useAuth } from '../../context/UserAuth';
import { useForm, Controller } from 'react-hook-form';
import {
@ -27,6 +29,7 @@ function JKEditProfile() {
const [countries, setCountries] = useState([]);
const [regions, setRegions] = useState([]);
const [cities, setCities] = useState([]);
const [showAvatarModal, setShowAvatarModal] = useState(false);
const [_, forceUpdate] = useReducer(x => x + 1, 0);
@ -308,6 +311,10 @@ function JKEditProfile() {
handleChange();
};
const toggleShowAvatarModel = () => {
setShowAvatarModal(!showAvatarModal);
};
const handleChange = () => {
const params = getValues();
const data = {
@ -336,318 +343,320 @@ function JKEditProfile() {
};
return (
<Card>
<FalconCardHeader title={t('page_title', { ns: 'profile' })} titleClass="font-weight-bold" />
<CardBody className="pt-3" style={{ backgroundColor: '#edf2f9' }}>
<Form>
<Row>
<Col>
<Card>
<CardHeader>
<h5>Basics</h5>
</CardHeader>
<CardBody className="bg-light">
<Row>
<Col md={4}>
<FormGroup>
<Label for="firstName">First Name</Label>
<Controller
name="firstName"
control={control}
render={({ field: { onChange, value } }) => (
<Input
value={value}
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="lastName">Last Name</Label>
<Controller
name="lastName"
control={control}
render={({ field: { onChange, value } }) => (
<Input
value={value}
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
</Col>
<Col md={4}>
<div className="d-flex align-items-center">
<div>
<JKCurrentUserAvatar size="sm" />
</div>
<div>
<a href="#">Change Photo</a>
</div>
</div>
</Col>
</Row>
<Row>
<Col md={4}>
<FormGroup>
<Label for="country">Country</Label>
{countries.length > 0 && (
<>
<Card>
<FalconCardHeader title={t('page_title', { ns: 'profile' })} titleClass="font-weight-bold" />
<CardBody className="pt-3" style={{ backgroundColor: '#edf2f9' }}>
<Form>
<Row>
<Col>
<Card>
<CardHeader>
<h5>Basics</h5>
</CardHeader>
<CardBody className="bg-light">
<Row>
<Col md={4}>
<FormGroup>
<Label for="firstName">First Name</Label>
<Controller
name="country"
name="firstName"
control={control}
render={({ field: { onChange, value } }) => {
const country = countries.find(country => country.countrycode === value);
return (
<Select
value={{ value: country.countrycode, label: country.countryname }}
onChange={handleCountryChange}
options={countries.map(country => {
return { value: country.countrycode, label: country.countryname };
})}
/>
);
}}
/>
)}
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="state">State / Province</Label>
{regions.length > 0 && (
<Controller
name="state"
control={control}
render={({ field: { onChange, value } }) => {
const region = regions.find(region => region.region === value);
return (
<Select
value={{ value: region.region, label: region.region }}
ref={regionRef}
onChange={handleRegionChange}
options={regions.map(region => {
return { value: region.region, label: region.region };
})}
/>
);
}}
/>
)}
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="city">City</Label>
<Controller
name="city"
control={control}
render={({ field: { onChange, value } }) => (
<Select
value={{ value: value, label: value }}
ref={cityRef}
onChange={e => {
onChange(e);
handleChange();
}}
options={cities.map(city => {
return { value: city, label: city };
})}
/>
)}
/>
</FormGroup>
</Col>
</Row>
<FormGroup>
<Label for="biography">Musical biography</Label>
<Controller
name="biography"
control={control}
render={({ field: { onChange, value } }) => (
<Input
style={{ height: 200 }}
type="textarea"
value={value}
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
<FormGroup check>
<Controller
name="subscribeEmail"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
type="checkbox"
onChange={e => {
onChange(e);
handleChange();
}}
/>
)}
/>
<Label check for="subscribeEmail">
Accept emails from JamKazam about the JamKazam services
</Label>
</FormGroup>
</CardBody>
</Card>
<Card className="mt-3">
<CardHeader>
<h5>Interests</h5>
</CardHeader>
<CardBody className="bg-light">
<FormGroup check>
<Controller
name="virtualBand"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in joining a virtual/online band
</Label>
</FormGroup>
<FormGroup check>
<Controller
name="traditionalBand"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in joining an in-person band
</Label>
</FormGroup>
<FormGroup check>
<Controller
name="cowriting"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in co-writing songs with others
</Label>
</FormGroup>
</CardBody>
</Card>
</Col>
<Col>
<Card>
<CardHeader>
<h5>Instruments</h5>
</CardHeader>
<CardBody className="bg-light" style={{ overflowY: 'scroll', height: 300 }}>
<FormGroup check>
{instrumentsInitialLoadingDone &&
musicInstruments.map((musicInstrument, index) => {
return (
<Row key={musicInstrument.id} className="mb-1">
<Col md={4}>
render={({ field: { onChange, value } }) => (
<Input
value={value}
onChange={e => {
handleInstrumentSelect(e, musicInstrument);
onChange(e);
handleTextInputChage();
}}
type="checkbox"
checked={musicInstrument.checked}
/>
<Label check for="check">
{musicInstrument.description}
</Label>
</Col>
<Col md={3}>
)}
/>
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="lastName">Last Name</Label>
<Controller
name="lastName"
control={control}
render={({ field: { onChange, value } }) => (
<Input
value={value}
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
</Col>
<Col md={4}>
<div className="d-flex align-items-center">
<div>
<JKCurrentUserAvatar size="sm" />
</div>
<div>
<a href="#" onClick={toggleShowAvatarModel}>{t('change_photo', { ns: 'profile'})}</a>
</div>
</div>
</Col>
</Row>
<Row>
<Col md={4}>
<FormGroup>
<Label for="country">Country</Label>
{countries.length > 0 && (
<Controller
name="country"
control={control}
render={({ field: { onChange, value } }) => {
const country = countries.find(country => country.countrycode === value);
return (
<Select
value={{ value: country.countrycode, label: country.countryname }}
onChange={handleCountryChange}
options={countries.map(country => {
return { value: country.countrycode, label: country.countryname };
})}
/>
);
}}
/>
)}
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="state">State / Province</Label>
{regions.length > 0 && (
<Controller
name="state"
control={control}
render={({ field: { onChange, value } }) => {
const region = regions.find(region => region.region === value);
return (
<Select
value={{ value: region.region, label: region.region }}
ref={regionRef}
onChange={handleRegionChange}
options={regions.map(region => {
return { value: region.region, label: region.region };
})}
/>
);
}}
/>
)}
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="city">City</Label>
<Controller
name="city"
control={control}
render={({ field: { onChange, value } }) => (
<Select
value={
musicInstrument.checked
? PROFICIENCIES.find(p => parseInt(p.value) === musicInstrument.proficiency_level)
: null
}
value={{ value: value, label: value }}
ref={cityRef}
onChange={e => {
handleInstrumentProficiencyChange(e, musicInstrument);
onChange(e);
handleChange();
}}
options={PROFICIENCIES}
isDisabled={!musicInstrument.checked}
options={cities.map(city => {
return { value: city, label: city };
})}
/>
</Col>
</Row>
);
})}
</FormGroup>
</CardBody>
</Card>
<Card className="mt-3">
<CardHeader>
<h5>Genres</h5>
</CardHeader>
<CardBody className="bg-light" style={{ overflowY: 'scroll', height: 300 }}>
<FormGroup check>
{genreInitialLoadingDone &&
genres.map((genre, index) => {
return (
<Row key={genre.genre_id}>
<Col md={4}>
<Input
onChange={e => {
handleGenreChange(e, genre);
}}
type="checkbox"
checked={genre.checked}
/>
<Label check for="check">
{genre.description}
</Label>
</Col>
</Row>
);
})}
</FormGroup>
</CardBody>
</Card>
</Col>
</Row>
</Form>
</CardBody>
</Card>
)}
/>
</FormGroup>
</Col>
</Row>
<FormGroup>
<Label for="biography">Musical biography</Label>
<Controller
name="biography"
control={control}
render={({ field: { onChange, value } }) => (
<Input
style={{ height: 200 }}
type="textarea"
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
<FormGroup check>
<Controller
name="subscribeEmail"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
type="checkbox"
onChange={e => {
onChange(e);
handleChange();
}}
/>
)}
/>
<Label check for="subscribeEmail">
Accept emails from JamKazam about the JamKazam services
</Label>
</FormGroup>
</CardBody>
</Card>
<Card className="mt-3">
<CardHeader>
<h5>Interests</h5>
</CardHeader>
<CardBody className="bg-light">
<FormGroup check>
<Controller
name="virtualBand"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in joining a virtual/online band
</Label>
</FormGroup>
<FormGroup check>
<Controller
name="traditionalBand"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in joining an in-person band
</Label>
</FormGroup>
<FormGroup check>
<Controller
name="cowriting"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in co-writing songs with others
</Label>
</FormGroup>
</CardBody>
</Card>
</Col>
<Col>
<Card>
<CardHeader>
<h5>Instruments</h5>
</CardHeader>
<CardBody className="bg-light" style={{ overflowY: 'scroll', height: 300 }}>
<FormGroup check>
{instrumentsInitialLoadingDone &&
musicInstruments.map((musicInstrument, index) => {
return (
<Row key={musicInstrument.id} className="mb-1">
<Col md={4}>
<Input
onChange={e => {
handleInstrumentSelect(e, musicInstrument);
}}
type="checkbox"
checked={musicInstrument.checked}
/>
<Label check for="check">
{musicInstrument.description}
</Label>
</Col>
<Col md={3}>
<Select
value={
musicInstrument.checked
? PROFICIENCIES.find(p => parseInt(p.value) === musicInstrument.proficiency_level)
: null
}
onChange={e => {
handleInstrumentProficiencyChange(e, musicInstrument);
}}
options={PROFICIENCIES}
isDisabled={!musicInstrument.checked}
/>
</Col>
</Row>
);
})}
</FormGroup>
</CardBody>
</Card>
<Card className="mt-3">
<CardHeader>
<h5>Genres</h5>
</CardHeader>
<CardBody className="bg-light" style={{ overflowY: 'scroll', height: 300 }}>
<FormGroup check>
{genreInitialLoadingDone &&
genres.map((genre, index) => {
return (
<Row key={genre.genre_id}>
<Col md={4}>
<Input
onChange={e => {
handleGenreChange(e, genre);
}}
type="checkbox"
checked={genre.checked}
/>
<Label check for="check">
{genre.description}
</Label>
</Col>
</Row>
);
})}
</FormGroup>
</CardBody>
</Card>
</Col>
</Row>
</Form>
</CardBody>
</Card>
<JKAvatarUpload show={showAvatarModal} onToggle={toggleShowAvatarModel} />
</>
);
}

View File

@ -0,0 +1,140 @@
import React, { useEffect, useRef, useState } from 'react';
import JKModalDialog from '../common/JKModalDialog';
import { Button, Card, CardBody } from 'reactstrap';
import { useTranslation } from 'react-i18next';
import * as filestack from 'filestack-js';
import { getFilepickerPolicy } from '../../helpers/rest';
import { useAuth } from '../../context/UserAuth';
import { getUserDetails, updateAvatar } from '../../helpers/rest';
const JKAvatarUpload = ({ show, onToggle }) => {
const { t } = useTranslation();
const client = useRef(null);
const { currentUser } = useAuth();
const [userDetails, setUserDetails] = useState(null);
useEffect(() => {
if (currentUser) {
//get user details
getUserDetails(currentUser.id)
.then(response => {
if (response.status === 200) {
return response.json();
} else {
console.log('getUserDetails error', response);
}
})
.then(user => {
console.log('userDetails', user);
setUserDetails(user);
})
.catch(error => {
console.log('getUserDetails error', error);
});
}
}, [currentUser]);
useEffect(() => {
if (currentUser && window.gon && window.gon.fp_apikey) {
getFilepickerPolicy(currentUser.id)
.then(response => {
if (response.status === 200) {
return response.json();
} else {
console.log('getFilepickerPolicy error', response);
}
})
.then(filepicker_policy => {
console.log('filepicker_policy', filepicker_policy);
console.log('currentUser', currentUser);
const options = {
security: {
policy: filepicker_policy.policy,
signature: filepicker_policy.signature
}
};
client.current = filestack.init(window.gon.fp_apikey, options);
//render avatar
})
.catch(error => {
console.log('getFilepickerPolicy error', error);
});
}
}, [currentUser, window.gon]);
useEffect(() => {
if (userDetails) {
//render avatar
}
}, [userDetails]);
const openFilePicker = () => {
client.current
.picker({
accept: ['image/*'],
maxFiles: 1,
transformations: {
crop: {
aspectRatio: 1,
force: true
},
circle: true,
rotate: true
},
onFileSelected: file => {
console.log('file selected', file);
},
onUploadDone: res => {
console.log('upload done', res);
// updateAvatar({
// original_fpfile: determineCurrentFpfile(),
// cropped_fpfile: scaled,
// cropped_large_fpfile: scaledLarger,
// crop_selection: currentSelection
// })
// .then(response => {
// if (response.status === 200) {
// return response.json();
// } else {
// console.log('updateAvatar error', response);
// }
// })
// .then(user => {
// console.log('updateAvatar', user);
// setUserDetails(user);
// })
// .catch(error => {
// console.log('updateAvatar error', error);
// });
}
})
.open();
};
return (
<JKModalDialog show={show} onToggle={onToggle} title={t('avatar_modal.title', { ns: 'profile' })} showFooter={true}>
{userDetails && userDetails.original_fpfile ? (
<div>{userDetails.original_fpfile}</div>
) : (
<Card style={{ width: '50%', height: '240px', margin: 'auto' }}>
<CardBody>
<div className="text-center">{t('avatar_modal.no_avatar', { ns: 'profile' })}</div>
</CardBody>
</Card>
)}
<div className="d-flex mt-3" style={{ alignItems: 'center', justifyContent: 'center', gap: '5px' }}>
<Button color="primary" outline onClick={openFilePicker}>
{t('avatar_modal.upload', { ns: 'profile' })}
</Button>
<Button color="secondary" outline onClick={onToggle}>
{t('avatar_modal.delete', { ns: 'profile' })}
</Button>
</div>
</JKModalDialog>
);
};
export default JKAvatarUpload;

View File

@ -1,12 +1,12 @@
import apiFetch from "./apiFetch";
import apiFetch from './apiFetch';
export const getMusicians = (page) => {
export const getMusicians = page => {
return new Promise((resolve, reject) => {
apiFetch(`/search/musicians?results=true`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
// export const getPeople = (page) => {
// return new Promise((resolve, reject) => {
@ -16,13 +16,13 @@ export const getMusicians = (page) => {
// })
// }
export const getPersonById = (id) => {
return new Promise((resolve, reject) => (
export const getPersonById = id => {
return new Promise((resolve, reject) =>
apiFetch(`/users/${id}/profile?show_teacher=true`)
.then(response => resolve(response))
.catch(error => reject(error))
))
}
.then(response => resolve(response))
.catch(error => reject(error))
);
};
export const getPeople = ({ data, offset, limit } = {}) => {
return new Promise((resolve, reject) => {
@ -30,34 +30,34 @@ export const getPeople = ({ data, offset, limit } = {}) => {
method: 'POST',
body: JSON.stringify(data)
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getPeopleIndex = () => {
return new Promise((resolve, reject) => {
apiFetch(`/users`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getGenres = () => {
return new Promise((resolve, reject) => {
apiFetch('/genres')
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getInstruments = () => {
return new Promise((resolve, reject) => {
apiFetch('/instruments')
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
// export const getCurrentUser = () => {
// return new Promise((resolve, reject) => {
@ -67,13 +67,13 @@ export const getInstruments = () => {
// })
// }
export const getFriends = (userId) => {
export const getFriends = userId => {
return new Promise((resolve, reject) => {
apiFetch(`/users/${userId}/friends`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const addFriend = (userId, friendId) => {
return new Promise((resolve, reject) => {
@ -81,108 +81,106 @@ export const addFriend = (userId, friendId) => {
method: 'POST',
body: JSON.stringify({ friend_id: friendId })
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const removeFriend = (userId, friendId) => {
return new Promise((resolve, reject) => {
apiFetch(`/users/${userId}/friends/${friendId}`, {
method: 'DELETE'
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getTextMessages = (options = {}) => {
return new Promise((resolve, reject) => {
apiFetch(`/text_messages?${new URLSearchParams(options)}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const createTextMessage = (options) => {
export const createTextMessage = options => {
return new Promise((resolve, reject) => {
apiFetch(`/text_messages`, {
method: "POST",
method: 'POST',
body: JSON.stringify(options)
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const createLobbyChatMessage = (options) => {
export const createLobbyChatMessage = options => {
return new Promise((resolve, reject) => {
apiFetch(`/chat`, {
method: "POST",
method: 'POST',
body: JSON.stringify(options)
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getNotifications = (userId, options = {}) => {
return new Promise((resolve, reject) => {
apiFetch(`/users/${userId}/notifications?${new URLSearchParams(options)}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const acceptFriendRequest = (userId, options = {}) => {
return new Promise((resolve, reject) => {
const { status, friend_request_id } = options
const { status, friend_request_id } = options;
apiFetch(`/users/${userId}/friend_requests/${friend_request_id}`, {
method: 'POST',
body: JSON.stringify({ status })
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const deleteNotification = (userId, notificationId) => {
return new Promise((resolve, reject) => {
apiFetch(`/users/${userId}/notifications/${notificationId}`, {
method: 'DELETE',
method: 'DELETE'
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getSessions = () => {
return new Promise((resolve, reject) => {
apiFetch(`/sessions`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getLatencyToUsers = (currentUserId, participantIds) => {
return new Promise((resolve, reject) => {
const query = participantIds.map(id => `user_ids[]=${id}`).join('&')
const query = participantIds.map(id => `user_ids[]=${id}`).join('&');
apiFetch(`/users/${currentUserId}/latencies?${query}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getLobbyChatMessages = (options = {}) => {
return new Promise((resolve, reject) => {
console.log('getLobbyChatMessages', options)
console.log('getLobbyChatMessages', options);
apiFetch(`/chat?${new URLSearchParams(options)}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const updateUser = (userId, options) => {
return new Promise((resolve, reject) => {
@ -190,32 +188,66 @@ export const updateUser = (userId, options) => {
method: 'PATCH',
body: JSON.stringify(options)
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getUserDetails = userId => {
return new Promise((resolve, reject) => {
apiFetch(`/users/${userId}`)
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getCountries = () => {
return new Promise((resolve, reject) => {
apiFetch(`/countries`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getRegions = (countryId) => {
export const getRegions = countryId => {
return new Promise((resolve, reject) => {
apiFetch(`/regions?country=${countryId}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getCities = (countryId, regionId) => {
return new Promise((resolve, reject) => {
apiFetch(`/cities?country=${countryId}&region=${regionId}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const getFilepickerPolicy = (userId, options) => {
let params = {};
if (options && options['handle']) {
params['handle'] = options['handle'];
}
if (options && options['convert']) {
params['convert'] = options['convert'];
}
const url = '/users/' + userId + '/filepicker_policy?' + new URLSearchParams(params);
return new Promise((resolve, reject) => {
apiFetch(url)
.then(response => resolve(response))
.catch(error => reject(error));
});
};
export const updateAvatar = (userId, options) => {
return new Promise((resolve, reject) => {
apiFetch(`/users/${userId}/avatar`, {
method: 'PATCH',
body: JSON.stringify(options)
})
.then(response => resolve(response))
.catch(error => reject(error));
});
}

View File

@ -1,3 +1,17 @@
{
"page_title": "Profile"
"page_title": "Profile",
"change_photo": "Change Photo",
"avatar_modal": {
"title": "Update Profile Photo",
"upload": "Upload",
"cancel": "Cancel",
"delete": "Delete",
"save": "Save",
"upload_photo": "Please upload a photo",
"no_avatar": "Please upload a photo.",
"error": {
"file_type": "Invalid file type. Please upload a JPG or PNG image.",
"upload": "There was an error uploading your avatar. Please try again."
}
}
}