Compare commits
No commits in common. "52fba3a87972b8a3cc4d09bdbcdf04c4d21a538c" and "61de5c81e7844e45e2180af85302b5bed783534d" have entirely different histories.
52fba3a879
...
61de5c81e7
3
.env
3
.env
|
@ -1,8 +1,5 @@
|
|||
NEXT_PUBLIC_SERVER_BASE_URL=https://api.bbuddy.expert/api
|
||||
NEXT_PUBLIC_AGORA_APPID=ed90c9dc42634e5687d4e2e0766b363f
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51LVB3LK5pVGxNPeKk4gedt5NW4cb8k7BVXvgOMPTK4x1nnbGTD8BCqDqgInboT6N72YwrTl4tOsVz8rAjbUadX1m00y4Aq5qE8
|
||||
STRIPE_SECRET_KEY=sk_test_51LVB3LK5pVGxNPeK6j0wCsPqYMoGfcuwf1LpwGEBsr1dUx4NngukyjYL2oMZer5EOlW3lqnVEPjNDruN0OkUohIf00fWFUHN5O
|
||||
STRIPE_PAYMENT_DESCRIPTION='BBuddy services'
|
||||
|
||||
NEXT_PUBLIC_CONTENTFUL_SPACE_ID = voxpxjq7y7vf
|
||||
NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN = s99GWKfpDKkNwiEJ3pN7US_tmqsGvDlaex-sOJwpzuc
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
{
|
||||
"name": "bbuddy-ui",
|
||||
"version": "0.2.5",
|
||||
"version": "0.0.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bbuddy-ui",
|
||||
"version": "0.2.5",
|
||||
"version": "0.0.4",
|
||||
"dependencies": {
|
||||
"@ant-design/cssinjs": "^1.18.1",
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@ant-design/nextjs-registry": "^1.0.0",
|
||||
"@contentful/rich-text-react-renderer": "^15.22.9",
|
||||
"@microsoft/signalr": "^8.0.7",
|
||||
"@stripe/react-stripe-js": "^2.7.3",
|
||||
"@stripe/stripe-js": "^4.1.0",
|
||||
"agora-rtc-react": "2.1.0",
|
||||
"agora-rtc-react": "^2.1.0",
|
||||
"agora-rtc-sdk-ng": "^4.20.2",
|
||||
"antd": "^5.12.1",
|
||||
"antd-img-crop": "^4.21.0",
|
||||
"antd-style": "^3.6.2",
|
||||
"axios": "^1.6.5",
|
||||
"contentful": "^10.13.3",
|
||||
"dayjs": "^1.11.10",
|
||||
|
@ -30,9 +27,7 @@
|
|||
"react-dom": "^18",
|
||||
"react-signalr": "^0.2.24",
|
||||
"react-slick": "^0.29.0",
|
||||
"react-stripe-js": "^1.1.5",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"stripe": "^16.2.0",
|
||||
"styled-components": "^6.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -182,82 +177,6 @@
|
|||
"react": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
||||
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.25.9",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz",
|
||||
"integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.3",
|
||||
"@babel/types": "^7.26.3",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
|
||||
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.25.9",
|
||||
"@babel/types": "^7.25.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
|
||||
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
|
||||
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
|
||||
"integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.3"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz",
|
||||
|
@ -269,60 +188,6 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.25.9",
|
||||
"@babel/parser": "^7.25.9",
|
||||
"@babel/types": "^7.25.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.26.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz",
|
||||
"integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.3",
|
||||
"@babel/parser": "^7.26.3",
|
||||
"@babel/template": "^7.25.9",
|
||||
"@babel/types": "^7.26.3",
|
||||
"debug": "^4.3.1",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
|
||||
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.9",
|
||||
"@babel/helper-validator-identifier": "^7.25.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@contentful/content-source-maps": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@contentful/content-source-maps/-/content-source-maps-0.11.0.tgz",
|
||||
|
@ -363,81 +228,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
|
||||
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/cache": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
|
||||
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/cache/node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/cache/node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/css": {
|
||||
"version": "11.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz",
|
||||
"integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/cache": "^11.13.5",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"@emotion/utils": "^1.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
|
@ -456,93 +246,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
|
||||
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
|
||||
},
|
||||
"node_modules/@emotion/react": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
|
||||
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/unitless": "^0.10.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize/node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/serialize/node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/serialize/node_modules/@emotion/unitless": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
|
||||
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/sheet": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
|
||||
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||
},
|
||||
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
|
||||
"integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/utils": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
|
||||
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
|
@ -722,54 +430,6 @@
|
|||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/signalr": {
|
||||
"version": "8.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-8.0.7.tgz",
|
||||
|
@ -1130,29 +790,6 @@
|
|||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@stripe/react-stripe-js": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.9.0.tgz",
|
||||
"integrity": "sha512-+/j2g6qKAKuWSurhgRMfdlIdKM+nVVJCy/wl0US2Ccodlqx0WqfIIBhUkeONkCG+V/b+bZzcj4QVa3E/rXtT4Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0 || ^4.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@stripe/stripe-js": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-4.10.0.tgz",
|
||||
"integrity": "sha512-KrMOL+sH69htCIXCaZ4JluJ35bchuCCznyPyrbN8JXSGQfwBI1SuIEMZNwvy8L8ykj29t6sa5BAAiL7fNoLZ8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.16"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||
|
@ -1177,16 +814,11 @@
|
|||
"version": "20.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz",
|
||||
"integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
|
||||
|
@ -1398,10 +1030,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/agora-rtc-react": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/agora-rtc-react/-/agora-rtc-react-2.1.0.tgz",
|
||||
"integrity": "sha512-3FGteA7FG51oK5MusbYNgAcKZaAQK+4sbEz4F0DPzcpDxqNANpocJDqOsmXoUAj5yDBsBZelmagU3abd++6RGA==",
|
||||
"license": "MIT",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/agora-rtc-react/-/agora-rtc-react-2.3.0.tgz",
|
||||
"integrity": "sha512-6D0uvXoZFlwQ/DClceJ1PUCpaHv3ebfMKFOnU0DXbiLpeMeYWM2uyuvfrcDjg4fGf033wPEzXVJHS0wx/miyJw==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
@ -1548,26 +1179,6 @@
|
|||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/antd-style": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/antd-style/-/antd-style-3.7.1.tgz",
|
||||
"integrity": "sha512-CQOfddVp4aOvBfCepa+Kj2e7ap+2XBINg1Kn2osdE3oQvrD7KJu/K0sfnLcFLkgCJygbxmuazYdWLKb+drPDYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ant-design/cssinjs": "^1.21.1",
|
||||
"@babel/runtime": "^7.24.1",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/css": "^11.11.2",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/serialize": "^1.1.3",
|
||||
"@emotion/utils": "^1.2.1",
|
||||
"use-merge-value": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"antd": ">=5.8.1",
|
||||
"react": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
|
@ -1851,21 +1462,6 @@
|
|||
"deep-equal": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"resolve": "^1.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -1971,6 +1567,7 @@
|
|||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
@ -2147,12 +1744,6 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/copy-to-clipboard": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
||||
|
@ -2161,22 +1752,6 @@
|
|||
"toggle-selection": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
|
@ -2477,15 +2052,6 @@
|
|||
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
|
||||
"integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.23.3",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
|
||||
|
@ -2675,6 +2241,7 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
@ -3228,12 +2795,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
|
@ -3689,15 +3250,6 @@
|
|||
"integrity": "sha512-PVH+X8/S9J6XItQgIRLlsrwXUmb/v13wxvcZgqohnnlUZZOEWbWZ07bLsuQ9dEMnNpT9APvBuVV50W5QmDt4pA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
|
@ -3717,6 +3269,7 @@
|
|||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
|
@ -3811,12 +3364,6 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-async-function": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
|
||||
|
@ -3888,6 +3435,7 @@
|
|||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
|
||||
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
|
@ -4242,30 +3790,12 @@
|
|||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsesc": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/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==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-pointer": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz",
|
||||
|
@ -4366,12 +3896,6 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
|
@ -4700,6 +4224,7 @@
|
|||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
@ -4897,6 +4422,7 @@
|
|||
"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,
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
},
|
||||
|
@ -4904,24 +4430,6 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
|
@ -4952,7 +4460,8 @@
|
|||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.11.1",
|
||||
|
@ -4974,6 +4483,7 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
@ -5050,6 +4560,7 @@
|
|||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
|
@ -5731,7 +5242,8 @@
|
|||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/react-signalr": {
|
||||
"version": "0.2.24",
|
||||
|
@ -5765,68 +5277,6 @@
|
|||
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-stripe-js": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/react-stripe-js/-/react-stripe-js-1.1.5.tgz",
|
||||
"integrity": "sha512-4lIucgf/FZj6Uxvf/TH+QQa/Qi3FXigwN/QY6H7naPyoEfw9LOuTzdgPAmm7aeSXj8nZJXVoigiGzzFZchXjew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@stripe/react-stripe-js": "1.7.2",
|
||||
"@stripe/stripe-js": "1.29.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-stripe-js/node_modules/@stripe/react-stripe-js": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-1.7.2.tgz",
|
||||
"integrity": "sha512-IAVg2nPUPoSwI//XDRCO7D8mGeK4+N3Xg63fYZHmlfEWAuFVcuaqJKTT67uzIdKYZhHZ/NMdZw/ttz+GOjP/rQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@stripe/stripe-js": "^1.26.0",
|
||||
"react": "^16.8.0 || ^17.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-stripe-js/node_modules/@stripe/stripe-js": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-1.29.0.tgz",
|
||||
"integrity": "sha512-OsUxk0VLlum8E2d6onlEdKuQcvLMs7qTrOXCnl/BGV3fAm65qr6h3e1IZ5AX4lgUlPRrzRcddSOA5DvkKKYLvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-stripe-js/node_modules/react-dom": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"scheduler": "^0.20.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "17.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-stripe-js/node_modules/scheduler": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
|
||||
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
|
@ -5898,6 +5348,7 @@
|
|||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.13.0",
|
||||
"path-parse": "^1.0.7",
|
||||
|
@ -5914,6 +5365,7 @@
|
|||
"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,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
|
@ -6221,15 +5673,6 @@
|
|||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
|
@ -6469,19 +5912,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/stripe": {
|
||||
"version": "16.12.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-16.12.0.tgz",
|
||||
"integrity": "sha512-H7eFVLDxeTNNSn4JTRfL2//LzCbDrMSZ+2q1c7CanVWgK2qIW5TwS+0V7N9KcKZZNpYh/uCqK0PyZh/2UsaAtQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.*"
|
||||
}
|
||||
},
|
||||
"node_modules/styled-components": {
|
||||
"version": "6.1.12",
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz",
|
||||
|
@ -6594,6 +6024,7 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
|
@ -6840,7 +6271,8 @@
|
|||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.2.0",
|
||||
|
@ -6912,15 +6344,6 @@
|
|||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-merge-value": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-merge-value/-/use-merge-value-1.2.0.tgz",
|
||||
"integrity": "sha512-DXgG0kkgJN45TcyoXL49vJnn55LehnrmoHc7MbKi+QDBvr8dsesqws8UlyIWGHMR+JXgxc1nvY+jDGMlycsUcw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.x"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
|
@ -7211,15 +6634,6 @@
|
|||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bbuddy-ui",
|
||||
"version": "0.2.5",
|
||||
"version": "0.0.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 4200",
|
||||
|
@ -14,13 +14,10 @@
|
|||
"@ant-design/nextjs-registry": "^1.0.0",
|
||||
"@contentful/rich-text-react-renderer": "^15.22.9",
|
||||
"@microsoft/signalr": "^8.0.7",
|
||||
"@stripe/react-stripe-js": "^2.7.3",
|
||||
"@stripe/stripe-js": "^4.1.0",
|
||||
"agora-rtc-react": "2.1.0",
|
||||
"agora-rtc-react": "^2.1.0",
|
||||
"agora-rtc-sdk-ng": "^4.20.2",
|
||||
"antd": "^5.12.1",
|
||||
"antd-img-crop": "^4.21.0",
|
||||
"antd-style": "^3.6.2",
|
||||
"axios": "^1.6.5",
|
||||
"contentful": "^10.13.3",
|
||||
"dayjs": "^1.11.10",
|
||||
|
@ -31,9 +28,7 @@
|
|||
"react-dom": "^18",
|
||||
"react-signalr": "^0.2.24",
|
||||
"react-slick": "^0.29.0",
|
||||
"react-stripe-js": "^1.1.5",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"stripe": "^16.2.0",
|
||||
"styled-components": "^6.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"applinks": {
|
||||
"apps": [],
|
||||
"details": [
|
||||
{
|
||||
"appID": "GTYAM4FYH3.com.bbuddy.whistle",
|
||||
"paths": ["/en/experts/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.bbuddy.whistle",
|
||||
"sha256_cert_fingerprints": [
|
||||
"87:A2:49:9A:F4:05:9C:06:3C:3D:F3:10:88:F5:49:6D:5F:F2:BC:1E:90:0D:F2:37:A5:BA:37:19:5C:A3:75:C2",
|
||||
"D0:28:97:E7:64:5D:ED:8D:7F:F1:41:B2:E8:F6:AB:7B:EE:FB:A3:1A:A2:D7:92:D4:C5:41:9A:3C:47:CE:EB:43",
|
||||
"86:42:FE:EA:44:22:9D:16:7F:FC:70:92:A6:39:9D:B1:C3:F1:DE:21:32:4A:45:8C:07:98:39:55:AF:47:32:66"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
|
@ -1,5 +1,5 @@
|
|||
import { GeneralFilter, ExpertsData, ExpertDetails } from '../types/experts';
|
||||
import { apiRequest } from './helpers';
|
||||
import { GeneralFilter, ExpertsData, ExpertDetails, ExpertScheduler, ExpertSchedulerSession, SignupSessionData } from '../types/experts';
|
||||
|
||||
export const getExpertsList = (locale: string, filter?: GeneralFilter): Promise<ExpertsData> => apiRequest({
|
||||
url: '/home/coachsearch1',
|
||||
|
@ -14,18 +14,3 @@ export const getExpertById = (id: string, locale: string): Promise<ExpertDetails
|
|||
data: { id },
|
||||
locale
|
||||
});
|
||||
|
||||
export const getSchedulerByExpertId = (id: string, locale: string): Promise<ExpertScheduler> => apiRequest({
|
||||
url: '/home/sessionsignupdata',
|
||||
method: 'post',
|
||||
data: { id },
|
||||
locale
|
||||
});
|
||||
|
||||
export const getSchedulerSession = (data: SignupSessionData, locale: string, token: string): Promise<ExpertSchedulerSession> => apiRequest({
|
||||
url: '/home/sessionsignupsubmit',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client'
|
||||
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { ProfileData, ProfileRequest } from '../../types/profile';
|
||||
import { getPersonalData, setPersonData } from '../profile';
|
||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||
|
@ -18,7 +18,7 @@ export const useProfileSettings = (locale: string) => {
|
|||
setProfileSettings(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
|
||||
})
|
||||
.finally(() => {
|
||||
setFetchLoading(false);
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import {useCallback, useEffect, useState} from 'react';
|
||||
import {useLocalStorage} from '../../hooks/useLocalStorage';
|
||||
import {AUTH_TOKEN_KEY} from '../../constants/common';
|
||||
import {Room} from '../../types/rooms';
|
||||
import {getRoomDetails} from '../rooms';
|
||||
import {SessionState} from "../../types/sessions";
|
||||
|
||||
export const useRoomDetails = (locale: string, roomId: number) => {
|
||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
const [room, setRoom] = useState<Room>();
|
||||
const [errorData, setErrorData] = useState<any>();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [isStarted, setIsStarted] = useState(false);
|
||||
|
||||
const fetchData = useCallback(() => {
|
||||
setLoading(true);
|
||||
setErrorData(undefined);
|
||||
setRoom(undefined);
|
||||
|
||||
getRoomDetails(locale, jwt, roomId)
|
||||
.then((room) => {
|
||||
setRoom(room);
|
||||
})
|
||||
.catch((err) => {
|
||||
setErrorData(err);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (room?.state === SessionState.STARTED) {
|
||||
setIsStarted(true);
|
||||
}
|
||||
}, [room?.state])
|
||||
|
||||
return {
|
||||
fetchData,
|
||||
loading,
|
||||
room,
|
||||
errorData,
|
||||
isStarted
|
||||
};
|
||||
};
|
|
@ -1,125 +0,0 @@
|
|||
import { apiRequest } from './helpers';
|
||||
import {GetUsersForRooms, Report, ReportData, Room, RoomEdit, RoomEditDTO} from '../types/rooms';
|
||||
|
||||
export const getUpcomingRooms = (locale: string, token: string): Promise<Room[]> => apiRequest({
|
||||
url: '/home/upcomingsessionsall',
|
||||
method: 'post',
|
||||
data: {
|
||||
sessionType: 'room'
|
||||
},
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const getRecentRooms = (locale: string, token: string): Promise<Room[]> => apiRequest({
|
||||
url: '/home/historicalmeetings',
|
||||
method: 'post',
|
||||
data: {
|
||||
sessionType: 'room'
|
||||
},
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const getRoomDetails = (locale: string, token: string, id: number): Promise<Room> => apiRequest({
|
||||
url: '/home/room',
|
||||
method: 'post',
|
||||
data: { id },
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const deleteRoomClient = (locale: string, token: string, data: { sessionId: number, clientUserId: number }): Promise<any> => apiRequest({
|
||||
url: '/home/deleteclientfromroom',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const deleteRoomSupervisor = (locale: string, token: string, data: { sessionId: number, supervisorUserId: number }): Promise<any> => apiRequest({
|
||||
url: '/home/deletesupervisorfromroom',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const becomeRoomClient = (locale: string, token: string, data: { sessionId: number, clientUserId: number }): Promise<any> => apiRequest({
|
||||
url: '/home/becomeroomclient',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const becomeRoomSupervisor = (locale: string, token: string, data: { sessionId: number, supervisorUserId: number }): Promise<any> => apiRequest({
|
||||
url: '/home/becomeroomsupervisor',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const getUsersList = (locale: string, token: string, data: { template: string }): Promise<GetUsersForRooms> => apiRequest({
|
||||
url: '/home/findusersforroom',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const addClient = (locale: string, token: string, data: { sessionId: number, clientUserId: number }): Promise<any> => apiRequest({
|
||||
url: '/home/addclienttoroom',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const addSupervisor = (locale: string, token: string, data: { sessionId: number, supervisorUserId: number }): Promise<any> => apiRequest({
|
||||
url: '/home/addsupervisortoroom',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const createRoom = (locale: string, token: string): Promise<any> => apiRequest({
|
||||
url: '/home/createroom',
|
||||
method: 'post',
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const updateRoom = (locale: string, token: string, data: RoomEdit): Promise<any> => apiRequest({
|
||||
url: '/home/updateroom',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const getRoomById = (locale: string, token: string, id: number): Promise<RoomEditDTO> => apiRequest({
|
||||
url: '/home/getroomforedit',
|
||||
method: 'post',
|
||||
data: { id },
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
// report
|
||||
export const getReport = (locale: string, token: string, id: number): Promise<Report[]> => apiRequest({
|
||||
url: `/home/getsessionsupervisorscores?sessionId=${id}`,
|
||||
method: 'post',
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const saveReport = (locale: string, token: string, data: ReportData): Promise<any> => apiRequest({
|
||||
url: '/home/setsessionsupervisorscores',
|
||||
method: 'post',
|
||||
data,
|
||||
locale,
|
||||
token
|
||||
});
|
|
@ -91,11 +91,3 @@ export const finishSession = (locale: string, token: string, sessionId: number):
|
|||
locale,
|
||||
token
|
||||
});
|
||||
|
||||
export const sessionPaymentConfirm = (locale: string, token: string, sessionId: number): Promise<Session> => apiRequest({
|
||||
url: '/home/session_pay_confirm',
|
||||
method: 'post',
|
||||
data: { id: sessionId },
|
||||
locale,
|
||||
token
|
||||
});
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
"use server";
|
||||
|
||||
import { Stripe } from "stripe";
|
||||
|
||||
import { headers } from "next/headers";
|
||||
|
||||
import { formatAmountForStripe } from "../utils/stripe-helpers";
|
||||
import { stripe } from "../lib/stripe";
|
||||
|
||||
export async function createCheckoutSession(
|
||||
data: FormData,
|
||||
): Promise<{ client_secret: string | null; url: string | null }> {
|
||||
const ui_mode = data.get(
|
||||
"uiMode",
|
||||
) as Stripe.Checkout.SessionCreateParams.UiMode;
|
||||
console.log('DATA', data)
|
||||
const origin: string = headers().get("origin") as string;
|
||||
|
||||
const checkoutSession: Stripe.Checkout.Session =
|
||||
await stripe.checkout.sessions.create({
|
||||
mode: "payment",
|
||||
submit_type: "donate",
|
||||
line_items: [
|
||||
{
|
||||
quantity: 1,
|
||||
price_data: {
|
||||
currency: 'eur',
|
||||
product_data: {
|
||||
name: "Custom amount donation",
|
||||
},
|
||||
unit_amount: formatAmountForStripe(
|
||||
Number(data.get("customDonation") as string),
|
||||
'eur',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
...(ui_mode === "hosted" && {
|
||||
success_url: `${origin}/payment/with-checkout/result?session_id={CHECKOUT_SESSION_ID}`,
|
||||
cancel_url: `${origin}/with-checkout`,
|
||||
}),
|
||||
...(ui_mode === "embedded" && {
|
||||
return_url: `${origin}/payment/with-embedded-checkout/result?session_id={CHECKOUT_SESSION_ID}`,
|
||||
}),
|
||||
ui_mode,
|
||||
});
|
||||
|
||||
return {
|
||||
client_secret: checkoutSession.client_secret,
|
||||
url: checkoutSession.url,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createPaymentIntent(
|
||||
data: { amount: number, sessionId?: string },
|
||||
): Promise<{ client_secret: string }> {
|
||||
|
||||
const params = {
|
||||
amount: formatAmountForStripe(
|
||||
data.amount,
|
||||
'eur',
|
||||
),
|
||||
automatic_payment_methods: { enabled: true },
|
||||
currency: 'eur',
|
||||
} as Stripe.PaymentIntentCreateParams;
|
||||
|
||||
if (data?.sessionId){
|
||||
params.metadata = {
|
||||
sessionId : data.sessionId
|
||||
}
|
||||
}
|
||||
|
||||
const paymentIntent: Stripe.PaymentIntent =
|
||||
await stripe.paymentIntents.create(params);
|
||||
|
||||
return { client_secret: paymentIntent.client_secret as string };
|
||||
}
|
||||
|
||||
export const getStripePaymentStatus = async (payment_intent: string): Promise<Stripe.PaymentIntent> => await stripe.paymentIntents.retrieve(payment_intent);
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
// import { useTranslations } from 'next-intl';
|
||||
import Link from 'next/link';
|
||||
import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import {getTranslations, unstable_setRequestLocale} from 'next-intl/server';
|
||||
import { i18nText } from '../../../../i18nKeys';
|
||||
import { fetchBlogPosts } from '../../../../lib/contentful/blogPosts';
|
||||
import {fetchBlogPosts} from "../../../../lib/contentful/blogPosts";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function News({params: {locale}}: { params: { locale: string } }) {
|
||||
unstable_setRequestLocale(locale);
|
||||
const t = await getTranslations('Main');
|
||||
const { data, total } = await fetchBlogPosts({preview: false, sticky: true})
|
||||
const {data, total} = await fetchBlogPosts({preview: false, sticky: true})
|
||||
|
||||
return (
|
||||
<div className="main-articles">
|
||||
|
|
|
@ -15,8 +15,7 @@ import React, { ReactNode } from 'react';
|
|||
export default function MainLayout({ children, news, experts }: {
|
||||
children: ReactNode,
|
||||
news: ReactNode,
|
||||
experts: ReactNode,
|
||||
payment: ReactNode
|
||||
experts: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
|
@ -25,4 +24,4 @@ export default function MainLayout({ children, news, experts }: {
|
|||
{experts}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -57,7 +57,7 @@ export default function ExpertProfilePage({ params: { locale } }: { params: { lo
|
|||
}
|
||||
}, [jwt]);
|
||||
|
||||
return data ? (
|
||||
return (
|
||||
<Loader isLoading={loading}>
|
||||
<ExpertProfile
|
||||
isFull={isFull}
|
||||
|
@ -66,5 +66,5 @@ export default function ExpertProfilePage({ params: { locale } }: { params: { lo
|
|||
updateData={setData}
|
||||
/>
|
||||
</Loader>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import React, { Suspense } from 'react';
|
||||
import { unstable_setRequestLocale } from 'next-intl/server';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { AccountMenu, RoomDetails, RoomsTabs } from '../../../../../../components/Account';
|
||||
import { RoomsType } from '../../../../../../types/rooms';
|
||||
|
||||
const ROOMS_ROUTES = [RoomsType.UPCOMING, RoomsType.RECENT, RoomsType.NEW];
|
||||
|
||||
export async function generateStaticParams({
|
||||
params: { locale },
|
||||
}: { params: { locale: string } }) {
|
||||
return [{ locale, slug: [RoomsType.UPCOMING] }];
|
||||
}
|
||||
|
||||
export default function RoomsDetailItem({ params: { locale, slug } }: { params: { locale: string, slug?: string[] } }) {
|
||||
unstable_setRequestLocale(locale);
|
||||
const roomType: string = slug?.length > 0 && slug[0] || '';
|
||||
const roomId: number | null = slug?.length > 1 && Number(slug[1]) || null;
|
||||
|
||||
if (!slug?.length || slug?.length > 2) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
if (ROOMS_ROUTES.includes(roomType as RoomsType) && Number.isInteger(roomId)) {
|
||||
return (
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<RoomDetails
|
||||
locale={locale}
|
||||
roomId={roomId || 0}
|
||||
activeType={roomType as RoomsType}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
if (ROOMS_ROUTES.includes(roomType as RoomsType) && !Number.isInteger(roomId)) {
|
||||
return (
|
||||
<>
|
||||
<div className="col-xl-3 col-lg-4 d-none d-lg-block">
|
||||
<AccountMenu locale={locale}/>
|
||||
</div>
|
||||
<div className="col-xl-9 col-lg-8 ">
|
||||
<div className="page-account__inner">
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<RoomsTabs
|
||||
locale={locale}
|
||||
activeTab={roomType as RoomsType}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return notFound();
|
||||
};
|
|
@ -1,12 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { redirect } from 'next/navigation';
|
||||
import { useLocalStorage } from '../../../../../hooks/useLocalStorage';
|
||||
import { AUTH_TOKEN_KEY } from '../../../../../constants/common';
|
||||
import { RoomsType } from '../../../../../types/rooms';
|
||||
|
||||
export default function RoomsMainPage() {
|
||||
const [token] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
|
||||
return token ? redirect(`rooms/${RoomsType.UPCOMING}`) : null;
|
||||
};
|
|
@ -26,7 +26,7 @@ export default function SessionDetailItem({ params: { locale, slug } }: { params
|
|||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<SessionDetails
|
||||
locale={locale}
|
||||
sessionId={sessionId as number}
|
||||
sessionId={sessionId}
|
||||
activeType={sessionType as SessionType}
|
||||
/>
|
||||
</Suspense>
|
||||
|
|
|
@ -3,7 +3,7 @@ import type { Metadata } from 'next';
|
|||
import { unstable_setRequestLocale } from 'next-intl/server';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { GeneralTopSection } from '../../../components/Page';
|
||||
import { ScreenCarousel } from '../../../components/Page/ScreenCarousel';
|
||||
import { ScreenCarousel } from '../../../components/Page/ScreenCarousel/index';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Bbuddy - Become a BB expert',
|
||||
|
|
|
@ -9,6 +9,7 @@ import {CustomPagination} from "../../../components/view/CustomPagination";
|
|||
import {DEFAULT_PAGE_SIZE} from "../../../constants/common";
|
||||
import {BlogPosts} from "../../../components/BlogPosts/BlogPosts";
|
||||
|
||||
|
||||
interface BlogPostPageParams {
|
||||
slug: string
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { getExpertById, getExpertsList } from '../../../../actions/experts';
|
|||
import {
|
||||
ExpertCard,
|
||||
ExpertCertificate,
|
||||
ExpertInformation,
|
||||
ExpertPractice
|
||||
} from '../../../../components/Experts/ExpertDetails';
|
||||
import { Details } from '../../../../types/education';
|
||||
|
@ -81,7 +82,8 @@ export default async function ExpertItem({ params: { expertId = '', locale } }:
|
|||
</BackButton>
|
||||
</Suspense>
|
||||
</div>
|
||||
<ExpertCard expert={expert} locale={locale} expertId={expertId}/>
|
||||
<ExpertCard expert={expert} locale={locale} />
|
||||
<ExpertInformation expert={expert} locale={locale} />
|
||||
|
||||
<h2 className="title-h2">{i18nText('expertBackground', locale)}</h2>
|
||||
<p className="base-text">
|
||||
|
|
|
@ -42,4 +42,4 @@ export default function LocaleLayout({ children, params: { locale } }: LayoutPro
|
|||
</ConfigProvider>
|
||||
</AntdRegistry>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import type { Stripe } from "stripe";
|
||||
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { stripe } from "../../../lib/stripe";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
let event: Stripe.Event;
|
||||
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(
|
||||
await (await req.blob()).text(),
|
||||
req.headers.get("stripe-signature") as string,
|
||||
process.env.STRIPE_WEBHOOK_SECRET as string,
|
||||
);
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : "Unknown error";
|
||||
// On error, log and return the error message.
|
||||
if (err! instanceof Error) console.log(err);
|
||||
console.log(`❌ Error message: ${errorMessage}`);
|
||||
return NextResponse.json(
|
||||
{ message: `Webhook Error: ${errorMessage}` },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Successfully constructed event.
|
||||
console.log("✅ Success:", event.id);
|
||||
|
||||
const permittedEvents: string[] = [
|
||||
"checkout.session.completed",
|
||||
"payment_intent.succeeded",
|
||||
"payment_intent.payment_failed",
|
||||
];
|
||||
|
||||
if (permittedEvents.includes(event.type)) {
|
||||
let data;
|
||||
|
||||
try {
|
||||
switch (event.type) {
|
||||
case "checkout.session.completed":
|
||||
data = event.data.object as Stripe.Checkout.Session;
|
||||
console.log(`💰 CheckoutSession status: ${data.payment_status}`);
|
||||
break;
|
||||
case "payment_intent.payment_failed":
|
||||
data = event.data.object as Stripe.PaymentIntent;
|
||||
console.log(`❌ Payment failed: ${data.last_payment_error?.message}`);
|
||||
break;
|
||||
case "payment_intent.succeeded":
|
||||
data = event.data.object as Stripe.PaymentIntent;
|
||||
console.log(`💰 PaymentIntent status: ${data.status}`);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled event: ${event.type}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return NextResponse.json(
|
||||
{ message: "Webhook handler failed" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
// Return a response to acknowledge receipt of the event.
|
||||
return NextResponse.json({ message: "Received" }, { status: 200 });
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import { Form, message, Upload } from 'antd';
|
||||
import type { UploadFile } from 'antd';
|
||||
import { Button, Form, message, Upload } from 'antd';
|
||||
import type { GetProp, UploadFile, UploadProps } from 'antd';
|
||||
import ImgCrop from 'antd-img-crop';
|
||||
import { CameraOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
import { useRouter } from '../../navigation';
|
||||
|
@ -12,14 +12,17 @@ import { validateImage } from '../../utils/account';
|
|||
import { useProfileSettings } from '../../actions/hooks/useProfileSettings';
|
||||
import { CustomInput } from '../view/CustomInput';
|
||||
import { OutlinedButton } from '../view/OutlinedButton';
|
||||
import { FilledSquareButton, FilledYellowButton } from '../view/FilledButton';
|
||||
import {FilledButton, FilledSquareButton, FilledYellowButton} from '../view/FilledButton';
|
||||
import { DeleteAccountModal } from '../Modals/DeleteAccountModal';
|
||||
import { Loader } from '../view/Loader';
|
||||
import {ButtonProps} from "antd/es/button/button";
|
||||
|
||||
type ProfileSettingsProps = {
|
||||
locale: string;
|
||||
};
|
||||
|
||||
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
|
||||
|
||||
export const ProfileSettings: FC<ProfileSettingsProps> = ({ locale }) => {
|
||||
const [form] = Form.useForm<ProfileRequest>();
|
||||
const { profileSettings, fetchProfileSettings, save, fetchLoading } = useProfileSettings(locale);
|
||||
|
@ -55,7 +58,7 @@ export const ProfileSettings: FC<ProfileSettingsProps> = ({ locale }) => {
|
|||
const onSaveProfile = () => {
|
||||
form.validateFields()
|
||||
.then(({ login, surname, username }) => {
|
||||
const { phone, role, languagesLinks } = profileSettings || {};
|
||||
const { phone, role, languagesLinks } = profileSettings;
|
||||
const newProfile: ProfileRequest = {
|
||||
phone,
|
||||
role,
|
||||
|
@ -72,7 +75,7 @@ export const ProfileSettings: FC<ProfileSettingsProps> = ({ locale }) => {
|
|||
reader.readAsDataURL(photo as File);
|
||||
reader.onloadend = () => {
|
||||
const newReg = new RegExp('data:image/(png|jpg|jpeg);base64,')
|
||||
newProfile.faceImage = reader?.result?.replace(newReg, '');
|
||||
newProfile.faceImage = reader.result.replace(newReg, '');
|
||||
newProfile.isFaceImageKeepExisting = false;
|
||||
|
||||
onSave(newProfile);
|
||||
|
@ -178,7 +181,7 @@ export const ProfileSettings: FC<ProfileSettingsProps> = ({ locale }) => {
|
|||
>
|
||||
{i18nText('save', locale)}
|
||||
</FilledYellowButton>
|
||||
<OutlinedButton onClick={() => router.push('settings/change-password')}>
|
||||
<OutlinedButton onClick={() => router.push('change-password')}>
|
||||
{i18nText('changePass', locale)}
|
||||
</OutlinedButton>
|
||||
<OutlinedButton
|
||||
|
|
|
@ -37,7 +37,7 @@ export const Agora = ({ sessionId, secret, stopCalling, remoteUser }: AgoraProps
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="b-agora__wrap b-agora__wrap__single">
|
||||
<div className="b-agora__wrap">
|
||||
<RemoteUserPanel calling={calling} user={remoteUser} />
|
||||
<div className="b-agora__panel">
|
||||
<MediaControl
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import { useJoin } from 'agora-rtc-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { MediaControl } from './view';
|
||||
import { UsersGroupPanel } from './components';
|
||||
|
||||
type AgoraProps = {
|
||||
roomId: number;
|
||||
secret?: string;
|
||||
stopCalling: () => void;
|
||||
};
|
||||
|
||||
export const AgoraGroup = ({ roomId, secret, stopCalling }: AgoraProps) => {
|
||||
const [calling, setCalling] = useState(false);
|
||||
const [micOn, setMic] = useState(false);
|
||||
const [cameraOn, setCamera] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setCalling(true);
|
||||
}, []);
|
||||
|
||||
useJoin(
|
||||
{
|
||||
appid: process.env.NEXT_PUBLIC_AGORA_APPID,
|
||||
channel: `${roomId}-${secret}`,
|
||||
token: null,
|
||||
},
|
||||
calling,
|
||||
);
|
||||
|
||||
const stop = () => {
|
||||
stopCalling();
|
||||
setCalling(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="b-agora__wrap">
|
||||
<UsersGroupPanel calling={calling} micOn={micOn} cameraOn={cameraOn}/>
|
||||
</div>
|
||||
<div className="b-agora__panel_group">
|
||||
<MediaControl
|
||||
calling={calling}
|
||||
cameraOn={cameraOn}
|
||||
micOn={micOn}
|
||||
setCalling={stop}
|
||||
setCamera={() => setCamera(a => !a)}
|
||||
setMic={() => setMic(a => !a)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
import {
|
||||
useIsConnected, useLocalCameraTrack, useLocalMicrophoneTrack, usePublish,
|
||||
useRemoteAudioTracks,
|
||||
useRemoteUsers,
|
||||
useRemoteVideoTracks
|
||||
} from 'agora-rtc-react';
|
||||
import { LocalUser } from './LocalUser';
|
||||
import { RemoteVideoPlayer } from './RemoteVideoPlayer';
|
||||
|
||||
type UsersGroupPanelProps = {
|
||||
calling: boolean;
|
||||
micOn: boolean;
|
||||
cameraOn: boolean;
|
||||
};
|
||||
|
||||
export const UsersGroupPanel = ({ calling, micOn, cameraOn }: UsersGroupPanelProps) => {
|
||||
const isConnected = useIsConnected();
|
||||
const remoteUsers = useRemoteUsers();
|
||||
const { localMicrophoneTrack } = useLocalMicrophoneTrack(micOn);
|
||||
const { localCameraTrack } = useLocalCameraTrack(cameraOn);
|
||||
const { videoTracks } = useRemoteVideoTracks(remoteUsers);
|
||||
const { audioTracks } = useRemoteAudioTracks(remoteUsers);
|
||||
|
||||
usePublish([localMicrophoneTrack, localCameraTrack]);
|
||||
audioTracks.map(track => track.play());
|
||||
|
||||
return calling && isConnected && remoteUsers ? (
|
||||
<div className={`b-agora__remote_groups gr-${remoteUsers.length + 1}`}>
|
||||
<div>
|
||||
<LocalUser
|
||||
audioTrack={localMicrophoneTrack}
|
||||
cameraOn={cameraOn}
|
||||
micOn={micOn}
|
||||
videoTrack={localCameraTrack}
|
||||
/>
|
||||
</div>
|
||||
{remoteUsers.length > 0 && remoteUsers.map((user) => (
|
||||
<div key={user.uid}>
|
||||
<RemoteVideoPlayer track={user.videoTrack} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null;
|
||||
}
|
|
@ -3,4 +3,3 @@ export * from './UserCover';
|
|||
export * from './RemoteUsers';
|
||||
export * from './LocalUserPanel';
|
||||
export * from './RemoteUserPanel';
|
||||
export * from './UsersGroupPanel';
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
import AgoraRTC, { AgoraRTCProvider } from 'agora-rtc-react';
|
||||
import { Session } from '../../../types/sessions';
|
||||
import { Room } from '../../../types/rooms';
|
||||
import { Agora } from './Agora';
|
||||
import { AgoraGroup } from './AgoraGroup';
|
||||
|
||||
export const AgoraClient = ({ session, stopCalling, isCoach }: { session?: Session, stopCalling: () => void, isCoach: boolean }) => {
|
||||
const remoteUser = isCoach ? (session?.clients?.length ? session?.clients[0] : undefined) : session?.coach;
|
||||
|
@ -22,17 +20,3 @@ export const AgoraClient = ({ session, stopCalling, isCoach }: { session?: Sessi
|
|||
</AgoraRTCProvider>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export const AgoraClientGroup = ({ room, stopCalling }: { room?: Room, stopCalling: () => void }) => {
|
||||
return room ? (
|
||||
<AgoraRTCProvider client={AgoraRTC.createClient({ mode: "rtc", codec: "vp8" })}>
|
||||
{room && (
|
||||
<AgoraGroup
|
||||
roomId={room.id}
|
||||
secret={room.secret}
|
||||
stopCalling={stopCalling}
|
||||
/>
|
||||
)}
|
||||
</AgoraRTCProvider>
|
||||
) : null;
|
||||
};
|
||||
|
|
|
@ -3,4 +3,3 @@
|
|||
export { AccountMenu } from './AccountMenu';
|
||||
export { ProfileSettings } from './ProfileSettings';
|
||||
export * from './sessions';
|
||||
export * from './rooms';
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { EditRoomForm } from './EditRoomForm';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { createRoom } from '../../../actions/rooms';
|
||||
import { Loader } from '../../view/Loader';
|
||||
import { useRouter } from '../../../navigation';
|
||||
import { RoomsType } from '../../../types/rooms';
|
||||
|
||||
|
||||
export const CreateRoom = ({ locale, jwt }: { locale: string, jwt: string }) => {
|
||||
const [roomId, setRoomId] = useState<number>();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const router = useRouter();
|
||||
|
||||
const getRoom = debounce(() => {
|
||||
createRoom(locale, jwt)
|
||||
.then((data) => {
|
||||
setRoomId(data);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
}, 500);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
getRoom();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Loader isLoading={loading}>
|
||||
{roomId && (
|
||||
<EditRoomForm
|
||||
roomId={roomId}
|
||||
locale={locale}
|
||||
jwt={jwt}
|
||||
mode="create"
|
||||
afterSubmit={() => router.push(`/account/rooms/${RoomsType.UPCOMING}`)}
|
||||
/>
|
||||
)}
|
||||
</Loader>
|
||||
)
|
||||
};
|
|
@ -1,220 +0,0 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Button, Form, Input, notification } from 'antd';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import { i18nText } from '../../../i18nKeys';
|
||||
import { Tag } from '../../../types/tags';
|
||||
import { Slot } from '../../../types/experts';
|
||||
import { RoomEdit, RoomEditDTO } from '../../../types/rooms';
|
||||
import { getRoomById, updateRoom } from '../../../actions/rooms';
|
||||
import { Loader } from '../../view/Loader';
|
||||
import { CustomInput } from '../../view/CustomInput';
|
||||
import { CustomSelect } from '../../view/CustomSelect';
|
||||
import { CustomSwitch } from '../../view/CustomSwitch';
|
||||
import { CustomMultiSelect } from '../../view/CustomMultiSelect';
|
||||
import { CustomDatePicker } from '../../view/CustomDatePicker';
|
||||
|
||||
type EditRoomFormProps = {
|
||||
roomId: number,
|
||||
locale: string,
|
||||
jwt: string,
|
||||
mode: 'create' | 'edit';
|
||||
afterSubmit?: () => void;
|
||||
}
|
||||
|
||||
type RoomFormState = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
date?: Dayjs;
|
||||
maxCount?: number;
|
||||
startAt?: string;
|
||||
supervisor?: boolean;
|
||||
tags?: number[];
|
||||
};
|
||||
|
||||
export const EditRoomForm = ({ roomId, locale, jwt, mode, afterSubmit }: EditRoomFormProps) => {
|
||||
const [form] = Form.useForm<RoomFormState>();
|
||||
const [editingRoom, setEditingRoom] = useState<RoomEditDTO>();
|
||||
const dateValue = Form.useWatch('date', form);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [fetchLoading, setFetchLoading] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setFetchLoading(true);
|
||||
getRoomById(locale, jwt, roomId)
|
||||
.then((data) => {
|
||||
setEditingRoom(data);
|
||||
const { item } = data || {};
|
||||
|
||||
if (mode === 'edit' && item) {
|
||||
form.setFieldsValue({
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
date: item?.scheduledStartAtUtc ? dayjs(item.scheduledStartAtUtc) : undefined,
|
||||
maxCount: item.maxClients,
|
||||
startAt: item?.scheduledStartAtUtc,
|
||||
supervisor: item.isNeedSupervisor,
|
||||
tags: item.tagIds || undefined
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setFetchLoading(false);
|
||||
})
|
||||
}, []);
|
||||
|
||||
const getAvailableSlots = useCallback((): string[] => {
|
||||
const dateList = new Set<string>();
|
||||
if (editingRoom?.availableSlots) {
|
||||
editingRoom.availableSlots.forEach(({ startTime }) => {
|
||||
const [date] = startTime.split('T');
|
||||
dateList.add(dayjs(date).format('YYYY-MM-DD'));
|
||||
});
|
||||
|
||||
return Array.from(dateList);
|
||||
}
|
||||
|
||||
return [];
|
||||
}, [editingRoom?.availableSlots]);
|
||||
|
||||
const getTimeOptions = (slots?: Slot[], curDate?: Dayjs) => {
|
||||
const date = curDate ? curDate.format('YYYY-MM-DD') : '';
|
||||
if (slots && slots?.length && date) {
|
||||
return slots.filter(({ startTime }) => dayjs(startTime).format('YYYY-MM-DD') === date)
|
||||
.map(({ startTime, endTime }) => ({ value: startTime, label: `${dayjs(startTime).format('HH:mm')} - ${dayjs(endTime).format('HH:mm')}` }));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const getTagsOptions = (tags?: Tag[]) => {
|
||||
if (tags) {
|
||||
return tags.map(({ id, name }) => ({ value: id, label: <span>{name}</span> })) || [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
setLoading(true);
|
||||
const { title, description, startAt, maxCount, tags, supervisor } = form.getFieldsValue();
|
||||
const result: RoomEdit = {
|
||||
...editingRoom,
|
||||
id: roomId,
|
||||
title,
|
||||
scheduledStartAtUtc: startAt,
|
||||
maxClients: maxCount,
|
||||
isNeedSupervisor: supervisor,
|
||||
tagIds: tags || []
|
||||
};
|
||||
|
||||
if (description) {
|
||||
result.description = description;
|
||||
}
|
||||
|
||||
updateRoom(locale, jwt, result)
|
||||
.then(() => {
|
||||
afterSubmit && afterSubmit();
|
||||
})
|
||||
.catch((err) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
});
|
||||
}
|
||||
|
||||
const disabledDate = (current: Dayjs) => current && !getAvailableSlots().includes(current.format('YYYY-MM-DD'));
|
||||
|
||||
return (
|
||||
<Loader isLoading={fetchLoading}>
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="off"
|
||||
style={{ display: 'flex', gap: 16, flexDirection: 'column' }}
|
||||
onFinish={onSubmit}
|
||||
className="b-room-form"
|
||||
>
|
||||
<Form.Item
|
||||
name="title"
|
||||
rules={[{ required: true }]}
|
||||
noStyle
|
||||
>
|
||||
<CustomInput
|
||||
size="small"
|
||||
placeholder={i18nText('title', locale)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="description">
|
||||
<Input.TextArea
|
||||
className="b-textarea"
|
||||
rows={4}
|
||||
maxLength={1000}
|
||||
placeholder={i18nText('description', locale)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="b-room-form__grid">
|
||||
<Form.Item
|
||||
name="date"
|
||||
rules={[{ required: true }]}
|
||||
noStyle
|
||||
>
|
||||
<CustomDatePicker
|
||||
locale={locale}
|
||||
label={i18nText('room.date', locale)}
|
||||
disabledDate={disabledDate}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="startAt"
|
||||
rules={[{ required: true }]}
|
||||
noStyle
|
||||
>
|
||||
<CustomSelect
|
||||
label={i18nText('room.time', locale)}
|
||||
options={getTimeOptions(editingRoom?.availableSlots, dateValue)}
|
||||
disabled={!dateValue}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="maxCount"
|
||||
rules={[{ required: true }]}
|
||||
noStyle
|
||||
>
|
||||
<CustomSelect
|
||||
label={i18nText('room.maxParticipants', locale)}
|
||||
options={Array.from({ length: 16 }).map((_, i) => ({ value: i+1, label: i+1 }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="supervisor"
|
||||
valuePropName="checked"
|
||||
label={i18nText('room.presenceOfSupervisor', locale)}
|
||||
className="b-room-switch"
|
||||
>
|
||||
<CustomSwitch />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item
|
||||
name="tags"
|
||||
noStyle
|
||||
>
|
||||
<CustomMultiSelect
|
||||
label={i18nText('topics', locale)}
|
||||
options={getTagsOptions(editingRoom?.tags)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
className="card-detail__apply"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
{i18nText('room.save', locale)}
|
||||
</Button>
|
||||
</Form>
|
||||
</Loader>
|
||||
);
|
||||
};
|
|
@ -1,88 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { RoomsType } from '../../../types/rooms';
|
||||
import { useSessionTracking } from '../../../actions/hooks/useSessionTracking';
|
||||
import { AccountMenu } from '../AccountMenu';
|
||||
import { Loader } from '../../view/Loader';
|
||||
import { RoomDetailsContent } from './RoomDetailsContent';
|
||||
import { useRoomDetails } from '../../../actions/hooks/useRoomDetails';
|
||||
import { AgoraClientGroup } from '../agora';
|
||||
import { SupervisorReportModal } from '../../Modals/SupervisorReportModal';
|
||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||
import { AUTH_USER } from '../../../constants/common';
|
||||
|
||||
type RoomDetailsProps = {
|
||||
locale: string;
|
||||
roomId: number;
|
||||
activeType: RoomsType;
|
||||
};
|
||||
|
||||
export const RoomDetails = ({ roomId, locale, activeType }: RoomDetailsProps) => {
|
||||
const { room, errorData, loading, fetchData, isStarted } = useRoomDetails(locale, roomId);
|
||||
const tracking = useSessionTracking(locale, roomId);
|
||||
const [isCalling, setIsCalling] = useState<boolean>(false);
|
||||
const [isOpenReport, setIsOpenReport] = useState<boolean>(false);
|
||||
const [userData] = useLocalStorage(AUTH_USER, '');
|
||||
const { id: userId = 0 } = userData ? JSON.parse(userData) : {};
|
||||
const isSupervisor = room?.supervisor && room.supervisor.id === +userId || false;
|
||||
|
||||
useEffect(() => {
|
||||
if (isCalling) {
|
||||
tracking.start();
|
||||
} else {
|
||||
tracking.stop();
|
||||
}
|
||||
}, [isCalling]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSupervisor && isStarted) {
|
||||
setIsOpenReport(true);
|
||||
}
|
||||
}, [isStarted]);
|
||||
|
||||
const stopCalling = () => {
|
||||
setIsCalling(false);
|
||||
fetchData();
|
||||
}
|
||||
|
||||
return isCalling
|
||||
? (
|
||||
<AgoraClientGroup
|
||||
room={room}
|
||||
stopCalling={stopCalling}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<div className="col-xl-3 col-lg-4 d-none d-lg-block">
|
||||
<AccountMenu locale={locale} />
|
||||
</div>
|
||||
<div className="col-xl-9 col-lg-8 ">
|
||||
<div className="page-account__inner">
|
||||
<Loader
|
||||
isLoading={loading}
|
||||
errorData={errorData}
|
||||
refresh={fetchData}
|
||||
>
|
||||
<RoomDetailsContent
|
||||
locale={locale}
|
||||
room={room}
|
||||
activeType={activeType}
|
||||
startRoom={() => setIsCalling(true)}
|
||||
refresh={fetchData}
|
||||
/>
|
||||
</Loader>
|
||||
</div>
|
||||
{isSupervisor && room?.id && (
|
||||
<SupervisorReportModal
|
||||
open={isOpenReport}
|
||||
handleCancel={() => setIsOpenReport(false)}
|
||||
locale={locale}
|
||||
refresh={fetchData}
|
||||
roomId={room.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,377 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button, notification, Tag } from 'antd';
|
||||
import { DeleteOutlined, LeftOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from '../../../navigation';
|
||||
import { Report, Room, RoomsType } from '../../../types/rooms';
|
||||
import { i18nText } from '../../../i18nKeys';
|
||||
import { LinkButton } from '../../view/LinkButton';
|
||||
import {
|
||||
addClient,
|
||||
addSupervisor,
|
||||
becomeRoomClient,
|
||||
becomeRoomSupervisor,
|
||||
deleteRoomClient,
|
||||
deleteRoomSupervisor,
|
||||
getReport
|
||||
} from '../../../actions/rooms';
|
||||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||
import { UserListModal } from '../../Modals/UsersListModal';
|
||||
import { SessionState } from '../../../types/sessions';
|
||||
import { EditRoomForm } from './EditRoomForm';
|
||||
|
||||
type RoomDetailsContentProps = {
|
||||
locale: string;
|
||||
activeType: RoomsType;
|
||||
room?: Room;
|
||||
startRoom: () => void;
|
||||
refresh: () => void;
|
||||
};
|
||||
|
||||
export const RoomDetailsContent = ({ room, startRoom, locale, activeType, refresh }: RoomDetailsContentProps) => {
|
||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
const [userData] = useLocalStorage(AUTH_USER, '');
|
||||
const { id: userId = 0 } = userData ? JSON.parse(userData) : {};
|
||||
const router = useRouter();
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
const [forSupervisor, setForSupervisor] = useState<boolean>(false);
|
||||
const startDate = room?.scheduledStartAtUtc ? dayjs(room?.scheduledStartAtUtc).locale(locale) : null;
|
||||
const endDate = room?.scheduledEndAtUtc ? dayjs(room?.scheduledEndAtUtc).locale(locale) : null;
|
||||
const today = startDate ? dayjs().format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD') : false;
|
||||
const isCreator = room?.coach && room.coach.id === +userId || false;
|
||||
const isSupervisor = room?.supervisor && room.supervisor.id === +userId || false;
|
||||
const isClient = room?.clients && room.clients.length > 0 && room.clients.map(({ id }) => id).includes(+userId) || false;
|
||||
const isTimeBeforeStart = room?.scheduledStartAtUtc ? dayjs() < dayjs(room.scheduledStartAtUtc) : false;
|
||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||
const [report, setReport] = useState<Report[] | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (room?.id && room?.supervisor && activeType === RoomsType.RECENT) {
|
||||
getReport(locale, jwt, room.id)
|
||||
.then((data) => {
|
||||
setReport(data);
|
||||
})
|
||||
}
|
||||
}, [room])
|
||||
|
||||
const goBack = () => router.push(`/account/rooms/${activeType}`);
|
||||
|
||||
const checkUserApply = (): boolean => (!room?.supervisor || !isSupervisor) && (!room?.clients || room?.clients && room?.clients.length === 0 || !isClient);
|
||||
|
||||
const deleteClient = (clientUserId: number) => {
|
||||
if (room?.id) {
|
||||
deleteRoomClient(locale, jwt, { sessionId: room.id, clientUserId })
|
||||
.then(() => {
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const deleteSupervisor = (supervisorUserId?: number) => {
|
||||
if (room?.id && supervisorUserId) {
|
||||
deleteRoomSupervisor(locale, jwt, { sessionId: room.id, supervisorUserId })
|
||||
.then(() => {
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
const becomeClient = () => {
|
||||
if (room?.id && userId) {
|
||||
becomeRoomClient(locale, jwt, { sessionId: room.id, clientUserId: +userId })
|
||||
.then(() => {
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const becomeSupervisor = () => {
|
||||
if (room?.id && userId) {
|
||||
becomeRoomSupervisor(locale, jwt, { sessionId: room.id, supervisorUserId: +userId })
|
||||
.then(() => {
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onInviteSupervisor = () => {
|
||||
setForSupervisor(true)
|
||||
setShowModal(true);
|
||||
};
|
||||
|
||||
const onAddUser = (id: number) => {
|
||||
if (room?.id) {
|
||||
setShowModal(false);
|
||||
|
||||
if (forSupervisor) {
|
||||
addSupervisor(locale, jwt, { sessionId: room.id, supervisorUserId: id })
|
||||
.then(() => {
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
});
|
||||
} else {
|
||||
addClient(locale, jwt, { sessionId: room.id, clientUserId: id })
|
||||
.then(() => {
|
||||
refresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const afterEditing = () => {
|
||||
setIsEdit(false);
|
||||
refresh();
|
||||
}
|
||||
|
||||
return !isEdit ? (
|
||||
<div className="card-detail">
|
||||
<div>
|
||||
<Button
|
||||
className="card-detail__back"
|
||||
type="link"
|
||||
icon={<LeftOutlined/>}
|
||||
onClick={goBack}
|
||||
>
|
||||
{i18nText('back', locale)}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="card-detail__name">{room?.title || ''}</div>
|
||||
<div
|
||||
className={`card-detail__date${today ? ' chosen' : ''}${activeType === RoomsType.RECENT ? ' history' : ''}`}>
|
||||
{today
|
||||
? `${i18nText('today', locale)} ${startDate?.format('HH:mm')} - ${endDate?.format('HH:mm')}`
|
||||
: `${startDate?.format('D MMMM')} ${startDate?.format('HH:mm')} - ${endDate?.format('HH:mm')}`}
|
||||
</div>
|
||||
{room?.themesTags && room.themesTags.length > 0 && (
|
||||
<div className="card-detail__skills">
|
||||
<div className="skills__list">
|
||||
{room.themesTags.map((skill) => <Tag key={skill?.id}
|
||||
className="skills__list__item">{skill?.name}</Tag>)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{room?.description && <div className="card-profile__desc">{room.description}</div>}
|
||||
{activeType === RoomsType.UPCOMING && (isCreator || isSupervisor || isClient) && (
|
||||
<div className="card-detail__actions">
|
||||
{(isCreator || isClient || isSupervisor) && (
|
||||
<Button
|
||||
className="card-detail__apply"
|
||||
onClick={startRoom}
|
||||
>
|
||||
{isCreator ? i18nText('session.start', locale) : i18nText('session.join', locale)}
|
||||
</Button>
|
||||
)}
|
||||
{isCreator && isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && (
|
||||
<Button
|
||||
className="card-detail__filled"
|
||||
onClick={() => setIsEdit(true)}
|
||||
>
|
||||
{i18nText('room.editRoom', locale)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="card-detail__profile">
|
||||
<div className="card-detail__profile_title">
|
||||
<div>{i18nText('room.roomCreator', locale)}</div>
|
||||
</div>
|
||||
<div className="card-detail__profile_list">
|
||||
<div className="card-detail__profile_item">
|
||||
<div className="card-detail__portrait card-detail__portrait_small">
|
||||
<Image src={room?.coach?.faceImageUrl || '/images/user-avatar.png'} width={86} height={86} alt=""/>
|
||||
</div>
|
||||
<div className="card-detail__inner">
|
||||
<div className="card-detail__name">{`${room?.coach?.name} ${room?.coach?.surname || ''}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{room?.isNeedSupervisor && (
|
||||
<div className="card-detail__profile">
|
||||
<div className="card-detail__profile_title">
|
||||
<div>{i18nText('room.supervisor', locale)}</div>
|
||||
</div>
|
||||
{room?.supervisor && (
|
||||
<div className="card-detail__profile_list">
|
||||
<div className="card-detail__profile_item">
|
||||
<div className="card-detail__portrait card-detail__portrait_small">
|
||||
<Image src={room?.supervisor?.faceImageUrl || '/images/user-avatar.png'} width={86}
|
||||
height={86}
|
||||
alt=""/>
|
||||
</div>
|
||||
<div className="card-detail__inner">
|
||||
<div
|
||||
className="card-detail__name">{`${room?.supervisor?.name} ${room?.supervisor?.surname || ''}`}</div>
|
||||
</div>
|
||||
{isCreator && activeType === RoomsType.UPCOMING && isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && (
|
||||
<LinkButton
|
||||
type="link"
|
||||
style={{alignSelf: 'flex-start'}}
|
||||
danger
|
||||
icon={<DeleteOutlined/>}
|
||||
onClick={() => deleteSupervisor(room?.supervisor?.id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{room?.supervisor && activeType === RoomsType.RECENT && (
|
||||
<>
|
||||
{room?.supervisorComment && (
|
||||
<div className="card-detail__supervisor-comment">{room.supervisorComment}</div>
|
||||
)}
|
||||
{report && report.length > 0 && (
|
||||
<div className="card-detail__report-list">
|
||||
{report.map(({ key, score }) => (
|
||||
<div key={key}>
|
||||
<div>{i18nText(`room.rating_${key?.toLowerCase()}`, locale)}</div>
|
||||
<div className="card-detail__report-list_divider" />
|
||||
<div>{score || 0}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && !room?.supervisor && isCreator && activeType === RoomsType.UPCOMING && (
|
||||
<Button
|
||||
className="card-detail__filled"
|
||||
onClick={onInviteSupervisor}
|
||||
>
|
||||
{i18nText('room.inviteSupervisor', locale)}
|
||||
</Button>
|
||||
)}
|
||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && !room?.supervisor && !isCreator && activeType === RoomsType.UPCOMING && checkUserApply() && (
|
||||
<Button
|
||||
className="card-detail__apply"
|
||||
onClick={becomeSupervisor}
|
||||
>
|
||||
{i18nText('room.joinSupervisor', locale)}
|
||||
</Button>
|
||||
)}
|
||||
{!room?.supervisor && !isCreator && !checkUserApply() && (
|
||||
<div className="card-profile__desc">{i18nText('noData', locale)}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="card-detail__profile">
|
||||
<div className="card-detail__profile_title">
|
||||
<div>{i18nText('room.participants', locale)}</div>
|
||||
<div>{`${room?.clients?.length || 0}/${room?.maxClients}`}</div>
|
||||
</div>
|
||||
{room?.clients && room?.clients?.length > 0 && (
|
||||
<div className="card-detail__profile_list">
|
||||
{room.clients.map(({id, faceImageUrl, name, surname}) => (
|
||||
<div key={id} className="card-detail__profile_item">
|
||||
<div className="card-detail__portrait card-detail__portrait_small">
|
||||
<Image src={faceImageUrl || '/images/user-avatar.png'} width={86}
|
||||
height={86}
|
||||
alt=""/>
|
||||
</div>
|
||||
<div className="card-detail__inner">
|
||||
<div
|
||||
className="card-detail__name">{`${name} ${surname || ''}`}</div>
|
||||
</div>
|
||||
{isCreator && room?.state === SessionState.COACH_APPROVED && activeType === RoomsType.UPCOMING && isTimeBeforeStart && (
|
||||
<LinkButton
|
||||
type="link"
|
||||
style={{alignSelf: 'flex-start'}}
|
||||
danger
|
||||
icon={<DeleteOutlined/>}
|
||||
onClick={() => deleteClient(id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && isCreator && activeType === RoomsType.UPCOMING && (!room?.clients || (room?.clients && room?.clients?.length < room.maxClients)) && (
|
||||
<Button
|
||||
className="card-detail__filled"
|
||||
onClick={() => setShowModal(true)}
|
||||
>
|
||||
{i18nText('room.inviteParticipant', locale)}
|
||||
</Button>
|
||||
)}
|
||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && !isCreator && activeType === RoomsType.UPCOMING && (!room?.clients || (room?.clients && room?.clients?.length < room.maxClients)) && checkUserApply() && (
|
||||
<Button
|
||||
className="card-detail__apply"
|
||||
onClick={becomeClient}
|
||||
>
|
||||
{i18nText('room.joinParticipant', locale)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{room && (
|
||||
<UserListModal
|
||||
locale={locale}
|
||||
jwt={jwt}
|
||||
isOpen={showModal}
|
||||
handleCancel={() => setShowModal(false)}
|
||||
submit={onAddUser}
|
||||
afterCloseModal={() => setForSupervisor(false)}
|
||||
room={room}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="card-detail">
|
||||
<div>
|
||||
<Button
|
||||
className="card-detail__back"
|
||||
type="link"
|
||||
icon={<LeftOutlined/>}
|
||||
onClick={() => setIsEdit(false)}
|
||||
>
|
||||
{i18nText('back', locale)}
|
||||
</Button>
|
||||
</div>
|
||||
<EditRoomForm
|
||||
roomId={room?.id || 0}
|
||||
locale={locale}
|
||||
jwt={jwt}
|
||||
mode="edit"
|
||||
afterSubmit={afterEditing}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,173 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import React, { MouseEvent, useCallback, useEffect, useState } from 'react';
|
||||
import { Empty, Space } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/ru';
|
||||
import 'dayjs/locale/en';
|
||||
import 'dayjs/locale/de';
|
||||
import 'dayjs/locale/it';
|
||||
import 'dayjs/locale/fr';
|
||||
import 'dayjs/locale/es';
|
||||
import { RoomsType } from '../../../types/rooms';
|
||||
import { getRecentRooms, getUpcomingRooms } from '../../../actions/rooms';
|
||||
import { Loader } from '../../view/Loader';
|
||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||
import { AUTH_TOKEN_KEY } from '../../../constants/common';
|
||||
import { usePathname, useRouter } from '../../../navigation';
|
||||
import { i18nText } from '../../../i18nKeys';
|
||||
import { CreateRoom } from './CreateRoom';
|
||||
|
||||
type RoomsTabsProps = {
|
||||
locale: string;
|
||||
activeTab: RoomsType;
|
||||
};
|
||||
|
||||
export const RoomsTabs = ({ locale, activeTab }: RoomsTabsProps) => {
|
||||
const [sort, setSort] = useState<string>();
|
||||
const [rooms, setRooms] = useState<any>();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [errorData, setErrorData] = useState<any>();
|
||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const fetchData = () => {
|
||||
setErrorData(undefined);
|
||||
setLoading(true);
|
||||
Promise.all([
|
||||
getUpcomingRooms(locale, jwt),
|
||||
getRecentRooms(locale, jwt)
|
||||
])
|
||||
.then(([upcoming, recent]) => {
|
||||
setRooms({
|
||||
[RoomsType.UPCOMING]: upcoming || [],
|
||||
[RoomsType.RECENT]: recent || []
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setErrorData(err);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const onChangeSort = useCallback((value: string) => {
|
||||
setSort(value);
|
||||
}, [sort]);
|
||||
|
||||
const onClickSession = (event: MouseEvent<HTMLDivElement>, id: number) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
router.push(`${pathname}/${id}`);
|
||||
};
|
||||
|
||||
const getChildren = (list?: any[]) => (
|
||||
<>
|
||||
{/* <div className="filter-session">
|
||||
<div className="filter-session__item">
|
||||
<CustomSelect
|
||||
label="Topic"
|
||||
value={sort}
|
||||
onChange={onChangeSort}
|
||||
options={[
|
||||
{ value: 'topic1', label: 'Topic 1' },
|
||||
{ value: 'topic2', label: 'Topic 2' },
|
||||
{ value: 'topic3', label: 'Topic 3' },
|
||||
{ value: 'topic4', label: 'Topic 4' }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="list-session">
|
||||
{list && list?.length > 0 ? list?.map(({ id, scheduledStartAtUtc, scheduledEndAtUtc, title, coach, clients, supervisor, maxClients }) => {
|
||||
const startDate = dayjs(scheduledStartAtUtc).locale(locale);
|
||||
const endDate = dayjs(scheduledEndAtUtc).locale(locale);
|
||||
const today = dayjs().format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD');
|
||||
|
||||
return (
|
||||
<div key={id} className="card-profile session__item" onClick={(e: MouseEvent<HTMLDivElement>) => onClickSession(e, id)}>
|
||||
<div className="card-profile__header">
|
||||
<div className="card-profile__header__portrait">
|
||||
<img src={coach?.faceImageUrl || '/images/person.png'} className="" alt="" />
|
||||
</div>
|
||||
<div className="card-profile__header__inner">
|
||||
<div>
|
||||
<div className="card-profile__header__name">{`${coach?.name} ${coach?.surname || ''}`}</div>
|
||||
<div className="card-profile__header__title">{title}</div>
|
||||
<div className={`card-profile__header__date${activeTab === RoomsType.RECENT ? ' history' : (today ? ' chosen' : '')}`}>
|
||||
{today
|
||||
? `${i18nText('today', locale)} ${startDate.format('HH:mm')} - ${endDate.format('HH:mm')}`
|
||||
: `${startDate.format('D MMMM')} ${startDate.format('HH:mm')} - ${endDate.format('HH:mm')}`}
|
||||
</div>
|
||||
<div className="card-room__details">
|
||||
{supervisor && (
|
||||
<>
|
||||
<div>{i18nText('room.supervisor', locale)}</div>
|
||||
<div>{`${supervisor?.name} ${supervisor?.surname || ''}`}</div>
|
||||
</>
|
||||
)}
|
||||
<div>{i18nText('room.members', locale)}</div>
|
||||
<div>{`${clients.length}/${maxClients}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={i18nText('noData', locale)} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
key: RoomsType.UPCOMING,
|
||||
label: (
|
||||
<>
|
||||
{i18nText('room.upcoming', locale)}
|
||||
{rooms?.upcoming && rooms?.upcoming?.length > 0 ? (<span className="count">{rooms?.upcoming.length}</span>) : null}
|
||||
</>
|
||||
),
|
||||
children: getChildren(rooms?.upcoming)
|
||||
},
|
||||
{
|
||||
key: RoomsType.RECENT,
|
||||
label: i18nText('room.recent', locale),
|
||||
children: getChildren(rooms?.recent)
|
||||
},
|
||||
{
|
||||
key: RoomsType.NEW,
|
||||
label: i18nText('room.newRoom', locale),
|
||||
children: <CreateRoom locale={locale} jwt={jwt} />
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<Loader
|
||||
isLoading={loading}
|
||||
errorData={errorData}
|
||||
refresh={fetchData}
|
||||
>
|
||||
<div className="tabs-session">
|
||||
{tabs.map(({ key, label }) => (
|
||||
<Space
|
||||
key={key}
|
||||
className={`tabs-session__item ${key === activeTab ? 'active' : ''}`}
|
||||
onClick={() => router.push(`/account/rooms/${key}`)}
|
||||
>
|
||||
{label}
|
||||
</Space>
|
||||
))}
|
||||
</div>
|
||||
{tabs.filter(({ key }) => key === activeTab)[0].children}
|
||||
</Loader>
|
||||
);
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
'use client'
|
||||
|
||||
export * from './RoomDetails';
|
||||
export * from './RoomsTabs';
|
||||
export * from './RoomDetailsContent';
|
||||
export * from './CreateRoom';
|
|
@ -81,7 +81,7 @@ export const SessionDetailsContent = ({ session, locale, activeType, startSessio
|
|||
const CoachCard = (coach?: PublicUser) => coach ? (
|
||||
<div className="card-detail__expert">
|
||||
<div className="card-detail__portrait">
|
||||
<Image src={coach?.faceImageUrl || '/images/user-avatar.png'} width={140} height={140} alt="" />
|
||||
<Image src={coach?.faceImageUrl || '/images/person.png'} width={140} height={140} alt="" />
|
||||
</div>
|
||||
<div className="card-detail__inner">
|
||||
<Link href={`/experts/${coach?.id}` as any} target="_blank">
|
||||
|
@ -106,7 +106,7 @@ export const SessionDetailsContent = ({ session, locale, activeType, startSessio
|
|||
<div className="card-detail__skills">
|
||||
<div className="skills__list">
|
||||
{session?.themesTags?.slice(0, 2).map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
|
||||
{session?.themesTags && session?.themesTags?.length > 2
|
||||
{session?.themesTags?.length > 2
|
||||
? (
|
||||
<Tag className="skills__list__more">
|
||||
<Link href={`/experts/${coach?.id}` as any} target="_blank">
|
||||
|
@ -128,7 +128,7 @@ export const SessionDetailsContent = ({ session, locale, activeType, startSessio
|
|||
const StudentCard = (student?: PublicUser | null) => student ? (
|
||||
<div className="card-detail__expert">
|
||||
<div className="card-detail__portrait">
|
||||
<Image src={student?.faceImageUrl || '/images/user-avatar.png'} width={140} height={140} alt="" />
|
||||
<Image src={student?.faceImageUrl || '/images/person.png'} width={140} height={140} alt="" />
|
||||
</div>
|
||||
<div className="card-detail__inner">
|
||||
<div className="card-detail__name">{`${student?.name} ${student?.surname || ''}`}</div>
|
||||
|
|
|
@ -14,7 +14,7 @@ import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
|||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
||||
import { getRecentSessions, getRequestedSessions, getUpcomingSessions } from '../../../actions/sessions';
|
||||
import { Session, Sessions, SessionType } from '../../../types/sessions';
|
||||
import { useRouter, usePathname } from '../../../navigation';
|
||||
import { useRouter } from '../../../navigation';
|
||||
import { i18nText } from '../../../i18nKeys';
|
||||
|
||||
type SessionsTabsProps = {
|
||||
|
@ -31,7 +31,6 @@ export const SessionsTabs = ({ locale, activeTab }: SessionsTabsProps) => {
|
|||
const [userData] = useLocalStorage(AUTH_USER, '');
|
||||
const { id: userId = 0 } = userData ? JSON.parse(userData) : {};
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const fetchData = () => {
|
||||
setErrorData(undefined);
|
||||
|
@ -67,7 +66,7 @@ export const SessionsTabs = ({ locale, activeTab }: SessionsTabsProps) => {
|
|||
const onClickSession = (event: MouseEvent<HTMLDivElement>, id: number) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
router.push(`${pathname}/${id}`);
|
||||
router.push(`${id}`);
|
||||
};
|
||||
|
||||
const getChildren = (list?: Session[]) => (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client'
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Alert, message } from 'antd';
|
||||
import {Alert, message} from 'antd';
|
||||
import Image from 'next/image';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { ExpertData, PayInfo, ProfileData } from '../../types/profile';
|
||||
|
|
|
@ -55,6 +55,8 @@ export const ExpertsAdditionalFilter = ({
|
|||
};
|
||||
const search = getSearchParamsString(newFilter);
|
||||
|
||||
console.log('here1');
|
||||
|
||||
router.push(search ? `${basePath}?${search}#filter` : `${basePath}#filter`);
|
||||
|
||||
// router.push({
|
||||
|
|
|
@ -1,28 +1,23 @@
|
|||
'use client';
|
||||
|
||||
import React, { FC, useState, useEffect } from 'react';
|
||||
import React, {FC, useEffect, useState} from 'react';
|
||||
import Image from 'next/image';
|
||||
import { Tag, Image as AntdImage, Space, Button } from 'antd';
|
||||
import { Tag, Image as AntdImage, Space } from 'antd';
|
||||
import { ZoomInOutlined, ZoomOutOutlined, StarFilled } from '@ant-design/icons';
|
||||
import { SignupSessionData } from '../../types/experts';
|
||||
import { ExpertDetails, Practice, ThemeGroup } from '../../types/experts';
|
||||
import { ExpertDocument } from '../../types/file';
|
||||
import { Locale } from '../../types/locale';
|
||||
import { CustomRate } from '../view/CustomRate';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { FilledYellowButton } from '../view/FilledButton';
|
||||
import { getStorageValue } from '../../hooks/useLocalStorage';
|
||||
import { AUTH_TOKEN_KEY, SESSION_DATA } from '../../constants/common';
|
||||
import { ScheduleModal } from '../Modals/ScheduleModal';
|
||||
import { ScheduleModalResult } from '../Modals/ScheduleModalResult';
|
||||
import SignalrConnection from "../../lib/signalr-connection";
|
||||
import {useRouter} from "../../navigation";
|
||||
import {useLocalStorage} from "../../hooks/useLocalStorage";
|
||||
import {AUTH_TOKEN_KEY} from "../../constants/common";
|
||||
|
||||
type ExpertDetailsProps = {
|
||||
expert: ExpertDetails;
|
||||
locale?: string;
|
||||
expertId?: string;
|
||||
};
|
||||
|
||||
type ExpertPracticeProps = {
|
||||
|
@ -31,13 +26,48 @@ type ExpertPracticeProps = {
|
|||
locale?: string;
|
||||
};
|
||||
|
||||
export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale, expertId }) => {
|
||||
export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale }) => {
|
||||
const { publicCoachDetails } = expert || {};
|
||||
const [showSchedulerModal, setShowSchedulerModal] = useState<boolean>(false);
|
||||
const [mode, setMode] = useState<'data' | 'time' | 'pay' | 'finish'>('data');
|
||||
const isRus = locale === Locale.ru;
|
||||
|
||||
return (
|
||||
<div className="expert-card">
|
||||
<div className="expert-card__wrap">
|
||||
<div className="expert-card__avatar">
|
||||
<Image src={publicCoachDetails?.faceImageUrl || '/images/person.png'} width={216} height={216} alt="" />
|
||||
</div>
|
||||
<div className="expert-card__inner">
|
||||
<h1 className="expert-card__title">{`${publicCoachDetails?.name} ${publicCoachDetails?.surname || ''}`}</h1>
|
||||
<div className="expert-card__info">
|
||||
<span>{`${publicCoachDetails?.practiceHours} ${i18nText('practiceHours', locale)}`}</span>
|
||||
<i>|</i>
|
||||
<span>{`${publicCoachDetails?.supervisionPerYearId} ${i18nText('supervisionCount', locale)}`}</span>
|
||||
</div>
|
||||
<div className="expert-card__rating">
|
||||
<CustomRate defaultValue={4} character={<StarFilled style={{ fontSize: 32 }} />} disabled />
|
||||
<span>{`4/5 (${i18nText('outOf', locale)} 345)`}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="expert-card__wrap-btn">
|
||||
<a href="#" className="btn-apply">
|
||||
<img src="/images/calendar-outline.svg" className="" alt="" />
|
||||
{i18nText('schedule', locale)}
|
||||
</a>
|
||||
{/*
|
||||
<a href="#" className="btn-video">
|
||||
<img src="/images/videocam-outline.svg" className="" alt=""/>
|
||||
Video
|
||||
</a>
|
||||
*/}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExpertInformation: FC<ExpertDetailsProps> = ({ expert, locale }) => {
|
||||
const { publicCoachDetails: { tags = [], sessionCost = 0, sessionDuration = 0, coachLanguages = [] , id, botUserId} } = expert || {};
|
||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
const isRus = locale === Locale.ru;
|
||||
const { joinChatPerson, addListener } = SignalrConnection();
|
||||
const router = useRouter();
|
||||
|
||||
|
@ -65,70 +95,8 @@ export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale, expertId })
|
|||
})
|
||||
}
|
||||
|
||||
const checkSession = (data?: SignupSessionData) => {
|
||||
if (data?.startAtUtc && data?.tagId) {
|
||||
const jwt = getStorageValue(AUTH_TOKEN_KEY, '');
|
||||
sessionStorage?.setItem(SESSION_DATA, JSON.stringify(data));
|
||||
if (jwt) {
|
||||
setMode('pay');
|
||||
} else {
|
||||
setShowSchedulerModal(false);
|
||||
const showAuth = new Event('show_auth_enter');
|
||||
document.dispatchEvent(showAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleShowPayForm = () => {
|
||||
setShowSchedulerModal(true);
|
||||
setMode('pay');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('show_pay_form', handleShowPayForm);
|
||||
return () => {
|
||||
document.removeEventListener('show_pay_form', handleShowPayForm);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onSchedulerHandle = () => {
|
||||
setMode('data');
|
||||
setShowSchedulerModal(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="expert-card">
|
||||
<div className="expert-card__wrap">
|
||||
<div className="expert-card__avatar">
|
||||
<Image src={publicCoachDetails?.faceImageUrl || '/images/person.png'} width={216} height={216} alt="" />
|
||||
</div>
|
||||
<div className="expert-card__inner">
|
||||
<h1 className="expert-card__title">{`${publicCoachDetails?.name} ${publicCoachDetails?.surname || ''}`}</h1>
|
||||
<div className="expert-card__info">
|
||||
<span>{`${publicCoachDetails?.practiceHours} ${i18nText('practiceHours', locale)}`}</span>
|
||||
<i>|</i>
|
||||
<span>{`${publicCoachDetails?.supervisionPerYearId} ${i18nText('supervisionCount', locale)}`}</span>
|
||||
</div>
|
||||
<div className="expert-card__rating">
|
||||
<CustomRate defaultValue={4} character={<StarFilled style={{ fontSize: 32 }} />} disabled />
|
||||
<span>{`4/5 (${i18nText('outOf', locale)} 345)`}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="expert-card__wrap-btn">
|
||||
<Button className="btn-apply" onClick={onSchedulerHandle}>
|
||||
<img src="/images/calendar-outline.svg" className="" alt="" />
|
||||
{i18nText('schedule', locale)}
|
||||
</Button>
|
||||
{/*
|
||||
<a href="#" className="btn-video">
|
||||
<img src="/images/videocam-outline.svg" className="" alt=""/>
|
||||
Video
|
||||
</a>
|
||||
*/}
|
||||
</div>
|
||||
</div>
|
||||
<div className="expert-info">
|
||||
{/* <h2 className="title-h2">{}</h2> */}
|
||||
<div className="skills__list">
|
||||
|
@ -154,7 +122,7 @@ export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale, expertId })
|
|||
{tags?.map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
|
||||
</div>
|
||||
<div className="wrap-btn-prise">
|
||||
<FilledYellowButton onClick={onSchedulerHandle}>{i18nText('signUp', locale)}</FilledYellowButton>
|
||||
<FilledYellowButton onClick={() => console.log('schedule')}>{i18nText('signUp', locale)}</FilledYellowButton>
|
||||
<div className="wrap-btn-prise__text">
|
||||
{`${sessionCost}€`} <span>/ {`${sessionDuration}${isRus ? 'мин' : 'min'}`}</span>
|
||||
</div>
|
||||
|
@ -172,26 +140,15 @@ export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale, expertId })
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ScheduleModal
|
||||
open={showSchedulerModal}
|
||||
handleCancel={() => setShowSchedulerModal(false)}
|
||||
updateMode={setMode}
|
||||
mode={mode}
|
||||
expertId={expertId as string}
|
||||
locale={locale as string}
|
||||
sessionCost={sessionCost}
|
||||
checkSession={checkSession}
|
||||
/>
|
||||
<ScheduleModalResult locale={locale as string} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExpertPractice: FC<ExpertPracticeProps> = ({ themes = [], cases = [], locale }) => {
|
||||
export const ExpertPractice: FC<ExpertPracticeProps> = ({themes = [], cases = [], locale}) => {
|
||||
return cases?.length > 0 ? (
|
||||
<div>
|
||||
<h3 className="title-h3">{i18nText('successfulCase', locale)}</h3>
|
||||
{cases?.map(({ id, description, themesGroupIds }) => {
|
||||
{cases?.map(({id, description, themesGroupIds}) => {
|
||||
const filtered = themes ? themes.filter(({ id }) => themesGroupIds?.includes(+id)) : [];
|
||||
|
||||
return (
|
||||
|
|
|
@ -114,6 +114,7 @@ export const ExpertsFilter = ({
|
|||
...getObjectByAdditionalFilter(searchParams)
|
||||
};
|
||||
const search = getSearchParamsString(newFilter);
|
||||
console.log('basePath', basePath);
|
||||
|
||||
router.push(search ? `${basePath}?${search}#filter` : `${basePath}#filter`);
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
'use client';
|
||||
|
||||
import React, { FC, useState } from 'react';
|
||||
import { Modal, Button, message, Form, Collapse } from 'antd';
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import {Modal, Button, message, Form, Collapse, GetProp, UploadProps} from 'antd';
|
||||
import type { CollapseProps } from 'antd';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { PracticePersonData } from '../../types/practice';
|
||||
import { PracticePersonData, PracticeDTO, PracticeData, PracticeCase } from '../../types/practice';
|
||||
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||
import { setEducation } from '../../actions/profile';
|
||||
import { EducationData, EducationDTO } from '../../types/education';
|
||||
import { CertificatesContent } from './educationModalContent/Certificates';
|
||||
import { EducationsContent } from './educationModalContent/Educations';
|
||||
import { TrainingsContent } from './educationModalContent/Trainings';
|
||||
import { MbasContent } from './educationModalContent/Mbas';
|
||||
import { ExperiencesContent } from './educationModalContent/Experiences';
|
||||
import {setEducation} from '../../actions/profile';
|
||||
import {Certificate, Details, EducationData, EducationDTO, Experience} from "../../types/education";
|
||||
import {CertificatesContent} from "./educationModalContent/Certificates";
|
||||
import {EducationsContent} from "./educationModalContent/Educations";
|
||||
import {TrainingsContent} from "./educationModalContent/Trainings";
|
||||
import {MbasContent} from "./educationModalContent/Mbas";
|
||||
import {ExperiencesContent} from "./educationModalContent/Experiences";
|
||||
|
||||
type EditExpertEducationModalProps = {
|
||||
open: boolean;
|
||||
|
|
|
@ -1,286 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import React, {FC, useEffect, useState} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Modal, Menu, Calendar, Radio, Button, Input, message, Form } from 'antd';
|
||||
import type { CalendarProps, MenuProps } from 'antd';
|
||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import 'dayjs/locale/ru';
|
||||
import 'dayjs/locale/en';
|
||||
import 'dayjs/locale/de';
|
||||
import 'dayjs/locale/it';
|
||||
import 'dayjs/locale/fr';
|
||||
import 'dayjs/locale/es';
|
||||
import { getLocale } from '../../utils/locale';
|
||||
import { AUTH_TOKEN_KEY, SESSION_DATA } from '../../constants/common';
|
||||
import { ExpertScheduler, SignupSessionData } from '../../types/experts';
|
||||
import { Tag } from '../../types/tags';
|
||||
import { getSchedulerByExpertId, getSchedulerSession } from '../../actions/experts';
|
||||
import { StripeElementsForm } from '../stripe/StripeElementsForm';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { CustomSelect } from '../../components/view/CustomSelect';
|
||||
import { Loader } from '../view/Loader';
|
||||
import { getStorageValue } from '../../hooks/useLocalStorage';
|
||||
|
||||
type ScheduleModalProps = {
|
||||
open: boolean;
|
||||
handleCancel: () => void;
|
||||
mode: 'data' | 'time' | 'pay' | 'finish';
|
||||
updateMode: (mode: 'data' | 'time' | 'pay' | 'finish') => void;
|
||||
sessionCost: number;
|
||||
expertId: string;
|
||||
locale: string;
|
||||
checkSession: (data?: SignupSessionData) => void;
|
||||
};
|
||||
|
||||
type MenuItem = Required<MenuProps>['items'][number];
|
||||
|
||||
const getCalendarMenu = (start: Dayjs): MenuItem[] => Array.from({ length: 3 })
|
||||
.map((_: unknown, index: number) => {
|
||||
const date = index ? start.add(index, 'M') : start.clone();
|
||||
return {
|
||||
label: <span className="b-calendar-month">{date.format('MMMM')}</span>,
|
||||
key: date.format('YYYY-MM-DD')
|
||||
}
|
||||
});
|
||||
|
||||
export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||||
open,
|
||||
handleCancel,
|
||||
mode,
|
||||
updateMode,
|
||||
sessionCost,
|
||||
locale,
|
||||
expertId,
|
||||
checkSession,
|
||||
}) => {
|
||||
const [selectDate, setSelectDate] = useState<Dayjs>(dayjs());
|
||||
const [dates, setDates] = useState<Record<string, { startTime: string, endTime: string }[]> | undefined>();
|
||||
const [tags, setTags] = useState<Tag[] | undefined>();
|
||||
const [rawScheduler, setRawScheduler] = useState<ExpertScheduler | null>(null);
|
||||
const [isPayLoading, setIsPayLoading] = useState<boolean>(false);
|
||||
const [sessionId, setSessionId] = useState<string>('');
|
||||
const [form] = Form.useForm<{ clientComment?: string, startAtUtc?: string, tagId?: number }>();
|
||||
|
||||
dayjs.locale(locale);
|
||||
|
||||
const signupSession = () => {
|
||||
const data = sessionStorage?.getItem(SESSION_DATA);
|
||||
const jwt = getStorageValue(AUTH_TOKEN_KEY, '');
|
||||
|
||||
if (jwt && data) {
|
||||
const parseData = JSON.parse(data);
|
||||
setIsPayLoading(true);
|
||||
getSchedulerSession(parseData as SignupSessionData, locale || 'en', jwt)
|
||||
.then((session) => {
|
||||
setSessionId(session?.sessionId);
|
||||
console.log(session?.sessionId);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
message.error('Не удалось провести оплату')
|
||||
})
|
||||
.finally(() => {
|
||||
sessionStorage?.removeItem(SESSION_DATA);
|
||||
setIsPayLoading(false);
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(()=> {
|
||||
if (open && mode !== 'pay') {
|
||||
getSchedulerByExpertId(expertId as string, locale as string)
|
||||
.then((data) => {
|
||||
setRawScheduler(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
form.resetFields();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && mode === 'pay') {
|
||||
signupSession();
|
||||
}
|
||||
}, [mode]);
|
||||
|
||||
useEffect(() => {
|
||||
const map = {} as any
|
||||
rawScheduler?.availableSlots.forEach((el) => {
|
||||
const key = dayjs(el.startTime).format('YYYY-MM-DD');
|
||||
if (!map[key]){
|
||||
map[key] = []
|
||||
}
|
||||
map[key].push(el);
|
||||
})
|
||||
setDates(map);
|
||||
setTags(rawScheduler?.tags)
|
||||
}, [rawScheduler]);
|
||||
|
||||
const onPanelChange = (value: Dayjs) => setSelectDate(value);
|
||||
|
||||
const onDateChange: CalendarProps<Dayjs>['onSelect'] = (value, selectInfo) => {
|
||||
if (selectInfo.source === 'date') {
|
||||
setSelectDate(value);
|
||||
updateMode('time');
|
||||
}
|
||||
};
|
||||
|
||||
const disabledDate = (currentDate: Dayjs) => !dates || !dates[currentDate.format('YYYY-MM-DD')];
|
||||
|
||||
const cellRender: CalendarProps<Dayjs>['fullCellRender'] = (date, info) => {
|
||||
const isWeekend = date.day() === 6 || date.day() === 0;
|
||||
return React.cloneElement(info.originNode, {
|
||||
...info.originNode.props,
|
||||
className: classNames('b-calendar-cell', {
|
||||
['b-calendar-cell__select']: selectDate.isSame(date, 'date'),
|
||||
['b-calendar-cell__today']: date.isSame(dayjs(), 'date'),
|
||||
['b-calendar-cell__weekend']: isWeekend,
|
||||
}),
|
||||
children: (
|
||||
<div>
|
||||
<span>
|
||||
{date.get('date')}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const onValidate = () => {
|
||||
form.validateFields()
|
||||
.then((values) => {
|
||||
checkSession({ coachId: +expertId, ...values });
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="b-modal"
|
||||
open={open}
|
||||
title={undefined}
|
||||
onOk={undefined}
|
||||
onCancel={handleCancel}
|
||||
footer={false}
|
||||
width={498}
|
||||
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||||
>
|
||||
{mode === 'data' && (
|
||||
<Calendar
|
||||
className="b-calendar"
|
||||
fullscreen={false}
|
||||
onPanelChange={onPanelChange}
|
||||
fullCellRender={cellRender}
|
||||
onSelect={onDateChange}
|
||||
value={selectDate}
|
||||
disabledDate={disabledDate}
|
||||
locale={getLocale(locale)}
|
||||
validRange={[selectDate.startOf('M'), selectDate.endOf('M')]}
|
||||
headerRender={({ onChange }) => {
|
||||
const start = dayjs().startOf('M');
|
||||
const [activeMonth, setActiveMonth] = useState<string>(start.format('YYYY-MM-DD'));
|
||||
|
||||
const onClick: MenuProps['onClick'] = (e) => {
|
||||
setActiveMonth(e.key);
|
||||
onChange(dayjs(e.key));
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu
|
||||
className="b-calendar-header"
|
||||
onClick={onClick}
|
||||
selectedKeys={[activeMonth]}
|
||||
mode="horizontal"
|
||||
items={getCalendarMenu(start)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{mode === 'time' && (
|
||||
<div className="b-schedule-time">
|
||||
<div className="b-schedule-time-header">
|
||||
<Button
|
||||
className="b-button-link-big"
|
||||
type="link"
|
||||
onClick={() => updateMode('data')}
|
||||
icon={<ArrowLeftOutlined />}
|
||||
iconPosition="start"
|
||||
>
|
||||
{selectDate.locale(locale).format('DD MMMM YYYY')}
|
||||
</Button>
|
||||
</div>
|
||||
<Form form={form}>
|
||||
<div className="b-schedule-select-tag">
|
||||
{tags && (
|
||||
<Form.Item
|
||||
name="tagId"
|
||||
rules={[{
|
||||
required: true,
|
||||
message: ''
|
||||
}]}
|
||||
>
|
||||
<CustomSelect
|
||||
label={i18nText('selectTopic', locale)}
|
||||
options={tags?.map(({id, name}) => ({value: id, label: name}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
</div>
|
||||
<div className="b-schedule-radio-list">
|
||||
<Form.Item
|
||||
name="startAtUtc"
|
||||
rules={[{
|
||||
required: true,
|
||||
message: ''
|
||||
}]}
|
||||
>
|
||||
<Radio.Group>
|
||||
{dates && dates[selectDate.format('YYYY-MM-DD')].map((el: any) => (
|
||||
<Radio
|
||||
key={el.startTime}
|
||||
value={el.startTime}
|
||||
>
|
||||
{dayjs(el.startTime).format('HH:mm')} - {dayjs(el.endTime).format('HH:mm')}
|
||||
</Radio>)
|
||||
)}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item name="clientComment">
|
||||
<Input.TextArea
|
||||
className="b-textarea"
|
||||
rows={2}
|
||||
placeholder={i18nText('sessionWishes', locale)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Button
|
||||
className="btn-apply"
|
||||
onClick={onValidate}
|
||||
>
|
||||
{i18nText('pay', locale)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{mode === 'pay' && (
|
||||
<div className="b-schedule-payment">
|
||||
<Loader isLoading={isPayLoading}>
|
||||
<StripeElementsForm
|
||||
amount={sessionCost}
|
||||
locale={locale}
|
||||
sessionId={sessionId}
|
||||
/>
|
||||
</Loader>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -1,73 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Result } from 'antd';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { useSearchParams, useRouter } from 'next/navigation';
|
||||
import { Stripe } from 'stripe';
|
||||
import { getStripePaymentStatus } from '../../actions/stripe';
|
||||
import { sessionPaymentConfirm } from '../../actions/sessions';
|
||||
import { getStorageValue } from '../../hooks/useLocalStorage';
|
||||
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
||||
import { Session, SessionState } from '../../types/sessions';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
|
||||
export const ScheduleModalResult = ({ locale }: { locale: string }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const [paymentStatus, setPaymentStatus] = useState<Stripe.PaymentIntent.Status | undefined>();
|
||||
const [session, setSession] = useState<Session | undefined>();
|
||||
const [error, setError] = useState<any>();
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
setError(undefined);
|
||||
const payment_intent = searchParams.get('payment_intent') || false;
|
||||
if (payment_intent) {
|
||||
getStripePaymentStatus(payment_intent)
|
||||
.then((result) => {
|
||||
setPaymentStatus(result?.status);
|
||||
if (result?.status === 'succeeded' && result?.metadata?.sessionId) {
|
||||
const jwt = getStorageValue(AUTH_TOKEN_KEY, '');
|
||||
sessionPaymentConfirm(locale, jwt, +result.metadata.sessionId)
|
||||
.then((session) => {
|
||||
setSession(session);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setError(err);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setError(err);
|
||||
})
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
const onClose = () => {
|
||||
const { origin, pathname } = window?.location || {};
|
||||
|
||||
router.push(`${origin}${pathname}`);
|
||||
setPaymentStatus(undefined);
|
||||
setSession(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="b-modal"
|
||||
open={paymentStatus === 'succeeded' && session?.state === SessionState.PAID}
|
||||
title={undefined}
|
||||
onOk={undefined}
|
||||
onCancel={onClose}
|
||||
footer={false}
|
||||
width={498}
|
||||
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||||
>
|
||||
<div className="b-schedule-payment-result">
|
||||
<Result
|
||||
status="success"
|
||||
title={i18nText('successPayment', locale)}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import React, { FC, useEffect, useRef, useState } from 'react';
|
||||
import { Modal, Button, message, Input } from 'antd';
|
||||
import { CloseOutlined, StarFilled } from '@ant-design/icons';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
||||
import { getReport, saveReport } from '../../actions/rooms';
|
||||
import { Report, ReportData } from '../../types/rooms';
|
||||
import { CustomRate } from '../view/CustomRate';
|
||||
|
||||
type SupervisorReportModalProps = {
|
||||
open: boolean;
|
||||
handleCancel: () => void;
|
||||
locale: string;
|
||||
refresh: () => void;
|
||||
roomId: number;
|
||||
};
|
||||
|
||||
export const SupervisorReportModal: FC<SupervisorReportModalProps> = ({
|
||||
open,
|
||||
handleCancel,
|
||||
locale,
|
||||
roomId,
|
||||
refresh
|
||||
}) => {
|
||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [report, setReport] = useState<Report[] | undefined>();
|
||||
const reasonRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getReport(locale, jwt, roomId)
|
||||
.then((data) => {
|
||||
setReport(data);
|
||||
})
|
||||
.catch(() => {
|
||||
message.error('Не удалось получить отчет');
|
||||
})
|
||||
}, []);
|
||||
|
||||
const onSaveRate = () => {
|
||||
const result: ReportData = {
|
||||
sessionId: roomId,
|
||||
sessionSupervisorScores: report || [],
|
||||
supervisorComment: reasonRef?.current?.resizableTextArea?.textArea?.value || ''
|
||||
};
|
||||
|
||||
setLoading(true);
|
||||
saveReport(locale, jwt, result)
|
||||
.then(() => {
|
||||
handleCancel();
|
||||
refresh();
|
||||
})
|
||||
.catch(() => {
|
||||
message.error('Не удалось сохранить отчет');
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
}
|
||||
|
||||
const onChangeRate = (val: number, id: number) => {
|
||||
setReport(report ? report.map((item) => {
|
||||
if (item.evaluationCriteriaId === id) {
|
||||
return {
|
||||
...item,
|
||||
score: val
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
}) : undefined);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="b-modal"
|
||||
open={open}
|
||||
title={undefined}
|
||||
onOk={undefined}
|
||||
onCancel={handleCancel}
|
||||
footer={false}
|
||||
width={498}
|
||||
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||||
>
|
||||
<div className="b-modal__report__content">
|
||||
<div className="b-rate-list">
|
||||
{report && report.length > 0 && report.map(({ key, evaluationCriteriaId, score }) => (
|
||||
<div key={evaluationCriteriaId} className="b-rate-list__item">
|
||||
<div className="b-rate-list__item_title">{i18nText(`room.rating_${key?.toLowerCase()}`, locale)}</div>
|
||||
<CustomRate
|
||||
defaultValue={score || 0}
|
||||
character={<StarFilled style={{ fontSize: 32 }} />}
|
||||
onChange={(val: number) => onChangeRate(val, evaluationCriteriaId)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<Input.TextArea
|
||||
ref={reasonRef}
|
||||
className="b-textarea"
|
||||
rows={1}
|
||||
placeholder={i18nText('room.tellAboutReason', locale)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
className="btn-apply"
|
||||
onClick={onSaveRate}
|
||||
loading={loading}
|
||||
>
|
||||
{i18nText('room.rate', locale)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -1,113 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Button, Modal, notification } from 'antd';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import debounce from 'lodash/debounce';
|
||||
import Image from 'next/image';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { getUsersList } from '../../actions/rooms';
|
||||
import { PublicUser } from '../../types/sessions';
|
||||
import { Room } from '../../types/rooms';
|
||||
import { CustomInput } from '../view/CustomInput';
|
||||
import { Loader } from '../view/Loader';
|
||||
|
||||
type UserListModalProps = {
|
||||
room: Room;
|
||||
isOpen: boolean;
|
||||
locale: string;
|
||||
handleCancel: () => void;
|
||||
jwt: string;
|
||||
submit: (id: number) => void;
|
||||
afterCloseModal?: () => void;
|
||||
};
|
||||
|
||||
export const UserListModal = ({ room, isOpen, locale, handleCancel, jwt, submit, afterCloseModal }: UserListModalProps) => {
|
||||
const [users, setUsers] = useState<PublicUser[] | undefined>();
|
||||
const [loading, seLoading] = useState<boolean>(false);
|
||||
|
||||
const onSearch = useCallback(debounce((e: any) => {
|
||||
if (e?.target?.value) {
|
||||
seLoading(true);
|
||||
getUsersList(locale, jwt, { template: e.target.value })
|
||||
.then(({ items }) => {
|
||||
const clients = room?.clients?.map(({ id }) => id);
|
||||
setUsers(items
|
||||
? items.filter(({ id }) => !(clients?.length && clients.includes(id) || id === room?.supervisor?.id || id === room?.coach?.id))
|
||||
: undefined);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
notification.error({
|
||||
message: 'Error',
|
||||
description: err?.response?.data?.errMessage
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
seLoading(false);
|
||||
});
|
||||
} else {
|
||||
setUsers(undefined);
|
||||
}
|
||||
|
||||
}, 300), []);
|
||||
|
||||
const onAfterClose = () => {
|
||||
setUsers(undefined);
|
||||
if (afterCloseModal) afterCloseModal();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="b-modal"
|
||||
open={isOpen}
|
||||
title={undefined}
|
||||
onOk={undefined}
|
||||
onCancel={handleCancel}
|
||||
footer={false}
|
||||
width={498}
|
||||
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||||
afterClose={onAfterClose}
|
||||
>
|
||||
<div className="b-modal__users-list__content">
|
||||
<CustomInput
|
||||
placeholder={i18nText('search', locale)}
|
||||
onChange={onSearch}
|
||||
allowClear
|
||||
/>
|
||||
{users && (
|
||||
<div className="b-users-list__wrapper">
|
||||
<Loader isLoading={loading}>
|
||||
{users.length > 0 ? (
|
||||
<div className="b-users-list">
|
||||
{users.map(({ id, name, surname, faceImageUrl }) => (
|
||||
<div className="b-users-list-item" key={id}>
|
||||
<div>
|
||||
<div className="card-detail__portrait card-detail__portrait_small">
|
||||
<Image src={faceImageUrl || '/images/user-avatar.png'} width={86}
|
||||
height={86}
|
||||
alt=""/>
|
||||
</div>
|
||||
<div className="card-detail__inner">
|
||||
<div
|
||||
className="card-detail__name">{`${name} ${surname || ''}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
className="card-detail__filled"
|
||||
onClick={() => submit(id)}
|
||||
>
|
||||
{i18nText('room.invite', locale)}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="b-users-list__empty">{i18nText('noData', locale)}</div>
|
||||
)}
|
||||
</Loader>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
|
@ -6,7 +6,7 @@ import { AUTH_USER } from '../../../constants/common';
|
|||
import { SocialConfig } from '../../../constants/social';
|
||||
import { useOauthWindow } from '../../../hooks/useOauthWindow';
|
||||
import { getAuth } from '../../../actions/auth';
|
||||
import {getPersonalData, getUserData} from '../../../actions/profile';
|
||||
import { getPersonalData } from '../../../actions/profile';
|
||||
import { CustomInput } from '../../view/CustomInput';
|
||||
import { CustomInputPassword } from '../../view/CustomInputPassword';
|
||||
import { FilledButton } from '../../view/FilledButton';
|
||||
|
@ -39,7 +39,7 @@ export const EnterContent: FC<EnterProps> = ({
|
|||
getAuth(locale, { login, password })
|
||||
.then((data) => {
|
||||
if (data.jwtToken) {
|
||||
getUserData(locale, data.jwtToken)
|
||||
getPersonalData(locale, data.jwtToken)
|
||||
.then((profile) => {
|
||||
localStorage.setItem(AUTH_USER, JSON.stringify(profile));
|
||||
updateToken(data.jwtToken);
|
||||
|
|
|
@ -23,31 +23,6 @@ function HeaderAuthLinks ({
|
|||
const selectedLayoutSegment = useSelectedLayoutSegment();
|
||||
const pathname = selectedLayoutSegment || '';
|
||||
const [token, setToken] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
const [isPayPath, setIsPayPath] = useState<boolean>(false);
|
||||
|
||||
const onOpen = (mode: 'enter' | 'register' | 'reset' | 'finish') => {
|
||||
setMode(mode);
|
||||
setIsOpenModal(true);
|
||||
};
|
||||
|
||||
const handleAuthRegister = () => {
|
||||
setIsPayPath(true);
|
||||
onOpen('register');
|
||||
};
|
||||
|
||||
const handleAuthEnter = () => {
|
||||
setIsPayPath(true);
|
||||
onOpen('enter');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('show_auth_register', handleAuthRegister);
|
||||
document.addEventListener('show_auth_enter', handleAuthEnter);
|
||||
return () => {
|
||||
document.removeEventListener('show_auth_register', handleAuthRegister);
|
||||
document.removeEventListener('show_auth_enter', handleAuthEnter);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpenModal) {
|
||||
|
@ -55,16 +30,9 @@ function HeaderAuthLinks ({
|
|||
}
|
||||
}, [isOpenModal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (token && isPayPath) {
|
||||
const showPayForm = new Event('show_pay_form');
|
||||
document.dispatchEvent(showPayForm);
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
const addNewEvent = (name: 'show_auth_register' | 'show_auth_enter') => {
|
||||
const evt = new Event(name);
|
||||
document.dispatchEvent(evt);
|
||||
const onOpen = (mode: 'enter' | 'register' | 'reset' | 'finish') => {
|
||||
setMode(mode);
|
||||
setIsOpenModal(true);
|
||||
};
|
||||
|
||||
return token
|
||||
|
@ -81,7 +49,7 @@ function HeaderAuthLinks ({
|
|||
<Button
|
||||
className="b-header__auth"
|
||||
type="link"
|
||||
onClick={() => addNewEvent('show_auth_register')}
|
||||
onClick={() => onOpen('register')}
|
||||
>
|
||||
{i18nText('registration', locale)}
|
||||
</Button>
|
||||
|
@ -93,7 +61,7 @@ function HeaderAuthLinks ({
|
|||
<Button
|
||||
className="b-header__auth"
|
||||
type="link"
|
||||
onClick={() => addNewEvent('show_auth_enter')}
|
||||
onClick={() => onOpen('enter')}
|
||||
>
|
||||
{i18nText('enter', locale)}
|
||||
</Button>
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import type { StripeError } from '@stripe/stripe-js';
|
||||
import {
|
||||
useStripe,
|
||||
useElements,
|
||||
PaymentElement,
|
||||
Elements,
|
||||
} from '@stripe/react-stripe-js';
|
||||
import { Form, Button, message } from 'antd';
|
||||
import getStripe from '../../utils/get-stripe';
|
||||
import { createPaymentIntent} from '../../actions/stripe';
|
||||
import { Payment } from '../../types/payment';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { WithError } from '../view/WithError';
|
||||
|
||||
type PaymentFormProps = {
|
||||
amount: number,
|
||||
sessionId?: string,
|
||||
locale: string
|
||||
}
|
||||
|
||||
type PaymentInfo = 'initial' | 'error' | 'processing' | 'requires_payment_method' | 'requires_confirmation' | 'requires_action' | 'succeeded';
|
||||
|
||||
const PaymentStatus = ({ status }: { status?: PaymentInfo }) => {
|
||||
switch (status) {
|
||||
case 'processing':
|
||||
case 'requires_payment_method':
|
||||
case 'requires_confirmation':
|
||||
return <h2>Processing...</h2>;
|
||||
|
||||
case 'requires_action':
|
||||
return <h2>Authenticating...</h2>;
|
||||
|
||||
case 'succeeded':
|
||||
return <h2>Payment Succeeded</h2>;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const CheckoutForm: FC<PaymentFormProps> = ({ amount, sessionId, locale }) => {
|
||||
const [form] = Form.useForm<Payment>();
|
||||
const formAmount = Form.useWatch('amount', form);
|
||||
const [paymentType, setPaymentType] = useState<string>('');
|
||||
const [payment, setPayment] = useState<{
|
||||
status: PaymentInfo
|
||||
}>({ status: 'initial' });
|
||||
const [errorData, setErrorData] = useState<any>();
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
|
||||
useEffect(() => {
|
||||
elements?.update({ amount: formAmount * 100 });
|
||||
}, [formAmount]);
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
if (!elements || !stripe) return;
|
||||
|
||||
setErrorData(undefined);
|
||||
setPayment({ status: "processing" });
|
||||
|
||||
const { error: submitError } = await elements.submit();
|
||||
|
||||
if (submitError) {
|
||||
if (submitError.message) {
|
||||
message.error(submitError.message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { client_secret: clientSecret } = await createPaymentIntent(
|
||||
{ amount, sessionId }
|
||||
);
|
||||
|
||||
const { error: confirmError } = await stripe!.confirmPayment({
|
||||
elements,
|
||||
clientSecret,
|
||||
confirmParams: {
|
||||
return_url: window.location.href,
|
||||
payment_method_data: {
|
||||
allow_redisplay: 'limited',
|
||||
// billing_details: {
|
||||
// name: input.cardholderName,
|
||||
// },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (confirmError) {
|
||||
setErrorData({
|
||||
title: i18nText('errorPayment', locale),
|
||||
message: confirmError.message ?? 'An unknown error occurred'
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
const { message } = err as StripeError;
|
||||
setErrorData({
|
||||
title: i18nText('errorPayment', locale),
|
||||
message: message ?? 'An unknown error occurred'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<WithError errorData={errorData}>
|
||||
<Form form={form} onFinish={onSubmit} style={{ display: 'flex', overflow: 'hidden', flexDirection: 'column', gap: 16, justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<PaymentElement
|
||||
onChange={(e) => {
|
||||
setPaymentType(e.value.type);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<PaymentStatus status={payment.status}/>
|
||||
</div>
|
||||
<Button
|
||||
className="btn-apply"
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
!["initial", "succeeded", "error"].includes(payment.status) ||
|
||||
!stripe
|
||||
}
|
||||
>
|
||||
{`${i18nText('pay', locale)} ${amount}€`}
|
||||
</Button>
|
||||
</Form>
|
||||
</WithError>
|
||||
);
|
||||
}
|
||||
|
||||
export const StripeElementsForm: FC<PaymentFormProps> = ({ amount, sessionId, locale }) => {
|
||||
return (
|
||||
<Elements
|
||||
stripe={getStripe()}
|
||||
options={{
|
||||
fonts: [{
|
||||
cssSrc: 'https://fonts.googleapis.com/css2?family=Comfortaa&display=swap',
|
||||
}],
|
||||
appearance: {
|
||||
variables: {
|
||||
colorIcon: '#2c7873',
|
||||
fontSizeBase: '16px',
|
||||
colorPrimary: '#66A5AD',
|
||||
colorBackground: '#F8F8F7',
|
||||
colorText: '#000',
|
||||
colorDanger: '#ff4d4f',
|
||||
focusBoxShadow: 'none',
|
||||
borderRadius: '8px'
|
||||
},
|
||||
},
|
||||
currency: 'eur',
|
||||
mode: "payment",
|
||||
amount: amount*100,
|
||||
}}
|
||||
>
|
||||
<CheckoutForm amount={amount} sessionId={sessionId} locale={locale} />
|
||||
</Elements>
|
||||
);
|
||||
};
|
|
@ -1,60 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { DatePicker } from 'antd';
|
||||
import { CalendarOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/ru';
|
||||
import 'dayjs/locale/en';
|
||||
import 'dayjs/locale/de';
|
||||
import 'dayjs/locale/it';
|
||||
import 'dayjs/locale/fr';
|
||||
import 'dayjs/locale/es';
|
||||
import { getLocale } from '../../utils/locale';
|
||||
|
||||
export const CustomDatePicker = (props: any) => {
|
||||
const { label, value, locale, ...other } = props;
|
||||
const [isActiveLabel, setIsActiveLabel] = useState<boolean>(false);
|
||||
|
||||
dayjs.locale(locale);
|
||||
|
||||
useEffect(() => {
|
||||
if (label) {
|
||||
setIsActiveLabel(!!value);
|
||||
} else {
|
||||
setIsActiveLabel(false);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const onOpenChange = (open: boolean) => {
|
||||
if (open) {
|
||||
if (!isActiveLabel) setIsActiveLabel(true)
|
||||
} else {
|
||||
setIsActiveLabel(!!value)
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`b-datepicker-wrap ${isActiveLabel ? 'b-datepicker__active' : ''}`}>
|
||||
<div className="b-datepicker-label">
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
<DatePicker
|
||||
className="b-datepicker"
|
||||
format="YYYY-MM-DD"
|
||||
locale={getLocale(locale)}
|
||||
value={value}
|
||||
showNow={false}
|
||||
onOpenChange={onOpenChange}
|
||||
needConfirm={false}
|
||||
placeholder=""
|
||||
variant="filled"
|
||||
allowClear={false}
|
||||
popupClassName="b-datepicker-popup"
|
||||
minDate={dayjs().startOf('month')}
|
||||
suffixIcon={<CalendarOutlined style={{ color: '#2c7873', fontSize: 20 }} />}
|
||||
{...other}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -16,8 +16,8 @@ export const WithError: FC<WithErrorProps> = ({
|
|||
return (
|
||||
<Result
|
||||
status="error"
|
||||
title={errorData?.title}
|
||||
subTitle={errorData?.message}
|
||||
title="Submission Failed"
|
||||
subTitle="Please check and modify the following information before resubmitting."
|
||||
extra={refresh ? (
|
||||
<Button type="primary" onClick={refresh}>
|
||||
Refresh page
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export const BASE_URL = process.env.NEXT_PUBLIC_SERVER_BASE_URL || 'https://api.bbuddy.expert/api';
|
||||
export const AUTH_TOKEN_KEY = 'bbuddy_token';
|
||||
export const AUTH_USER = 'bbuddy_auth_user';
|
||||
export const SESSION_DATA = 'bbuddy_session_data';
|
||||
|
||||
export const DEFAULT_PAGE_SIZE = 5;
|
||||
export const DEFAULT_PAGE = 1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
|
||||
export function getStorageValue (key: string, defaultValue: any) {
|
||||
function getStorageValue (key: string, defaultValue: any) {
|
||||
if (typeof window !== 'undefined') {
|
||||
const saved = localStorage.getItem(key);
|
||||
return saved || defaultValue;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export default {
|
||||
accountMenu: {
|
||||
sessions: 'Kommende & letzte Sitzungen',
|
||||
rooms: 'Zimmer',
|
||||
notifications: 'Benachrichtigung',
|
||||
support: 'Hilfe & Support',
|
||||
information: 'Rechtliche Informationen',
|
||||
|
@ -43,47 +42,13 @@ export default {
|
|||
addComment: 'Neuen Kommentar hinzufügen',
|
||||
commentPlaceholder: 'Ihr Kommentar',
|
||||
clientComments: 'Kundenkommentare',
|
||||
coachComments: 'Expertenkommentare'
|
||||
coachComments: 'Trainerkommentare'
|
||||
},
|
||||
room: {
|
||||
upcoming: 'Zukünftige Räume',
|
||||
requested: 'Angeforderte Räume',
|
||||
recent: 'Kürzliche Räume',
|
||||
newRoom: 'Neuer Raum',
|
||||
editRoom: 'Raum bearbeiten',
|
||||
date: 'Datum',
|
||||
time: 'Zeit',
|
||||
maxParticipants: 'Max. erlaubte Teilnehmer',
|
||||
presenceOfSupervisor: 'Anwesenheit eines Supervisors',
|
||||
supervisor: 'Supervisor',
|
||||
members: 'Mitglieder',
|
||||
participants: 'Teilnehmer',
|
||||
roomCreator: 'Raum-Ersteller',
|
||||
inviteSupervisor: 'Supervisor einladen',
|
||||
joinSupervisor: 'Als Supervisor beitreten',
|
||||
inviteParticipant: 'Teilnehmer einladen',
|
||||
joinParticipant: 'Als Teilnehmer beitreten',
|
||||
rapport: 'Rapport',
|
||||
invite: 'Invite',
|
||||
save: 'Raum speichern',
|
||||
rate: 'Bewerten',
|
||||
tellAboutReason: 'Sag uns, was passiert ist',
|
||||
rating_raport: 'Rapport',
|
||||
rating_position_and_presence: 'Position oder Präsenz eines Coaches',
|
||||
rating_balance_and_frustration: 'Balance zwischen Unterstützung und Frustration',
|
||||
rating_agreement: 'Erstellung einer Coaching-Vereinbarung (Sitzungsvertrag)',
|
||||
rating_planning_and_goals: 'Planung und Zielsetzung',
|
||||
rating_reality: 'Klärung der Realität',
|
||||
rating_opportunities: 'Neue Möglichkeiten gefunden',
|
||||
rating_action_plan: 'Es wurde ein Aktionsplan erstellt',
|
||||
rating_motivation: 'Motivationsquellen gefunden',
|
||||
rating_next_session_stretch: 'Es ist noch Zeit bis zur nächsten Sitzung',
|
||||
rating_relationship: 'Aufbau einer vertrauensvollen Beziehung zum Klienten',
|
||||
rating_listening: 'Tiefes, aktives Zuhören',
|
||||
rating_questions: 'Verwendung „starker“ Fragen',
|
||||
rating_communication: 'Direkte Kommunikation',
|
||||
rating_awareness: 'Entwicklung und Stimulierung des Bewusstseins',
|
||||
rating_progress: 'Fortschritts- und Verantwortungsmanagement'
|
||||
newRoom: 'Neuer Raum'
|
||||
},
|
||||
agreementText: 'Folgendes habe ich gelesen und erkläre mich damit einverstanden: Benutzervereinbarung,',
|
||||
userAgreement: 'Benutzervereinbarung',
|
||||
|
@ -145,9 +110,9 @@ export default {
|
|||
seminars: 'Seminare',
|
||||
courses: 'Kurse',
|
||||
mba: 'MBA-Information',
|
||||
aboutCoach: 'Über den Experten',
|
||||
aboutCoach: 'Über Coach',
|
||||
education: 'Bildung',
|
||||
coaching: 'Expertenprofil',
|
||||
coaching: 'Coaching',
|
||||
experiences: 'Praktische Erfahrung',
|
||||
payInfo: 'Zahlungsdaten',
|
||||
sessionDuration: 'Sitzungsdauer',
|
||||
|
@ -181,10 +146,6 @@ export default {
|
|||
saturday: 'Sa',
|
||||
addNew: 'Neu hinzufügen',
|
||||
mExperiences: 'Führungserfahrung',
|
||||
pay: 'Zahlung',
|
||||
sessionWishes: 'Schreiben Sie Ihre Wünsche zur Sitzung',
|
||||
successPayment: 'Erfolgreiche Zahlung',
|
||||
errorPayment: 'Zahlungsfehler',
|
||||
errors: {
|
||||
invalidEmail: 'Die E-Mail-Adresse ist ungültig',
|
||||
emptyEmail: 'Bitte geben Sie Ihre E-Mail ein',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export default {
|
||||
accountMenu: {
|
||||
sessions: 'Upcoming & Recent Sessions',
|
||||
rooms: 'Rooms',
|
||||
notifications: 'Notification',
|
||||
support: 'Help & Support',
|
||||
information: 'Legal Information',
|
||||
|
@ -43,47 +42,13 @@ export default {
|
|||
addComment: 'Add new',
|
||||
commentPlaceholder: 'Your comment',
|
||||
clientComments: 'Client Comments',
|
||||
coachComments: 'Expert Comments'
|
||||
coachComments: 'Coach Comments'
|
||||
},
|
||||
room: {
|
||||
upcoming: 'Upcoming Rooms',
|
||||
requested: 'Rooms Requested',
|
||||
recent: 'Recent Rooms',
|
||||
newRoom: 'New Room',
|
||||
editRoom: 'Edit Room',
|
||||
date: 'Date',
|
||||
time: 'Time',
|
||||
maxParticipants: 'Max Participants Allowed',
|
||||
presenceOfSupervisor: 'Presence of a Supervisor',
|
||||
supervisor: 'Supervisor',
|
||||
members: 'Members',
|
||||
participants: 'Participants',
|
||||
roomCreator: 'Room Creator',
|
||||
inviteSupervisor: 'Invite Supervisor',
|
||||
joinSupervisor: 'Join As A Supervisor',
|
||||
inviteParticipant: 'Invite Participant',
|
||||
joinParticipant: 'Join as a participant',
|
||||
rapport: 'Rapport',
|
||||
invite: 'Invite',
|
||||
save: 'Save room',
|
||||
rate: 'Rate',
|
||||
tellAboutReason: 'Tell us what happened',
|
||||
rating_raport: 'Rapport',
|
||||
rating_position_and_presence: 'Coaching position or coaching presence',
|
||||
rating_balance_and_frustration: 'Balance of support and frustration',
|
||||
rating_agreement: 'Creating a coaching agreement (session contract)',
|
||||
rating_planning_and_goals: 'Planning and goal setting',
|
||||
rating_reality: 'Clarifying reality',
|
||||
rating_opportunities: 'New opportunities found',
|
||||
rating_action_plan: 'An action plan has been drawn up',
|
||||
rating_motivation: 'Sources of motivation found',
|
||||
rating_next_session_stretch: 'There is a stretch for the next session',
|
||||
rating_relationship: 'Establishing a trusting relationship with the client',
|
||||
rating_listening: 'Deep, active listening',
|
||||
rating_questions: 'Using "strong" questions',
|
||||
rating_communication: 'Direct communication',
|
||||
rating_awareness: 'Developing and stimulating awareness',
|
||||
rating_progress: 'Progress and Responsibility Management'
|
||||
newRoom: 'New Room'
|
||||
},
|
||||
agreementText: 'I have read and agree with the terms of the User Agreement,',
|
||||
userAgreement: 'User Agreement',
|
||||
|
@ -144,10 +109,10 @@ export default {
|
|||
seminars: 'Seminars',
|
||||
courses: 'Courses',
|
||||
mba: 'MBA Information',
|
||||
aboutCoach: 'About Expert',
|
||||
aboutCoach: 'About Coach',
|
||||
skillsInfo: 'Skills Info',
|
||||
education: 'Education',
|
||||
coaching: 'Expert profile',
|
||||
coaching: 'Coaching',
|
||||
experiences: 'Practical experience',
|
||||
payInfo: 'Payment Info',
|
||||
sessionDuration: 'Session duration',
|
||||
|
@ -181,10 +146,6 @@ export default {
|
|||
saturday: 'Sa',
|
||||
addNew: 'Add New',
|
||||
mExperiences: 'Managerial Experience',
|
||||
pay: 'Pay',
|
||||
sessionWishes: 'Write your wishes about the session',
|
||||
successPayment: 'Successful Payment',
|
||||
errorPayment: 'Payment Error',
|
||||
errors: {
|
||||
invalidEmail: 'The email address is not valid',
|
||||
emptyEmail: 'Please enter your E-mail',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export default {
|
||||
accountMenu: {
|
||||
sessions: 'Próximas y recientes sesiones',
|
||||
rooms: 'Habitaciones',
|
||||
notifications: 'Notificación',
|
||||
support: 'Ayuda y asistencia',
|
||||
information: 'Información jurídica',
|
||||
|
@ -43,47 +42,13 @@ export default {
|
|||
addComment: 'Añadir nuevo comentario',
|
||||
commentPlaceholder: 'Tu comentario',
|
||||
clientComments: 'Comentarios del cliente',
|
||||
coachComments: 'Comentarios del experto'
|
||||
coachComments: 'Comentarios del entrenador'
|
||||
},
|
||||
room: {
|
||||
upcoming: 'Próximas salas',
|
||||
requested: 'Salas solicitadas',
|
||||
recent: 'Salas recientes',
|
||||
newRoom: 'Nueva sala',
|
||||
editRoom: 'Editar la sala',
|
||||
date: 'Fecha',
|
||||
time: 'Tiempo',
|
||||
maxParticipants: 'Máximo de participantes permitidos',
|
||||
presenceOfSupervisor: 'Presencia de un supervisor',
|
||||
supervisor: 'Supervisor',
|
||||
members: 'Miembros',
|
||||
participants: 'Participantes',
|
||||
roomCreator: 'Creador de salas',
|
||||
inviteSupervisor: 'Invitar al supervisor',
|
||||
joinSupervisor: 'Unirse como supervisor',
|
||||
inviteParticipant: 'Invitar a un participante',
|
||||
joinParticipant: 'Unirse como participante',
|
||||
rapport: 'Buena relación',
|
||||
invite: 'Invitar',
|
||||
save: 'Guardar sala',
|
||||
rate: 'Valorar',
|
||||
tellAboutReason: 'Cuéntanos qué ha pasado',
|
||||
rating_raport: 'Buena relación',
|
||||
rating_position_and_presence: 'Puesto de coach o presencia de coach',
|
||||
rating_balance_and_frustration: 'Equilibrio entre apoyo y frustración',
|
||||
rating_agreement: 'Crear un acuerdo de coaching (contrato de sesión)',
|
||||
rating_planning_and_goals: 'Planear y establecer los objetivos',
|
||||
rating_reality: 'Clarificar la realidad',
|
||||
rating_opportunities: 'Nuevas oportunidades encontradas',
|
||||
rating_action_plan: 'Se ha diseñado un plan de acción',
|
||||
rating_motivation: 'Fuentes de motivación encontradas',
|
||||
rating_next_session_stretch: 'Queda un poco para la siguiente sesión',
|
||||
rating_relationship: 'Establecer una relación de confianza con el cliente',
|
||||
rating_listening: 'Escucha activa y profunda',
|
||||
rating_questions: 'Usar preguntas "contundentes"',
|
||||
rating_communication: 'Comunicación directa',
|
||||
rating_awareness: 'Desarrollar y estimular la conciencia',
|
||||
rating_progress: 'Progreso y gestión de la responsabilidad'
|
||||
newRoom: 'Nueva sala'
|
||||
},
|
||||
agreementText: 'He leído y acepto las condiciones del Acuerdo de usuario,',
|
||||
userAgreement: 'Acuerdo de usuario',
|
||||
|
@ -145,9 +110,9 @@ export default {
|
|||
seminars: 'Seminarios',
|
||||
courses: 'Cursos',
|
||||
mba: 'Información sobre máster en ADE (MBA)',
|
||||
aboutCoach: 'Acerca del experto',
|
||||
aboutCoach: 'Sobre el coach',
|
||||
education: 'Educación',
|
||||
coaching: 'Perfil del experto',
|
||||
coaching: 'Coaching',
|
||||
experiences: 'Experiencia práctica',
|
||||
payInfo: 'Información de pago',
|
||||
sessionDuration: 'Duración de la sesión',
|
||||
|
@ -181,10 +146,6 @@ export default {
|
|||
saturday: 'S',
|
||||
addNew: 'Añadir nuevo',
|
||||
mExperiences: 'Experiencia de dirección',
|
||||
pay: 'Pago',
|
||||
sessionWishes: 'Escribe tus deseos sobre la sesión',
|
||||
successPayment: 'Pago Exitoso',
|
||||
errorPayment: 'Error de Pago',
|
||||
errors: {
|
||||
invalidEmail: 'La dirección de correo electrónico no es válida',
|
||||
emptyEmail: 'Introduce tu correo electrónico',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export default {
|
||||
accountMenu: {
|
||||
sessions: 'Sessions futures et récentes',
|
||||
rooms: 'Chambres',
|
||||
notifications: 'Notification',
|
||||
support: 'Aide et support',
|
||||
information: 'Informations légales',
|
||||
|
@ -43,47 +42,13 @@ export default {
|
|||
addComment: 'Ajouter un nouveau commentaire',
|
||||
commentPlaceholder: 'Votre commentaire',
|
||||
clientComments: 'Commentaires du client',
|
||||
coachComments: 'Commentaires de l\'expert'
|
||||
coachComments: 'Commentaires du coach'
|
||||
},
|
||||
room: {
|
||||
upcoming: 'Salles futures',
|
||||
requested: 'Salles demandées',
|
||||
recent: 'Salles récentes',
|
||||
newRoom: 'Nouvelle salle',
|
||||
editRoom: 'Modifier la salle',
|
||||
date: 'Date',
|
||||
time: 'Temps',
|
||||
maxParticipants: 'Max de participants autorisés',
|
||||
presenceOfSupervisor: 'Présence d\'un superviseur',
|
||||
supervisor: 'Superviseur',
|
||||
members: 'Membres',
|
||||
participants: 'Participants',
|
||||
roomCreator: 'Créateur de la salle',
|
||||
inviteSupervisor: 'Inviter un superviseur',
|
||||
joinSupervisor: 'Rejoindre en tant que superviseur',
|
||||
inviteParticipant: 'Inviter un participant',
|
||||
joinParticipant: 'Rejoindre en tant que participant',
|
||||
rapport: 'Rapport',
|
||||
invite: 'Inviter',
|
||||
save: 'Sauvegarder la salle',
|
||||
rate: 'Noter',
|
||||
tellAboutReason: 'Dites-nous ce qui s\'est passé',
|
||||
rating_raport: 'Rapport',
|
||||
rating_position_and_presence: 'Poste de coach ou présence de coach',
|
||||
rating_balance_and_frustration: 'Équilibre entre assistance et frustration',
|
||||
rating_agreement: 'Création d\'un contrat de coaching (contrat de séance)',
|
||||
rating_planning_and_goals: 'Planification et définition des objectifs',
|
||||
rating_reality: 'Clarification de la réalité',
|
||||
rating_opportunities: 'Nouvelles opportunités trouvées',
|
||||
rating_action_plan: 'Un plan d\'action a été établi',
|
||||
rating_motivation: 'Sources de motivation trouvées',
|
||||
rating_next_session_stretch: 'Une période est présente pour la prochaine session',
|
||||
rating_relationship: 'Établissement d\'une relation de confiance avec le client',
|
||||
rating_listening: 'Écoute approfondie et active',
|
||||
rating_questions: 'Utilisation de questions «fortes»',
|
||||
rating_communication: 'Communication directe',
|
||||
rating_awareness: 'Développement et stimulation de la prise de conscience',
|
||||
rating_progress: 'Gestion de la progression et de la responsabilité'
|
||||
newRoom: 'Nouvelle salle'
|
||||
},
|
||||
agreementText: 'J\'ai lu et j\'accepte les dispositions de l\'Accord Utilisateur et de la',
|
||||
userAgreement: '',
|
||||
|
@ -145,9 +110,9 @@ export default {
|
|||
seminars: 'Séminaires',
|
||||
courses: 'Cours',
|
||||
mba: 'Infos Maîtrise en gestion',
|
||||
aboutCoach: 'À propos de l\'expert',
|
||||
aboutCoach: 'À propos du coach',
|
||||
education: 'Éducation',
|
||||
coaching: 'Profil de l\'expert',
|
||||
coaching: 'Coaching',
|
||||
experiences: 'Expérience pratique',
|
||||
payInfo: 'Infos sur le paiement',
|
||||
sessionDuration: 'Durée de la session',
|
||||
|
@ -181,10 +146,6 @@ export default {
|
|||
saturday: 'Sa',
|
||||
addNew: 'Ajouter un nouveau',
|
||||
mExperiences: 'Expérience en gestion',
|
||||
pay: 'Paiement',
|
||||
sessionWishes: 'Écrivez vos souhaits concernant la session',
|
||||
successPayment: 'Paiement Réussi',
|
||||
errorPayment: 'Erreur de Paiement',
|
||||
errors: {
|
||||
invalidEmail: 'L\'adresse e-mail n\'est pas valide',
|
||||
emptyEmail: 'Veuillez saisir votre e-mail',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export default {
|
||||
accountMenu: {
|
||||
sessions: 'Prossime e recenti sessioni',
|
||||
rooms: 'Stanze',
|
||||
notifications: 'Notifica',
|
||||
support: 'Assistenza e supporto',
|
||||
information: 'Informazioni legali',
|
||||
|
@ -43,47 +42,13 @@ export default {
|
|||
addComment: 'Aggiungi nuovo commento',
|
||||
commentPlaceholder: 'Il tuo commento',
|
||||
clientComments: 'Commenti del cliente',
|
||||
coachComments: 'Commenti dell\'esperto'
|
||||
coachComments: 'Commenti dell\'allenatore'
|
||||
},
|
||||
room: {
|
||||
upcoming: 'Prossime sale',
|
||||
requested: 'Sale richieste',
|
||||
recent: 'Sale recenti',
|
||||
newRoom: 'Nuova sala',
|
||||
editRoom: 'Modifica sala',
|
||||
date: 'Data',
|
||||
time: 'Tempo',
|
||||
maxParticipants: 'Numero massimo di partecipanti consentiti',
|
||||
presenceOfSupervisor: 'Presenza di un relatore',
|
||||
supervisor: 'Relatore',
|
||||
members: 'Iscritti',
|
||||
participants: 'Partecipanti',
|
||||
roomCreator: 'Creatore sala',
|
||||
inviteSupervisor: 'Invita relatore',
|
||||
joinSupervisor: 'Partecipa come relatore',
|
||||
inviteParticipant: 'Invita partecipante',
|
||||
joinParticipant: 'Partecipa come partecipante',
|
||||
rapport: 'Rapporto',
|
||||
invite: 'Invita',
|
||||
save: 'Salva sala',
|
||||
rate: 'Valuta',
|
||||
tellAboutReason: 'Descrivi cosa è successo',
|
||||
rating_raport: 'Rapporto',
|
||||
rating_position_and_presence: 'Posizione di coaching o presenza di coaching',
|
||||
rating_balance_and_frustration: 'Equilibrio tra sostegno e frustrazione',
|
||||
rating_agreement: 'Creazione di un accordo di coaching (contratto di sessione)',
|
||||
rating_planning_and_goals: 'Pianificazione e definizione di obiettivi',
|
||||
rating_reality: 'Chiarimento della realtà',
|
||||
rating_opportunities: 'Nuove opportunità trovate',
|
||||
rating_action_plan: 'È stato elaborato un piano d\'azione',
|
||||
rating_motivation: 'Fonti di motivazione trovate',
|
||||
rating_next_session_stretch: 'Esiste un\'estensione per la prossima sessione',
|
||||
rating_relationship: 'Instaurazione di un rapporto di fiducia con il cliente',
|
||||
rating_listening: 'Ascolto profondo e attivo',
|
||||
rating_questions: 'Utilizzo di domande "forti"',
|
||||
rating_communication: 'Comunicazione diretta',
|
||||
rating_awareness: 'Sviluppo e stimolo della consapevolezza',
|
||||
rating_progress: 'Gestione dei progressi e delle responsabilità'
|
||||
newRoom: 'Nuova sala'
|
||||
},
|
||||
agreementText: 'Ho letto e accetto i termini dell\'Accordo con l\'utente,',
|
||||
userAgreement: '',
|
||||
|
@ -145,9 +110,9 @@ export default {
|
|||
seminars: 'Seminari',
|
||||
courses: 'Corsi',
|
||||
mba: 'Info sull\'MBA',
|
||||
aboutCoach: 'Informazioni sull\'esperto',
|
||||
aboutCoach: 'Informazioni sul coach',
|
||||
education: 'Istruzione',
|
||||
coaching: 'Profilo dell\'esperto',
|
||||
coaching: 'Coaching',
|
||||
experiences: 'Esperienza pratica',
|
||||
payInfo: 'Info pagamento',
|
||||
sessionDuration: 'Durata della sessione',
|
||||
|
@ -181,10 +146,6 @@ export default {
|
|||
saturday: 'Sa',
|
||||
addNew: 'Aggiungi nuovo',
|
||||
mExperiences: 'Esperienza manageriale',
|
||||
pay: 'Pagamento',
|
||||
sessionWishes: 'Scrivi i tuoi desideri riguardo alla sessione',
|
||||
successPayment: 'Pagamento Riuscito',
|
||||
errorPayment: 'Errore di Pagamento',
|
||||
errors: {
|
||||
invalidEmail: 'L\'indirizzo e-mail non è valido',
|
||||
emptyEmail: 'Inserisci l\'e-mail',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export default {
|
||||
accountMenu: {
|
||||
sessions: 'Предстоящие и недавние сессии',
|
||||
rooms: 'Комнаты',
|
||||
notifications: 'Уведомления',
|
||||
support: 'Служба поддержки',
|
||||
information: 'Юридическая информация',
|
||||
|
@ -43,47 +42,13 @@ export default {
|
|||
addComment: 'Добавить новый',
|
||||
commentPlaceholder: 'Ваш комментарий',
|
||||
clientComments: 'Комментарии клиента',
|
||||
coachComments: 'Комментарии эксперта'
|
||||
coachComments: 'Комментарии коуча'
|
||||
},
|
||||
room: {
|
||||
upcoming: 'Предстоящие комнаты',
|
||||
requested: 'Запрошенные комнаты',
|
||||
recent: 'Недавние комнаты',
|
||||
newRoom: 'Новая комната',
|
||||
editRoom: 'Изменить комнату',
|
||||
date: 'Дата',
|
||||
time: 'Время',
|
||||
maxParticipants: 'Макс. кол-во участников',
|
||||
presenceOfSupervisor: 'Присутствие супервизора',
|
||||
supervisor: 'Супервайзер',
|
||||
members: 'Участники',
|
||||
participants: 'Участники',
|
||||
roomCreator: 'Создатель комнаты',
|
||||
inviteSupervisor: 'Пригласить супервизора',
|
||||
joinSupervisor: 'Присоединиться как супервизор',
|
||||
inviteParticipant: 'Пригласить участника',
|
||||
joinParticipant: 'Присоединиться как участник',
|
||||
rapport: 'Раппорт',
|
||||
invite: 'Пригласить',
|
||||
save: 'Сохранить комнату',
|
||||
rate: 'Оценить',
|
||||
tellAboutReason: 'Расскажите, что произошло',
|
||||
rating_raport: 'Раппорт',
|
||||
rating_position_and_presence: 'Коуч-позиция или коучинговое присутствие',
|
||||
rating_balance_and_frustration: 'Баланс поддержки и фрустрации',
|
||||
rating_agreement: 'Создание коучингового соглашения (контракт на сессию)',
|
||||
rating_planning_and_goals: 'Планирование и постановка целей',
|
||||
rating_reality: 'Прояснение реальности',
|
||||
rating_opportunities: 'Найдены новые возможности',
|
||||
rating_action_plan: 'Составлен план действий',
|
||||
rating_motivation: 'Найдены источники мотивации',
|
||||
rating_next_session_stretch: 'Есть "протяжка" на следующую сессию',
|
||||
rating_relationship: 'Установление доверительных отношений с клиентом',
|
||||
rating_listening: 'Глубокое активное слушание',
|
||||
rating_questions: 'Использование сильных вопросов',
|
||||
rating_communication: 'Прямая коммуникация',
|
||||
rating_awareness: 'Развитие и стимулирование осознанности',
|
||||
rating_progress: 'Управление прогрессом и ответственностью'
|
||||
newRoom: 'Новая комната'
|
||||
},
|
||||
agreementText: 'Я прочитал и согласен с условиями Пользовательского соглашения,',
|
||||
userAgreement: 'Пользовательского соглашения',
|
||||
|
@ -146,9 +111,9 @@ export default {
|
|||
courses: 'Курсы',
|
||||
mba: 'Информация о MBA',
|
||||
experiences: 'Практический опыт',
|
||||
aboutCoach: 'Информация об эксперте',
|
||||
aboutCoach: 'О коуче',
|
||||
education: 'Образование',
|
||||
coaching: 'Профиль эксперта',
|
||||
coaching: 'Коучинг',
|
||||
payInfo: 'Платежная информация',
|
||||
sessionDuration: 'Продолжительность сессии',
|
||||
experienceHours: 'Общее количество часов практического опыта',
|
||||
|
@ -181,10 +146,6 @@ export default {
|
|||
saturday: 'Сб',
|
||||
addNew: 'Добавить',
|
||||
mExperiences: 'Управленческий опыт',
|
||||
pay: 'Оплата',
|
||||
sessionWishes: 'Напишите свои пожелания по поводу сессии',
|
||||
successPayment: 'Успешная оплата',
|
||||
errorPayment: 'Ошибка оплаты',
|
||||
chat: {
|
||||
join: 'начать чат',
|
||||
joinAI: 'начать чат с ИИ'
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import "server-only";
|
||||
|
||||
import Stripe from "stripe";
|
||||
|
||||
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
|
||||
apiVersion: "2024-06-20",
|
||||
appInfo: {
|
||||
name: "bbuddy-ui",
|
||||
url: "",
|
||||
},
|
||||
});
|
|
@ -668,7 +668,6 @@ a {
|
|||
& > div {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding-left: 1px;
|
||||
|
||||
&:first-child {
|
||||
flex-direction: column;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__comment__content, &__report__content {
|
||||
&__comment__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 44px 40px;
|
||||
|
@ -82,13 +82,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__users-list__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 40px;
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-mask {
|
||||
|
|
|
@ -931,10 +931,6 @@
|
|||
&.chosen {
|
||||
color: #D93E5C;
|
||||
}
|
||||
|
||||
&.history {
|
||||
color: #c4c4c4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
&__wrap {
|
||||
width: 100%;
|
||||
height: 716px;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&__single {
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
|
@ -28,16 +25,6 @@
|
|||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
z-index: 2;
|
||||
|
||||
&_group {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background: rgba(0, 59, 70, 0.4);
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&__controls {
|
||||
|
@ -139,48 +126,6 @@
|
|||
position: absolute;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&_groups {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
|
||||
& > div {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
|
||||
video {
|
||||
object-fit: contain !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.gr-1 {
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.gr-2, &.gr-3, &.gr-4 {
|
||||
& > div {
|
||||
flex: calc((100% - 16px) / 2) 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.gr-5, &.gr-6, &.gr-7, &.gr-8, &.gr-9 {
|
||||
& > div {
|
||||
flex: calc((100% - 16px * 2) / 3) 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.gr-10, &.gr-11, &.gr-12, &.gr-13, &.gr-14, &.gr-15, &.gr-16 {
|
||||
& > div {
|
||||
flex: calc((100% - 16px * 3) / 4) 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__video {
|
||||
|
|
|
@ -18,11 +18,6 @@
|
|||
background: lightgray 50%;
|
||||
box-shadow: 0 8px 16px 0 rgba(102, 165, 173, 0.32);
|
||||
overflow: hidden;
|
||||
|
||||
&_small {
|
||||
width: 86px;
|
||||
height: 86px;
|
||||
}
|
||||
}
|
||||
|
||||
&__inner {
|
||||
|
@ -46,41 +41,6 @@
|
|||
line-height: 120%;
|
||||
}
|
||||
|
||||
&__supervisor-comment {
|
||||
width: 100%;
|
||||
background: #E4F5FA;
|
||||
padding: 8px;
|
||||
border-radius: 0 8px 8px 8px;
|
||||
color: #66A5AD;
|
||||
@include rem(13);
|
||||
font-weight: 500;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
&__report-list {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
color: #4E7C86;
|
||||
@include rem(13);
|
||||
font-weight: 500;
|
||||
line-height: 120%;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
&_divider {
|
||||
flex: 1;
|
||||
border-bottom: 1px solid #E4F5FA;
|
||||
}
|
||||
}
|
||||
|
||||
&__comments {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -240,31 +200,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__filled {
|
||||
user-select: none;
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
border-radius: 8px !important;
|
||||
background: #66A5AD !important;
|
||||
box-shadow: none !important;
|
||||
display: flex;
|
||||
height: 54px !important;
|
||||
padding: 15px 24px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff !important;
|
||||
@include rem(15);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 160%;
|
||||
|
||||
&:hover, &:active {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
padding-bottom: 8px;
|
||||
|
@ -333,54 +268,6 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__profile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding-top: 16px;
|
||||
align-items: flex-start;
|
||||
border-top: 1px solid #C4DFE6;
|
||||
|
||||
&_title {
|
||||
width: 100%;
|
||||
gap: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
div {
|
||||
@include rem(18);
|
||||
font-weight: 600;
|
||||
line-height: 150%;
|
||||
color: #6FB98F;
|
||||
|
||||
&:first-child {
|
||||
color: #003B46;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&_list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&_item {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: space-between;
|
||||
|
||||
.card-detail__inner {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-detail__name {
|
||||
color: #2C7873;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
.b-calendar {
|
||||
padding: 44px 40px !important;
|
||||
|
||||
&-month {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
&-header {
|
||||
justify-content: center;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
&-cell {
|
||||
span {
|
||||
color: #66A5AD;
|
||||
}
|
||||
|
||||
&__weekend {
|
||||
span {
|
||||
color: #FFBD00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-picker-body {
|
||||
margin-bottom: -42px !important;
|
||||
}
|
||||
|
||||
.ant-picker-panel {
|
||||
border-top: none !important;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.ant-picker-cell {
|
||||
opacity: 0 !important;
|
||||
|
||||
&-disabled {
|
||||
&::before {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
span {
|
||||
color: rgba(0, 0, 0, 0.25) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-picker-cell-in-view {
|
||||
opacity: 1 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
th, td {
|
||||
vertical-align: middle !important;
|
||||
height: 40px !important;
|
||||
}
|
||||
|
||||
th {
|
||||
color: #66A5AD !important;
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
.b-datepicker {
|
||||
width: 100% !important;
|
||||
height: 54px !important;
|
||||
|
||||
&.ant-picker-filled {
|
||||
background: transparent !important;
|
||||
z-index: 1;
|
||||
padding-top: 22px !important;
|
||||
padding-left: 16px !important;
|
||||
|
||||
&:hover {
|
||||
border-color: #2c7873 !important;
|
||||
}
|
||||
|
||||
.ant-picker-input {
|
||||
input {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-picker-suffix {
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
&-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: #F8F8F7;
|
||||
border-radius: 8px;
|
||||
|
||||
&.b-datepicker__active .b-datepicker-label {
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
line-height: 14px;
|
||||
top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-label {
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
color: #000;
|
||||
opacity: .3;
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 15px;
|
||||
right: 22px;
|
||||
z-index: 0;
|
||||
transition: all .1s ease;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&-popup {
|
||||
padding: 16px !important;
|
||||
|
||||
.ant-picker-date-panel {
|
||||
padding: 16px 8px !important;
|
||||
}
|
||||
|
||||
.ant-picker-header-view {
|
||||
color: #2c7873 !important;
|
||||
}
|
||||
|
||||
.ant-picker-header {
|
||||
border: none !important;
|
||||
|
||||
.ant-picker-header-super-prev-btn, .ant-picker-header-super-next-btn {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-picker-cell {
|
||||
opacity: 0 !important;
|
||||
padding: 0 !important;
|
||||
|
||||
&:not(.ant-picker-cell-disabled) {
|
||||
color: #66A5AD !important;
|
||||
|
||||
&:hover {
|
||||
.ant-picker-cell-inner {
|
||||
color: #6FB98F !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-selected:not(.ant-picker-cell-disabled) .ant-picker-cell-inner {
|
||||
color: #6FB98F !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
color: rgba(0, 0, 0, 0.25) !important;
|
||||
|
||||
&::before {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-picker-cell-in-view {
|
||||
opacity: 1 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ant-picker-cell-inner::before {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
th, td {
|
||||
vertical-align: middle !important;
|
||||
height: 36px !important;
|
||||
}
|
||||
|
||||
th {
|
||||
color: #66A5AD !important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
.ant-form-item-has-error .ant-radio-inner {
|
||||
border-color: #ff4d4f !important;
|
||||
}
|
|
@ -14,25 +14,4 @@
|
|||
color: #c4dfe6 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
&_title {
|
||||
color: #2C7873;
|
||||
@include rem(13);
|
||||
font-weight: 500;
|
||||
line-height: 120%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
.card-room {
|
||||
&__details {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 120px auto;
|
||||
gap: 4px 8px;
|
||||
|
||||
div {
|
||||
@include rem(13);
|
||||
font-weight: 500;
|
||||
line-height: 120%;
|
||||
color: #2C7873;
|
||||
|
||||
&:nth-child(2n) {
|
||||
color: #6FB98F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.b-users-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&__empty {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
&-item {
|
||||
padding: 0 0 16px;
|
||||
border-bottom: 1px solid #C4DFE6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.b-room-form {
|
||||
&__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.card-detail__apply {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.b-room-switch {
|
||||
label {
|
||||
margin-right: 24px;
|
||||
&:after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-item-option-content {
|
||||
span {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
.b-schedule {
|
||||
&-time {
|
||||
padding: 44px 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
|
||||
.b-button-link-big {
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
color: #6FB98F !important;
|
||||
font-family: var(--font-comfortaa);
|
||||
padding: 0 !important;
|
||||
border: none !important;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
|
||||
&-radio-list {
|
||||
.ant-radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-payment {
|
||||
padding: 44px 40px;
|
||||
min-height: 300px;
|
||||
}
|
||||
}
|
|
@ -3,12 +3,11 @@
|
|||
height: 54px !important;
|
||||
|
||||
.ant-select-selector {
|
||||
background-color: transparent !important;
|
||||
background-color: #F8F8F7 !important;
|
||||
border-color: #F8F8F7 !important;
|
||||
border-radius: 8px !important;
|
||||
padding: 22px 16px 8px !important;
|
||||
box-shadow: none !important;
|
||||
z-index: 1;
|
||||
|
||||
.ant-select-selection-item {
|
||||
font-size: 15px !important;
|
||||
|
@ -18,12 +17,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.ant-select-status-error {
|
||||
.ant-select-selector {
|
||||
border-color: #ff4d4f !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-selection-overflow-item {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
@ -42,9 +35,6 @@
|
|||
&-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: #F8F8F7;
|
||||
border-radius: 8px;
|
||||
|
||||
&.b-multiselect__active .b-multiselect-label {
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
|
@ -54,12 +44,12 @@
|
|||
}
|
||||
|
||||
&-label {
|
||||
font-size: 14px;
|
||||
font-size: 15px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
color: #000;
|
||||
opacity: .4;
|
||||
opacity: .3;
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 15px;
|
||||
|
@ -80,12 +70,11 @@
|
|||
height: 54px !important;
|
||||
|
||||
.ant-select-selector {
|
||||
background-color: transparent !important;
|
||||
background-color: #F8F8F7 !important;
|
||||
border-color: #F8F8F7 !important;
|
||||
border-radius: 8px !important;
|
||||
padding: 22px 16px 8px !important;
|
||||
box-shadow: none !important;
|
||||
z-index: 1;
|
||||
|
||||
.ant-select-selection-item {
|
||||
font-size: 15px !important;
|
||||
|
@ -95,12 +84,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.ant-select-status-error {
|
||||
.ant-select-selector {
|
||||
border-color: #ff4d4f !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-arrow {
|
||||
color: #2c7873 !important;
|
||||
}
|
||||
|
@ -115,8 +98,6 @@
|
|||
&-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: #F8F8F7;
|
||||
border-radius: 8px;
|
||||
|
||||
&.b-select__active .b-select-label {
|
||||
font-size: 12px;
|
||||
|
@ -132,7 +113,7 @@
|
|||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
color: #000;
|
||||
opacity: .4;
|
||||
opacity: .3;
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 15px;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
.ant-picker-input {
|
||||
input {
|
||||
font-size: 14px !important;
|
||||
font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,3 @@
|
|||
@import "_practice.scss";
|
||||
@import "_collapse.scss";
|
||||
@import "_timepicker.scss";
|
||||
@import "_datepicker.scss";
|
||||
@import "_calendar.scss";
|
||||
@import "_schedule.scss";
|
||||
@import "_radio.scss";
|
||||
@import "_room.scss";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful'
|
||||
import {BlogPostFields} from "./blogPost";
|
||||
import {ContentImage} from "../lib/contentful/contentImage";
|
||||
|
||||
export interface AuthorFields {
|
||||
|
|
|
@ -71,24 +71,3 @@ export type ExpertDetails = {
|
|||
associations?: Association[];
|
||||
associationLevels?: AssociationLevel[];
|
||||
};
|
||||
|
||||
export type Slot = {
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
}
|
||||
|
||||
export type ExpertScheduler = {
|
||||
tags: Tag[],
|
||||
availableSlots: Slot[];
|
||||
}
|
||||
|
||||
export type ExpertSchedulerSession = {
|
||||
sessionId: string
|
||||
};
|
||||
|
||||
export type SignupSessionData = {
|
||||
coachId: number,
|
||||
tagId?: number,
|
||||
startAtUtc?: string,
|
||||
clientComment?: string
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
export type Payment = {
|
||||
amount: number;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
import { PublicUser, Session, SessionState } from './sessions';
|
||||
import { Tag } from './tags';
|
||||
import { Slot } from './experts';
|
||||
|
||||
export enum RoomsType {
|
||||
UPCOMING = 'upcoming',
|
||||
RECENT = 'recent',
|
||||
NEW = 'new',
|
||||
}
|
||||
|
||||
export type Record = {
|
||||
id: number;
|
||||
sessionId: number;
|
||||
sid?: string;
|
||||
resourceId?: string;
|
||||
readyForLoad?: boolean;
|
||||
cname?: string;
|
||||
}
|
||||
|
||||
export type Room = Session & { recordings?: Record[] };
|
||||
|
||||
export type GetUsersForRooms = {
|
||||
items?: PublicUser[],
|
||||
isTooManyResults?: boolean;
|
||||
}
|
||||
|
||||
export type RoomEdit = {
|
||||
id: number,
|
||||
scheduledStartAtUtc?: string,
|
||||
scheduledEndAtUtc?: string,
|
||||
state?: SessionState,
|
||||
cost?: number,
|
||||
maxClients?: number,
|
||||
title?: string,
|
||||
description?: string,
|
||||
isNeedSupervisor?: boolean,
|
||||
tagIds?: number[]
|
||||
};
|
||||
|
||||
export type RoomEditDTO = {
|
||||
item: RoomEdit;
|
||||
tags?: Tag[];
|
||||
availableSlots: Slot[];
|
||||
};
|
||||
|
||||
export type Report = {
|
||||
evaluationCriteriaId: number,
|
||||
evaluationCriteriaName?: string,
|
||||
score?: number,
|
||||
key?: string
|
||||
};
|
||||
|
||||
export type ReportData = {
|
||||
sessionId: number,
|
||||
sessionSupervisorScores: Report[],
|
||||
supervisorComment?: string
|
||||
};
|
|
@ -6,8 +6,6 @@ export type PublicUser = {
|
|||
name?: string;
|
||||
surname?: string;
|
||||
faceImageUrl?: string;
|
||||
coachBotId?: number;
|
||||
parentId?: number;
|
||||
};
|
||||
|
||||
// type User = {
|
||||
|
@ -150,7 +148,6 @@ export type Session = {
|
|||
themesTags?: SessionTag[];
|
||||
coachComments?: SessionComment[];
|
||||
clientComments?: SessionComment[];
|
||||
creatorId?: number;
|
||||
};
|
||||
|
||||
export enum SessionType {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { message } from 'antd';
|
|||
import type { UploadFile } from 'antd';
|
||||
import { i18nText } from '../i18nKeys';
|
||||
|
||||
const ROUTES = ['sessions', 'rooms', 'notifications', 'support', 'information', 'settings', 'messages', 'expert-profile'];
|
||||
const ROUTES = ['sessions', 'notifications', 'support', 'information', 'settings', 'messages', 'expert-profile'];
|
||||
const COUNTS: Record<string, number> = {
|
||||
sessions: 12,
|
||||
notifications: 5
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { Stripe, loadStripe } from '@stripe/stripe-js';
|
||||
|
||||
let stripePromise: Promise<Stripe | null>;
|
||||
|
||||
export default function getStripe(): Promise<Stripe | null> {
|
||||
if (!stripePromise)
|
||||
stripePromise = loadStripe(
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY as string,
|
||||
);
|
||||
|
||||
return stripePromise;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import locale_ru from 'antd/lib/calendar/locale/ru_RU';
|
||||
import locale_en from 'antd/lib/calendar/locale/en_GB';
|
||||
import locale_de from 'antd/lib/calendar/locale/de_DE';
|
||||
import locale_it from 'antd/lib/calendar/locale/it_IT';
|
||||
import locale_es from 'antd/lib/calendar/locale/es_ES';
|
||||
import locale_fr from 'antd/lib/calendar/locale/fr_FR';
|
||||
|
||||
// for calendars
|
||||
export const getLocale = (locale: string) => {
|
||||
if (locale) {
|
||||
switch (locale) {
|
||||
case 'ru':
|
||||
return locale_ru;
|
||||
case 'de':
|
||||
return locale_de;
|
||||
case 'fr':
|
||||
return locale_fr;
|
||||
case 'it':
|
||||
return locale_it;
|
||||
case 'es':
|
||||
return locale_es;
|
||||
default:
|
||||
return locale_en;
|
||||
}
|
||||
}
|
||||
|
||||
return locale_en;
|
||||
};
|
|
@ -1,30 +0,0 @@
|
|||
export function formatAmountForDisplay(
|
||||
amount: number,
|
||||
currency: string,
|
||||
): string {
|
||||
let numberFormat = new Intl.NumberFormat(["en-US"], {
|
||||
style: "currency",
|
||||
currency: currency,
|
||||
currencyDisplay: "symbol",
|
||||
});
|
||||
return numberFormat.format(amount);
|
||||
}
|
||||
|
||||
export function formatAmountForStripe(
|
||||
amount: number,
|
||||
currency: string,
|
||||
): number {
|
||||
let numberFormat = new Intl.NumberFormat(["en-US"], {
|
||||
style: "currency",
|
||||
currency: currency,
|
||||
currencyDisplay: "symbol",
|
||||
});
|
||||
const parts = numberFormat.formatToParts(amount);
|
||||
let zeroDecimalCurrency: boolean = true;
|
||||
for (let part of parts) {
|
||||
if (part.type === "decimal") {
|
||||
zeroDecimalCurrency = false;
|
||||
}
|
||||
}
|
||||
return zeroDecimalCurrency ? amount : Math.round(amount * 100);
|
||||
}
|
Loading…
Reference in New Issue