From b1f52d330553f01ec4dab3271d2a57b2ce16da97 Mon Sep 17 00:00:00 2001 From: aeddaqqa Date: Wed, 11 Sep 2024 03:21:25 +0100 Subject: [PATCH] set getters and update functions for messenger configuration --- package.json | 5 +- src/components/Input.tsx | 48 +- src/components/MainButton.tsx | 34 +- src/helpers/partnerConfigurationContext.tsx | 134 +- src/helpers/usePartnerConfig.tsx | 407 +- src/helpers/useSmartContract.tsx | 113 +- src/helpers/useWalletBalance.tsx | 38 + src/layout/PartnersLayout.tsx | 172 +- src/layout/RoutesSuite.tsx | 11 +- src/redux/services/partners.ts | 13 +- src/redux/slices/partner.ts | 60 + src/redux/store.ts | 8 +- src/views/partners/ConfigurDistrubitor.tsx | 273 + src/views/partners/ConfigurSupplier.tsx | 290 + src/views/partners/Configuration.tsx | 349 +- src/views/partners/ManageBots.tsx | 251 + src/views/settings/Links.tsx | 92 +- webpack.local.js | 5 - yarn.lock | 6776 ++++++++++--------- 19 files changed, 5629 insertions(+), 3450 deletions(-) create mode 100644 src/helpers/useWalletBalance.tsx create mode 100644 src/redux/slices/partner.ts create mode 100644 src/views/partners/ConfigurDistrubitor.tsx create mode 100644 src/views/partners/ConfigurSupplier.tsx create mode 100644 src/views/partners/ManageBots.tsx diff --git a/package.json b/package.json index 48c9b1ca..8e47191e 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.1", "@testing-library/user-event": "^14.1.1", + "@types/big.js": "^6.2.2", "@types/ethereum-protocol": "^1.0.5", "@types/jest": "^27.4.1", "@types/lodash": "^4.14.189", @@ -81,11 +82,11 @@ "webpack-cli": "^5.0.0", "webpack-dev-server": "^4.3.1", "webpack-merge": "^5.8.0", - "yup": "^0.32.11" + "yup": "^0.32.11", + "ethers": "^6.13.2" }, "dependencies": { "big.js": "^6.2.1", - "ethers": "^6.13.2", "react": "^18.2.0", "react-circle-flags": "^0.0.18", "react-dom": "^18.2.0", diff --git a/src/components/Input.tsx b/src/components/Input.tsx index cacf6c70..75476493 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -1,35 +1,33 @@ import { InputAdornment, Typography } from '@mui/material' import TextField from '@mui/material/TextField' // Import TextField if using Material-UI -import React from 'react' +import React, { useMemo } from 'react' +import { actionTypes, usePartnerConfigurationContext } from '../helpers/partnerConfigurationContext' -const Input = ({ - label, - value, - onChange, - placeholder, - type = 'text', - fullWidth = false, - variant = 'outlined', - error = true, - helperText = '', - endIcon = null, - ...rest -}) => { +const Input = ({ variant = 'outlined', ...rest }) => { + const { state, dispatch } = usePartnerConfigurationContext() + const error = useMemo(() => { + if (!state.balance) return true + let balance = parseFloat(state.balance) + if (balance < 100) return true + else return false + }, [state]) return ( { + dispatch({ + type: actionTypes.UPDATE_BALANCE, + payload: { + newValue: e.target.value, + }, + }) + }} + type="number" error={error} - helperText={helperText} InputProps={{ sx: { '& input': { - fontSize: '16px', // Equivalent to Typography body2 + fontSize: '16px', }, }, startAdornment: ( @@ -40,10 +38,10 @@ const Input = ({ color: theme => theme.palette.text.primary, }} > - C-Chain Balance: + Prefund Amount: ), - endAdornment: true ? ( + endAdornment: error ? ( children: React.ReactNode style?: React.CSSProperties + disabled?: boolean + loading?: boolean + endIcon?: any }) { return ( diff --git a/src/helpers/partnerConfigurationContext.tsx b/src/helpers/partnerConfigurationContext.tsx index c16d2561..a33601db 100644 --- a/src/helpers/partnerConfigurationContext.tsx +++ b/src/helpers/partnerConfigurationContext.tsx @@ -1,4 +1,7 @@ -import React, { createContext, useContext, useReducer } from 'react' +import { ethers } from 'ethers' +import React, { createContext, useContext, useEffect, useReducer } from 'react' +import { usePartnerConfig } from './usePartnerConfig' +import { useSmartContract } from './useSmartContract' export const services = [ { @@ -28,7 +31,7 @@ const initialState = { title: 'Messenger configuration', services: [], paragraph: - 'This Camino Messenger wizard will assist you in generating and activating your Camino Messenger address. Once the process is complete, your Camino Messenger address will appear on your partner detail page, allowing you to communicate directly with other Camino Messenger accounts.', + 'This wizard will help you create and activate your Camino Messenger Account with Camino Messenger address. Once set up, your Camino Messenger address will be displayed on your partner detail page, enabling direct communication with other Camino Messenger users.', distributor: { isDistributor: true, services: [] }, }, { @@ -36,7 +39,7 @@ const initialState = { title: 'Supplier configuration', type: 'supplier', paragraph: - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Donec sociis natoque penatibus.', + 'Configure services, set fees, and add specific details. Customize your setup by selecting or removing services below.', isSupplier: false, services: [], }, @@ -45,7 +48,7 @@ const initialState = { type: 'distributor', title: 'Distributor configuration', paragraph: - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Donec sociis natoque penatibus.', + 'Configure wanted services. Customize your setup by selecting or removing services below.', services: [], isDistributor: false, }, @@ -57,12 +60,17 @@ const initialState = { 'Please take a moment to review the data we got from you and check if everything is correct. If needed you can change them now or later in your Camino Messenger preferences', }, ], + balance: '0', step: 0, + isAbleToCreateCMAccount: false, + registredServices: [], } // Action types export const actionTypes = { ADD_SERVICE: 'ADD_SERVICE', + UPDATE_SUPPORTED_SERVICES: 'UPDATE_SUPPORTED_SERVICES', + UPDATE_WANTED_SERVICES: 'UPDATE_WANTED_SERVICES', REMOVE_SERVICE: 'REMOVE_SERVICE', UPDATE_STEP: 'UPDATE_STEP', UPDATE_IS_SUPPLIER: 'UPDATE_IS_SUPPLIER', @@ -72,10 +80,14 @@ export const actionTypes = { UPDATE_FEE: 'UPDATE_FEE', UPDATE_IS_OFF_CHAIN: 'UPDATE_IS_OFF_CHAIN', UPDATE_IS_CAM: 'UPDATE_IS_CAM', + UPDATE_IS_ABLE_TO_CREATE_MESSENGER_ACCOUNT: 'UPDATE_IS_ABLE_TO_CREATE_MESSENGER_ACCOUNT', + GET_ALL_REGISTRED_SERVICES: 'GET_ALL_REGISTRED_SERVICES', + UPDATE_BALANCE: 'UPDATE_BALANCE', + RESET_STATE: 'RESET_STATE', } // Reducer function -function reducer(state = initialState, action) { +export function reducer(state = initialState, action) { switch (action.type) { case actionTypes.ADD_SERVICE: { const { step, newService } = action.payload @@ -91,6 +103,53 @@ function reducer(state = initialState, action) { ), } } + case actionTypes.RESET_STATE: + const { initialState } = action.payload + return { ...state, ...initialState } + case actionTypes.UPDATE_SUPPORTED_SERVICES: { + const { services } = action.payload + if (!services || services.length < 1) return state + let parsedServices = services[0].map((service, index) => { + return { + name: service, + fee: ethers.formatEther(services[1][index][0]), + rackRates: services[1][index][1], + capabilities: services[1][index][2], + } + }) + return { + ...state, + stepsConfig: state.stepsConfig.map(item => + item.step === 1 + ? { + ...item, + isSupplier: parsedServices.length > 0 ? true : false, + services: parsedServices, + } + : item, + ), + } + } + + case actionTypes.UPDATE_WANTED_SERVICES: { + const { wantedServices } = action.payload + console.log({ wantedServices }) + if (!wantedServices || wantedServices.length < 1) return state + return { + ...state, + stepsConfig: state.stepsConfig.map(item => + item.step === 2 + ? { + ...item, + isDistributor: wantedServices.length > 0 ? true : false, + services: wantedServices.map(elem => { + return { name: elem } + }), + } + : item, + ), + } + } case actionTypes.REMOVE_SERVICE: { const { step, serviceIndex } = action.payload @@ -107,6 +166,14 @@ function reducer(state = initialState, action) { } } + case actionTypes.UPDATE_IS_ABLE_TO_CREATE_MESSENGER_ACCOUNT: { + const { isAbleToCreateCMAccount } = action.payload + return { + ...state, + isAbleToCreateCMAccount, + } + } + case actionTypes.UPDATE_STEP: { const { step } = action.payload return { @@ -129,6 +196,14 @@ function reducer(state = initialState, action) { } } + case actionTypes.GET_ALL_REGISTRED_SERVICES: { + const { registredServices } = action.payload + return { + ...state, + registredServices, + } + } + case actionTypes.UPDATE_IS_CAM: { return { ...state, @@ -144,24 +219,31 @@ function reducer(state = initialState, action) { } case actionTypes.UPDATE_IS_SUPPLIER: { - if (state.step === 2) + if (state.step === 2) { return { ...state, stepsConfig: state.stepsConfig.map(item => item.step === state.step ? { ...item, + services: state.stepsConfig[state.step].isDistributor + ? [] + : item.services, isDistributor: !state.stepsConfig[state.step].isDistributor, } : item, ), } + } return { ...state, stepsConfig: state.stepsConfig.map(item => item.step === state.step ? { ...item, + services: state.stepsConfig[state.step].isSupplier + ? [] + : item.services, isSupplier: !state.stepsConfig[state.step].isSupplier, } : item, @@ -171,7 +253,6 @@ function reducer(state = initialState, action) { case actionTypes.UPDATE_RACK_RATES: { const { step, serviceIndex } = action.payload - console.log('dispatched') return { ...state, stepsConfig: state.stepsConfig.map(item => @@ -264,6 +345,13 @@ function reducer(state = initialState, action) { ), } } + case actionTypes.UPDATE_BALANCE: { + const { newValue } = action.payload + return { + ...state, + balance: newValue, + } + } default: return state @@ -276,6 +364,38 @@ const PartnerConfigContext = createContext() // Context provider component export const PartnerConfigurationProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState) + const partnerConfig = usePartnerConfig() + const { contractCMAccountAddress, accountReadContract } = useSmartContract() + + useEffect(() => { + if (partnerConfig.account) { + partnerConfig.getAllServices().then(result => { + dispatch({ + type: actionTypes.GET_ALL_REGISTRED_SERVICES, + payload: { + registredServices: result, + }, + }) + }) + } + }, [partnerConfig.account]) + + useEffect(() => { + if (accountReadContract) { + partnerConfig.getSupportedServices().then(res => { + dispatch({ + type: actionTypes.UPDATE_SUPPORTED_SERVICES, + payload: { services: res }, + }) + }) + partnerConfig.getWantedServices().then(res => { + dispatch({ + type: actionTypes.UPDATE_WANTED_SERVICES, + payload: { wantedServices: res }, + }) + }) + } + }, [accountReadContract]) return ( diff --git a/src/helpers/usePartnerConfig.tsx b/src/helpers/usePartnerConfig.tsx index 86807052..2997b168 100644 --- a/src/helpers/usePartnerConfig.tsx +++ b/src/helpers/usePartnerConfig.tsx @@ -1,4 +1,7 @@ -import { useCallback } from 'react' +import { ethers } from 'ethers' +import { useCallback, useEffect } from 'react' +import { useAppDispatch } from '../hooks/reduxHooks' +import { updateCMAcocuntContract } from '../redux/slices/partner' import { useSmartContract } from './useSmartContract' export const usePartnerConfig = () => { @@ -7,229 +10,293 @@ export const usePartnerConfig = () => { writeToContract, account, managerWriteContract, + setContractCMAccountAddress, accountWriteContract, managerReadContract, wallet, + CMAccountCreated, + accountReadContract, } = useSmartContract() - const CMAccountAddress = '0x5ce952E7D9c42b083ee92b0678cFEF25B984e0e2' + const dispatch = useAppDispatch() - const getAllServices = useCallback(async () => { + async function CreateConfiguration(state) { if (!account) { console.error('Account is not initialized') return } - try { - const services = await readFromContract('manager', 'getAllRegisteredServiceNames') - console.log({ service: services[0] }) - return services + let balance = ethers.parseEther(state.balance ? state.balance : '0') + let services = state.stepsConfig[1].services.map(elem => { + return { + ...elem, + fee: ethers.parseEther(elem.fee ? elem.fee : '0'), + capabilities: elem.capabilities.filter(item => item !== ''), + } + }) + let wantedServices = state.stepsConfig[2].services.map(elem => elem.name) + let isOffChainPayement = state.stepsConfig[3].isOffChain + let isCam = state.stepsConfig[3].isCam + + const tx = await writeToContract('manager', 'createCMAccount', account, account, { + value: balance, + }) + const event = tx.logs.find(log => { + try { + return managerWriteContract.interface.parseLog(log).name === 'CMAccountCreated' + } catch (e) { + return false + } + }) + + const parsedEvent = managerWriteContract.interface.parseLog(event) + const cmAccountAddress = parsedEvent.args.account + setContractCMAccountAddress(cmAccountAddress) + CMAccountCreated({ services, wantedServices, isOffChainPayement }, cmAccountAddress) + return tx } catch (error) { - console.error('Error checking CM account:', error) + console.error(error) throw error } - }, [account, readFromContract]) + } - const getSupportedServices = useCallback(async () => { + const getAllServices = useCallback(async () => { if (!account) { console.error('Account is not initialized') return } try { - const services = await readFromContract('account', 'getSupportedServices') + const services = await readFromContract('manager', 'getAllRegisteredServiceNames') return services } catch (error) { - console.error('Error checking CM account:', error) + console.error('Error getting All Services:', error) throw error } }, [account, readFromContract]) - const getWantedServices = useCallback(async () => { + const getSupportedServices = useCallback(async () => { if (!account) { console.error('Account is not initialized') return } try { - const services = await readFromContract('account', 'getWantedServices') + const services = await readFromContract('account', 'getSupportedServices') + // if (services && services.length >= 1) console.log({ services: services[1][0] }) return services } catch (error) { - console.error('Error checking CM account:', error) + console.error('Error getting Supported Services:', error) throw error } }, [account, readFromContract]) - const isCMAccount = useCallback(async () => { + const getWantedServices = useCallback(async () => { if (!account) { console.error('Account is not initialized') return } try { - const CMACCOUNT_ROLE = await readFromContract('manager', 'CMACCOUNT_ROLE') - const roleMemberCount = await readFromContract( - 'manager', - 'getRoleMemberCount', - CMACCOUNT_ROLE, - ) - // const creator1 = await readFromContract('manager', 'getRoleMember', 0) - const creator2 = await managerReadContract.getRoleMember(CMACCOUNT_ROLE, 0) - const creator = await readFromContract('manager', 'getCMAccountCreator', creator2) - console.log({ creator, creator2 }) - // const services = await readFromContract('manager', 'getAllRegisteredServiceNames') - // console.log({ creator, services }) - // const result = await readFromContract('manager', 'isCMAccount', address) - // return result - return 'done' + const wantedServices = await readFromContract('account', 'getWantedServices') + return wantedServices } catch (error) { - console.error('Error checking CM account:', error) + console.error('Error getting All Wanted Services:', error) throw error } }, [account, readFromContract]) - const createCMAccount = useCallback(async () => { - if (!account) { - console.error('Account is not initialized') - return - } + const getListOfBots = useCallback(async () => { try { - const prefundAmount = await readFromContract('manager', 'getPrefundAmount') - const tx = await writeToContract('manager', 'createCMAccount', account, account, { - value: prefundAmount, - }) - console.log({ tx }) - const event = tx.logs.find(log => { - try { - return managerWriteContract.interface.parseLog(log).name === 'CMAccountCreated' - } catch (e) { - return false - } - }) + const CHEQUE_OPERATOR_ROLE = await readFromContract('account', 'CHEQUE_OPERATOR_ROLE') + const roleMemberCount = await readFromContract( + 'account', + 'getRoleMemberCount', + CHEQUE_OPERATOR_ROLE, + ) - const parsedEvent = managerWriteContract.interface.parseLog(event) - const cmAccountAddress = parsedEvent.args.account - console.log({ parsedEvent, cmAccountAddress }) - return tx + const botPromises = [] + for (let i = 0; i < roleMemberCount; i++) { + botPromises.push(accountReadContract.getRoleMember(CHEQUE_OPERATOR_ROLE, i)) + } + + const bots = await Promise.all(botPromises) + return bots } catch (error) { - console.error(error) throw error } - }, [account, readFromContract, writeToContract]) + }, [account, managerReadContract]) - const grantRoles = useCallback(async () => { - if (!account) { - console.error('Account is not initialized') - return - } + const isCMAccount = useCallback(async () => { try { - const DEFAULT_ADMIN_ROLE = await readFromContract('manager', 'DEFAULT_ADMIN_ROLE') - const tx = await writeToContract('manager', 'grantRole', DEFAULT_ADMIN_ROLE, account) - const UPGRADER_ROLE = await readFromContract('manager', 'UPGRADER_ROLE') - const tx2 = await writeToContract('manager', 'grantRole', UPGRADER_ROLE, account) - const SERVICE_REGISTRY_ADMIN_ROLE = await readFromContract( - 'manager', - 'SERVICE_REGISTRY_ADMIN_ROLE', - ) - const tx3 = await writeToContract( + const CMACCOUNT_ROLE = await readFromContract('manager', 'CMACCOUNT_ROLE') + const roleMemberCount = await readFromContract( 'manager', - 'grantRole', - SERVICE_REGISTRY_ADMIN_ROLE, - account, + 'getRoleMemberCount', + CMACCOUNT_ROLE, ) - console.log({ tx, tx2, tx3 }) - return 'roles granted' - } catch (error) { - console.error(error) - throw error - } - }, [account, readFromContract, writeToContract]) - - const registerServices = useCallback(async () => { - if (!account) { - console.error('Account is not initialized') - return - } - try { - // Services to register - const serviceNames = [ - 'cmp.service.accommodation.v1.AccommodationSearchService', - 'cmp.service.accommodation.v2.AccommodationSearchService', - 'cmp.service.accommodation.v3.AccommodationSearchService', - 'cmp.service.accommodation.v4.AccommodationSearchService', - 'cmp.service.accommodation.v5.AccommodationSearchService', - 'cmp.service.accommodation.v6.AccommodationSearchService', - ] - - for (const serviceName of serviceNames) { - await writeToContract('manager', 'registerService', serviceName) + let i = 0 + while (i < roleMemberCount) { + managerReadContract.getRoleMember(CMACCOUNT_ROLE, i).then(async role => { + readFromContract('manager', 'getCMAccountCreator', role).then(creator => { + if (creator === wallet.address) { + setContractCMAccountAddress(role) + dispatch(updateCMAcocuntContract(role)) + } + }) + }) + i++ } - return 'done' + return } catch (error) { - console.error(error) throw error } - }, [account, writeToContract]) + }, [account, managerReadContract]) - const addServices = useCallback(async () => { - if (!account) { - console.error('Account is not initialized') - return - } - // const fee1 = 1000n - // const fee2 = 2000n - // const fee3 = 3000n + useEffect(() => { + isCMAccount() + }, [wallet]) - // const restrictedRate1 = false - // const restrictedRate2 = true - // const restrictedRate3 = false + const addServices = useCallback( + async services => { + if (!account) { + console.error('Account is not initialized') + return + } + try { + for (const service of services) { + console.log('Processing service:', service.name) + const tx = await accountWriteContract.addService( + service.name, + ethers.parseEther(service.fee ? service.fee : '0'), + service.rackRates, + service.capabilities.filter(item => item !== ''), + ) + console.log('Transaction sent:', tx.hash) + const receipt = await tx.wait() + console.log('Transaction mined:', receipt.transactionHash) + } + } catch (error) { + console.error(error) + throw error + } + }, + [account, writeToContract], + ) + const removeServices = useCallback( + async services => { + if (!account) { + console.error('Account is not initialized') + return + } + try { + for (const service of services) { + console.log('Processing service:', service.name) + const tx = await accountWriteContract.removeService(service.name) + console.log('Transaction sent:', tx.hash) + const receipt = await tx.wait() + console.log('Transaction mined:', receipt.transactionHash) + } + } catch (error) { + console.error(error) + throw error + } + }, + [account, writeToContract], + ) - // const capabilities1 = ['test capability 1'] - // const capabilities2 = ['test capability 2'] - // const capabilities3 = ['test capability 3'] - // const prefundAmount = await readFromContract('manager', 'getPrefundAmount') - const serviceName = (await getAllServices())[0] - try { - const tx = await accountWriteContract.addService(serviceName, 0, true, [ - 'test capability 1', - ]) - console.log({ tx }) - } catch (error) { - console.error(error) - throw error - } - }, [account, readFromContract, writeToContract]) + const addMessengerBot = useCallback( + async address => { + if (!account) { + console.error('Account is not initialized') + return + } + try { + const addMessengerBotTx = await accountWriteContract.addMessengerBot(address, 0n) + await addMessengerBotTx.wait() + console.log({ addMessengerBotTx }) + console.log({ addMessengerBotTx: 'done' }) + return addMessengerBotTx + } catch (error) { + console.error(error) + throw error + } + }, + [account, accountWriteContract], + ) - const addWantedServices = useCallback(async () => { - if (!account) { - console.error('Account is not initialized') - return - } - try { - const tx = await accountWriteContract.addWantedServices([ - 'cmp.services.info.v1.CountryEntryRequirementsService', - ]) - console.log({ tx }) - } catch (error) { - console.error(error) - throw error - } - }, [account, readFromContract, writeToContract]) + const removeMessengerBot = useCallback( + async address => { + if (!account) { + console.error('Account is not initialized') + return + } + try { + const removeMessengerBotTx = await accountWriteContract.removeMessengerBot(address) + await removeMessengerBotTx.wait() + console.log({ removeMessengerBotTx }) + console.log({ removeMessengerBotTx: 'done' }) + return removeMessengerBotTx + } catch (error) { + console.error(error) + throw error + } + }, + [account, accountWriteContract], + ) - const setOffChainPaymentSupported = useCallback(async () => { - if (!account) { - console.error('Account is not initialized') - return - } - try { - const tx = await accountWriteContract.setOffChainPaymentSupported(true) - console.log({ tx }) - } catch (error) { - console.error(error) - throw error - } - }, [account, readFromContract, writeToContract]) + const addWantedServices = useCallback( + async services => { + if (!account) { + console.error('Account is not initialized') + return + } + try { + const wantedServicesTx = await accountWriteContract.addWantedServices(services) + await wantedServicesTx.wait() + return wantedServicesTx + } catch (error) { + console.error(error) + throw error + } + }, + [account, accountWriteContract], + ) + const removeWantedServices = useCallback( + async services => { + if (!account) { + console.error('Account is not initialized') + return + } + try { + const wantedServicesTx = await accountWriteContract.removeWantedServices(services) + await wantedServicesTx.wait() + return wantedServicesTx + } catch (error) { + console.error(error) + throw error + } + }, + [account, writeToContract], + ) + + const setOffChainPaymentSupported = useCallback( + async value => { + if (!account) { + console.error('Account is not initialized') + return + } + try { + const tx = await accountWriteContract.setOffChainPaymentSupported(value) + return tx + } catch (error) { + console.error(error) + throw error + } + }, + [account, accountWriteContract], + ) const getOffChainPaymentSupported = useCallback(async () => { if (!account) { @@ -237,22 +304,40 @@ export const usePartnerConfig = () => { return } try { - const services = await readFromContract('account', 'offChainPaymentSupported') - return services + const offChainPaymentSupported = await readFromContract( + 'account', + 'offChainPaymentSupported', + ) + return offChainPaymentSupported } catch (error) { console.error(error) throw error } - }, [account, readFromContract, writeToContract]) + }, [account, readFromContract]) + + // useEffect(() => { + // if (account) { + // getOffChainPaymentSupported() + // getSupportedServices() + // getWantedServices() + // } + // }, [account]) return { + CreateConfiguration, + account, + removeWantedServices, + getOffChainPaymentSupported, + setOffChainPaymentSupported, + getWantedServices, addWantedServices, getSupportedServices, addServices, + removeServices, getAllServices, isCMAccount, - createCMAccount, - grantRoles, - registerServices, + addMessengerBot, + getListOfBots, + removeMessengerBot, } } diff --git a/src/helpers/useSmartContract.tsx b/src/helpers/useSmartContract.tsx index 991f7b72..ec61ab18 100644 --- a/src/helpers/useSmartContract.tsx +++ b/src/helpers/useSmartContract.tsx @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import React, { createContext, useContext, useEffect, useState } from 'react' +import React, { createContext, ReactNode, useContext, useEffect, useState } from 'react' import store from 'wallet/store' import { useAppSelector } from '../hooks/reduxHooks' import { getActiveNetwork } from '../redux/slices/network' @@ -10,7 +10,11 @@ const SmartContractContext = createContext(null) export const useSmartContract = () => useContext(SmartContractContext) -export const SmartContractProvider: React.FC = ({ children }) => { +type SmartContractProviderProps = { + children?: ReactNode +} + +export const SmartContractProvider: React.FC = ({ children }) => { const [provider, setProvider] = useState(null) const [managerReadContract, setManagerReadContract] = useState(null) const [managerWriteContract, setManagerWriteContract] = useState(null) @@ -18,19 +22,83 @@ export const SmartContractProvider: React.FC = ({ children }) => { const [accountWriteContract, setAccountWriteContract] = useState(null) const [wallet, setWallet] = useState(null) const [account, setAccount] = useState(null) - // let web3Provider = `${net.protocol}://${net.ip}:${net.port}/ext/bc/C/rpc` + const [contractCMAccountAddress, setContractCMAccountAddress] = useState('') + const auth = useAppSelector(state => state.appConfig.isAuth) + const contractCMAccountManagerAddress = '0xE5B2f76C778D082b07BDd7D51FFe83E3E055B47F' + + const CMAccountCreated = async (state, cmAccountAddress) => { + const accountWritableContract = new ethers.Contract(cmAccountAddress, CMAccount, wallet) + const accountReadOnlyContract = new ethers.Contract(cmAccountAddress, CMAccount, provider) + setContractCMAccountAddress(cmAccountAddress) + setAccountReadContract(accountReadOnlyContract) + setAccountWriteContract(accountWritableContract) + if (!account) { + console.error('Account is not initialized') + return { success: false, error: 'Account is not initialized' } + } + try { + console.log('Starting to add services:', state.services.length) + for (const service of state.services) { + console.log('Processing service:', service.name) + const tx = await accountWritableContract.addService( + service.name, + parseInt(service.fee), + service.rackRates, + service.capabilities, + ) + console.log('Transaction sent:', tx.hash) + const receipt = await tx.wait() + console.log('Transaction mined:', receipt.transactionHash) + } + + console.log('Adding wanted services') + if (state.wantedServices.length > 0) { + const wantedServicesTx = await accountWritableContract.addWantedServices( + state.wantedServices, + ) + await wantedServicesTx.wait() + } + + console.log('Setting off-chain payment support') + const offChainPaymentTx = await accountWritableContract.setOffChainPaymentSupported( + state.isOffChainPayement, + ) + await offChainPaymentTx.wait() + + console.log('All operations completed successfully') + return { success: true, message: 'All operations completed successfully' } + } catch (error) { + console.error('Error in CMAccountCreated:', error) + return { success: false, error: error.message } + } + } + + const initializeCMAccountContract = async () => { + if (!provider) return + try { + const accountWritableContract = new ethers.Contract( + contractCMAccountAddress, + CMAccount, + wallet, + ) + const accountReadOnlyContract = new ethers.Contract( + contractCMAccountAddress, + CMAccount, + provider, + ) + setAccountReadContract(accountReadOnlyContract) + setAccountWriteContract(accountWritableContract) + } catch (error) { + console.error('User denied account access:', error) + } + } const initializeEthers = async () => { const selectedNetwork = store.getters['Network/selectedNetwork'] - // const ethersProvider = new ethers.BrowserProvider(window.ethereum) const ethersProvider = new ethers.JsonRpcProvider( `${selectedNetwork.protocol}://${selectedNetwork.ip}:${selectedNetwork.port}/ext/bc/C/rpc`, ) try { const wallet = new ethers.Wallet(store.state.activeWallet?.ethKey, ethersProvider) - const contractCMAccountManagerAddress = '0xE5B2f76C778D082b07BDd7D51FFe83E3E055B47F' - const contractCMAccountAddress = '0xCB43DabB6d579d5a6067aB06575601D3D77882CA' - - // Initialize CMAccountManager contracts const managerWritableContract = new ethers.Contract( contractCMAccountManagerAddress, CMAccountManager.abi, @@ -41,25 +109,10 @@ export const SmartContractProvider: React.FC = ({ children }) => { CMAccountManager.abi, ethersProvider, ) - - // Initialize CMAccount contracts - const accountWritableContract = new ethers.Contract( - contractCMAccountAddress, - CMAccount, - wallet, - ) - console.log({ accountWritableContract: accountWritableContract.interface }) - const accountReadOnlyContract = new ethers.Contract( - contractCMAccountAddress, - CMAccount, - ethersProvider, - ) setWallet(wallet) setProvider(ethersProvider) setManagerReadContract(managerReadOnlyContract) setManagerWriteContract(managerWritableContract) - setAccountReadContract(accountReadOnlyContract) - setAccountWriteContract(accountWritableContract) setAccount(wallet.address) } catch (error) { console.error('User denied account access:', error) @@ -68,9 +121,12 @@ export const SmartContractProvider: React.FC = ({ children }) => { const activeNetwork = useAppSelector(getActiveNetwork) useEffect(() => { - // console.log({ store: store.state.activeWallet?.ethKey }) - initializeEthers() - }, [activeNetwork]) + if (auth) initializeEthers() + }, [activeNetwork, auth]) + + useEffect(() => { + if (contractCMAccountAddress) initializeCMAccountContract() + }, [provider, contractCMAccountAddress]) const readFromContract = async ( contractType: 'manager' | 'account', @@ -79,7 +135,6 @@ export const SmartContractProvider: React.FC = ({ children }) => { ) => { const contract = contractType === 'manager' ? managerReadContract : accountReadContract if (!contract) { - console.error(`${contractType} read contract is not initialized`) return } @@ -99,7 +154,6 @@ export const SmartContractProvider: React.FC = ({ children }) => { ) => { const contract = contractType === 'manager' ? managerWriteContract : accountWriteContract if (!contract) { - console.error(`${contractType} write contract is not initialized`) return } @@ -114,6 +168,8 @@ export const SmartContractProvider: React.FC = ({ children }) => { } const value = { + contractCMAccountAddress, + setContractCMAccountAddress, wallet, provider, managerReadContract, @@ -123,6 +179,7 @@ export const SmartContractProvider: React.FC = ({ children }) => { account, readFromContract, writeToContract, + CMAccountCreated, } return {children} diff --git a/src/helpers/useWalletBalance.tsx b/src/helpers/useWalletBalance.tsx new file mode 100644 index 00000000..80fd289c --- /dev/null +++ b/src/helpers/useWalletBalance.tsx @@ -0,0 +1,38 @@ +import { ethers } from 'ethers' +import { useEffect, useState } from 'react' +import store from 'wallet/store' +import { useSmartContract } from './useSmartContract' +const useWalletBalance = () => { + const [balance, setBalance] = useState('') + const [balanceOfAnAddress, setBalanceOfAnAddress] = useState('') + const [error, setError] = useState(null) + const { provider } = useSmartContract() + + async function getBalanceOfAnAddress(address: string) { + const fetchedBalance = await provider.getBalance(address) + setBalanceOfAnAddress(ethers.formatEther(fetchedBalance)) + } + useEffect(() => { + const fetchBalance = async () => { + if (!provider) { + setBalance(null) + return + } + + try { + const fetchedBalance = await provider.getBalance( + '0x' + store.state.activeWallet.ethAddress, + ) + setBalance(ethers.formatEther(fetchedBalance)) + } catch (err) { + console.error('Error fetching balance:', err) + setError(err.message) + } + } + + fetchBalance() + }, [provider]) + return { balance, error, balanceOfAnAddress, getBalanceOfAnAddress } +} + +export default useWalletBalance diff --git a/src/layout/PartnersLayout.tsx b/src/layout/PartnersLayout.tsx index 1f92c8a3..e865b67b 100644 --- a/src/layout/PartnersLayout.tsx +++ b/src/layout/PartnersLayout.tsx @@ -1,76 +1,130 @@ -import { Box, Toolbar } from '@mui/material' +import { Box, Toolbar, Typography } from '@mui/material' -import React from 'react' -import { Outlet } from 'react-router' +import React, { useEffect } from 'react' +import { Navigate, Outlet, useNavigate } from 'react-router' +import store from 'wallet/store' +import { PartnerConfigurationProvider } from '../helpers/partnerConfigurationContext' import { SmartContractProvider } from '../helpers/useSmartContract' +import { useAppSelector } from '../hooks/reduxHooks' +import { useIsPartnerQuery } from '../redux/services/partners' +import { getWalletName } from '../redux/slices/app-config' import Links from '../views/settings/Links' +const ClaimProfile = () => { + return ( + + Claim a profile + + Get in touch with the Camino Foundation to claim a Partner page and be then be able + to edit it and to create/manage a Camino Messenger configuration + + + ) +} + const PartnersLayout = () => { const path = window.location.pathname + const { data, isLoading } = useIsPartnerQuery({ + // pChainAddress: 'P-camino1g65uqn6t77p656w64023nh8nd9updzmxxmmdhy', + pChainAddress: 'P-camino1avxc8uyqzf9hzwa9eacgg2x8y473j85sd2jwdt', + }) + const walletName = useAppSelector(getWalletName) + const navigate = useNavigate() + useEffect(() => { + if ( + walletName && + path.includes('partners/messenger-configuration') && + store.state.activeWallet?.type === 'multisig' + ) { + navigate('/') + } + }, [walletName]) + if (isLoading) return <> + if (path.includes('partners/messenger-configuration') && !store.state.isAuth) { + return + } return ( - - theme.palette.background.paper, - flexGrow: 1, - p: '1.5rem', - zIndex: 9, - position: 'fixed', - top: '65px', - width: '100vw', - height: '61px', - display: 'flex', - justifyContent: 'center', - right: 0, - }} - > - - - {/* {path.includes('partners/messenger-configuration') && ( - theme.palette.background.paper, - flexGrow: 1, - p: '1.5rem', - zIndex: 9, - position: 'fixed', - top: '129px', - width: '100vw', - height: '61px', - display: 'flex', - justifyContent: 'center', - right: 0, - }} - > - - - )} */} + theme.customWidth.layoutMaxWitdh, - mb: '2rem', + height: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', }} > - + theme.palette.background.paper, + flexGrow: 1, + p: '1.5rem', + zIndex: 9, + position: 'fixed', + top: '65px', + width: '100vw', + height: '61px', + display: 'flex', + justifyContent: 'center', + right: 0, + }} + > + + + {path.includes('partners/messenger-configuration') && !!data && ( + theme.palette.background.paper, + flexGrow: 1, + p: '1.5rem', + zIndex: 9, + position: 'fixed', + top: '129px', + width: '100vw', + height: '61px', + display: 'flex', + justifyContent: 'center', + right: 0, + }} + > + + + )} + theme.customWidth.layoutMaxWitdh, + mb: '2rem', + }} + > + {!path.includes('partners/messenger-configuration') || + (path.includes('partners/messenger-configuration') && !!data) ? ( + + ) : ( + + )} + - + ) } diff --git a/src/layout/RoutesSuite.tsx b/src/layout/RoutesSuite.tsx index a09e6b76..f9eb1f11 100644 --- a/src/layout/RoutesSuite.tsx +++ b/src/layout/RoutesSuite.tsx @@ -13,9 +13,12 @@ import ExplorerApp from '../views/explorer/ExplorerApp' import LandingPage from '../views/landing/LandingPage' import LoginPage from '../views/login/LoginPage' import Partners from '../views/partners' +import ConfigurDistrubitor from '../views/partners/ConfigurDistrubitor' +import ConfigurSupplier from '../views/partners/ConfigurSupplier' import Overreview from '../views/partners/Configuration' import CreatedOffers from '../views/partners/CreatedOffers' import Foundation from '../views/partners/Foundation' +import ManageBots from '../views/partners/ManageBots' import Partner from '../views/partners/Partner' import MultisigWallet from '../views/settings/MultisigWallet' import VerifyWallet from '../views/settings/VerifyWallet' @@ -110,11 +113,13 @@ export default function RoutesSuite() { }> } /> + }> } /> - } /> - distribution} /> - Supply} /> + } /> + } /> + } /> + } /> }> diff --git a/src/redux/services/partners.ts b/src/redux/services/partners.ts index 50a9de24..d19509f6 100644 --- a/src/redux/services/partners.ts +++ b/src/redux/services/partners.ts @@ -45,7 +45,18 @@ export const partnersApi = createApi({ return response.data[0] }, }), + isPartner: build.query({ + query: ({ pChainAddress }) => { + let query = + '?populate=*&sort[0]=companyName:asc&pagination[page]=1&pagination[pageSize]=12' + query += `&filters[pChainAddress][$contains]=${pChainAddress}` + return query + }, + transformResponse: (response: PartnersResponseType) => { + return response.data[0] + }, + }), }), }) -export const { useListPartnersQuery, useFetchPartnerDataQuery } = partnersApi +export const { useListPartnersQuery, useFetchPartnerDataQuery, useIsPartnerQuery } = partnersApi diff --git a/src/redux/slices/partner.ts b/src/redux/slices/partner.ts new file mode 100644 index 00000000..8d986aeb --- /dev/null +++ b/src/redux/slices/partner.ts @@ -0,0 +1,60 @@ +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' +import { ethers } from 'ethers' +import CMAccount from '../../helpers/CMAccountManagerModule#CMAccount.json' +interface InitialStatePartner { + CMAccountAddress: string + isCMAccountCreated: boolean + services: string[] + wantedServices: string[] + offChainPayement: boolean + accountWritableContract: ethers.Contract | null + accountReadContract: ethers.Contract | null +} + +let initialState: InitialStatePartner = { + CMAccountAddress: '', + isCMAccountCreated: false, + services: [], + wantedServices: [], + offChainPayement: false, + accountReadContract: null, + accountWritableContract: null, +} + +export const updateCMAcocuntContract = createAsyncThunk( + 'partner/updateCMAccountContract', + async ({ + contractCMAccountAddress, + wallet, + provider, + }: { + contractCMAccountAddress: string + wallet: ethers.Wallet + provider: ethers.Provider + }) => { + const accountWritableContract = new ethers.Contract( + contractCMAccountAddress, + CMAccount, + wallet, + ) + const accountReadOnlyContract = new ethers.Contract( + contractCMAccountAddress, + CMAccount, + provider, + ) + }, +) + +const partnerSlice = createSlice({ + name: 'partner', + initialState, + reducers: { + initializeCMAccountContract: (state, { payload }) => { + console.log(payload) + }, + }, + extraReducers: builder => {}, +}) + +export const {} = partnerSlice.actions +export default partnerSlice.reducer diff --git a/src/redux/store.ts b/src/redux/store.ts index 421f2315..56644944 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,8 +1,9 @@ -import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit' +import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit' +import { partnersApi } from './services/partners' import appConfigReducer from './slices/app-config' -import themeReducer from './slices/theme' import network from './slices/network' -import { partnersApi } from './services/partners' +import partner from './slices/partner' +import themeReducer from './slices/theme' export type AppDispatch = typeof store.dispatch export type RootState = ReturnType export type AppThunk = ThunkAction< @@ -18,6 +19,7 @@ export function configureAppStore() { appConfig: appConfigReducer, theme: themeReducer, network: network, + partner: partner, [partnersApi.reducerPath]: partnersApi.reducer, }, middleware: getDefaultMiddleware => diff --git a/src/views/partners/ConfigurDistrubitor.tsx b/src/views/partners/ConfigurDistrubitor.tsx new file mode 100644 index 00000000..3176732f --- /dev/null +++ b/src/views/partners/ConfigurDistrubitor.tsx @@ -0,0 +1,273 @@ +import { + Box, + Checkbox, + Divider, + FormControl, + FormControlLabel, + MenuItem, + Select, + Typography, +} from '@mui/material' +import React, { useEffect, useReducer, useState } from 'react' +import MainButton from '../../components/MainButton' +import { + actionTypes, + reducer, + usePartnerConfigurationContext, +} from '../../helpers/partnerConfigurationContext' +import { usePartnerConfig } from '../../helpers/usePartnerConfig' +import { Configuration } from './Configuration' + +const ConfigurDistrubitor = () => { + const { removeWantedServices, addWantedServices, getWantedServices } = usePartnerConfig() + const { state, dispatch } = usePartnerConfigurationContext() + const [distrubitorState, dispatchDistrubitorState] = useReducer(reducer, { ...state, step: 2 }) + const [editing, setEditing] = useState(false) + const [loading, setLoading] = useState(false) + + // useEffect(() => {}) + const handleChange = event => { + addService(event.target.value) + } + const addService = service => { + if ( + !distrubitorState.stepsConfig[distrubitorState.step].services.find( + elem => elem.name === service, + ) + ) + dispatchDistrubitorState({ + type: actionTypes.ADD_SERVICE, + payload: { + step: distrubitorState.step, + newService: { + name: distrubitorState.registredServices.find(elem => elem === service), + fee: '0', + capabilities: [''], + rackRates: true, + }, + }, + }) + } + + async function confirmEditing() { + setLoading(true) + const originalNames = new Set(state.stepsConfig[2].services.map(item => item.name)) + const updatedNames = new Set( + distrubitorState.stepsConfig[2].services.map(item => item.name), + ) + + const removed = state.stepsConfig[2].services.filter(item => !updatedNames.has(item.name)) + const added = distrubitorState.stepsConfig[2].services.filter( + item => !originalNames.has(item.name), + ) + await removeWantedServices(removed.map(elem => elem.name)) + await addWantedServices(added.map(elem => elem.name)) + let res = await getWantedServices() + dispatch({ + type: actionTypes.UPDATE_WANTED_SERVICES, + payload: { wantedServices: res }, + }) + setLoading(false) + setEditing(false) + console.log({ + removed, + added, + }) + // setEditing(false) + } + + function cancelEditing() { + dispatchDistrubitorState({ + type: actionTypes.RESET_STATE, + payload: { initialState: { ...state, step: 2 } }, + }) + console.log({ state: distrubitorState.stepsConfig[2].services }) + setEditing(false) + } + useEffect(() => { + dispatchDistrubitorState({ + type: actionTypes.RESET_STATE, + payload: { initialState: { ...state, step: 2 } }, + }) + }, [state]) + return ( + + + Messenger setup + {state.stepsConfig[2].title} + {state.stepsConfig[2].paragraph && ( + + {state.stepsConfig[2].paragraph} + + )} + + + Are you a {state.stepsConfig[2].type}? + + } + control={ + theme.palette.secondary.main, + '&.Mui-checked': { + color: theme => theme.palette.secondary.main, + }, + '&.MuiCheckbox-colorSecondary.Mui-checked': { + color: theme => theme.palette.secondary.main, + }, + }} + checked={distrubitorState.stepsConfig[2].isDistributor} + onChange={() => + dispatchDistrubitorState({ + type: actionTypes.UPDATE_IS_SUPPLIER, + }) + } + /> + } + /> + {distrubitorState.stepsConfig[distrubitorState.step].isDistributor && ( + + services + + + + + )} + + + + + {!editing ? ( + { + setEditing(true) + }} + > + Edit Distributor Configuration + + ) : ( + <> + { + cancelEditing() + }} + > + Cancel Editing + + { + confirmEditing() + }} + > + Confirm Editing + + + )} + + + + + + + ) +} + +export default ConfigurDistrubitor diff --git a/src/views/partners/ConfigurSupplier.tsx b/src/views/partners/ConfigurSupplier.tsx new file mode 100644 index 00000000..01bb1114 --- /dev/null +++ b/src/views/partners/ConfigurSupplier.tsx @@ -0,0 +1,290 @@ +import { + Box, + Checkbox, + Divider, + FormControl, + FormControlLabel, + MenuItem, + Select, + Typography, +} from '@mui/material' +import React, { useEffect, useReducer, useState } from 'react' +import MainButton from '../../components/MainButton' +import { + actionTypes, + reducer, + usePartnerConfigurationContext, +} from '../../helpers/partnerConfigurationContext' +import { usePartnerConfig } from '../../helpers/usePartnerConfig' +import { Configuration } from './Configuration' + +const ConfigurSupplier = () => { + const { state, dispatch } = usePartnerConfigurationContext() + const [supplierState, dispatchSupplierState] = useReducer(reducer, { ...state, step: 1 }) + const [editing, setEditing] = useState(false) + const [loading, setLoading] = useState(false) + const { removeServices, addServices, getSupportedServices } = usePartnerConfig() + const handleChange = event => { + addService(event.target.value) + } + const addService = service => { + if ( + !supplierState.stepsConfig[supplierState.step].services.find( + elem => elem.name === service, + ) + ) + dispatchSupplierState({ + type: actionTypes.ADD_SERVICE, + payload: { + step: supplierState.step, + newService: { + name: supplierState.registredServices.find(elem => elem === service), + fee: '0', + capabilities: [''], + rackRates: true, + }, + }, + }) + } + const isEqual = (a, b) => { + return ( + a.name === b.name && + a.fee === b.fee && + a.rackRates === b.rackRates && + JSON.stringify(a.capabilities) === JSON.stringify(b.capabilities) + ) + } + async function confirmEditing() { + setLoading(true) + const removed = state.stepsConfig[supplierState.step].services.filter( + origItem => + !supplierState.stepsConfig[supplierState.step].services.some(updatedItem => + isEqual(origItem, updatedItem), + ), + ) + + const added = supplierState.stepsConfig[supplierState.step].services.filter( + updatedItem => + !state.stepsConfig[supplierState.step].services.some(origItem => + isEqual(origItem, updatedItem), + ), + ) + console.log({ removed, added }) + await removeServices(removed) + await addServices(added) + let res = await getSupportedServices() + dispatch({ + type: actionTypes.UPDATE_SUPPORTED_SERVICES, + payload: { services: res }, + }) + setLoading(false) + setEditing(false) + // const originalNames = new Set(state.stepsConfig[2].services.map(item => item.name)) + // const updatedNames = new Set( + // distrubitorState.stepsConfig[2].services.map(item => item.name), + // ) + // const removed = state.stepsConfig[2].services.filter(item => !updatedNames.has(item.name)) + // const added = distrubitorState.stepsConfig[2].services.filter( + // item => !originalNames.has(item.name), + // ) + // console.log({ + // removed, + // added, + // }) + // setEditing(false) + } + + function cancelEditing() { + dispatchSupplierState({ + type: actionTypes.RESET_STATE, + payload: { initialState: { ...state, step: 1 } }, + }) + console.log({ state: supplierState.stepsConfig[1].services }) + setEditing(false) + } + + useEffect(() => { + dispatchSupplierState({ + type: actionTypes.RESET_STATE, + payload: { initialState: { ...state, step: 1 } }, + }) + }, [state]) + + return ( + + + Messenger setup + {state.stepsConfig[1].title} + {state.stepsConfig[1].paragraph && ( + + {state.stepsConfig[1].paragraph} + + )} + + + Are you a {state.stepsConfig[1].type}? + + } + control={ + theme.palette.secondary.main, + '&.Mui-checked': { + color: theme => theme.palette.secondary.main, + }, + '&.MuiCheckbox-colorSecondary.Mui-checked': { + color: theme => theme.palette.secondary.main, + }, + }} + checked={supplierState.stepsConfig[1].isSupplier} + onChange={() => + dispatchSupplierState({ type: actionTypes.UPDATE_IS_SUPPLIER }) + } + /> + } + /> + {supplierState.stepsConfig[supplierState.step].isSupplier && ( + + services + + + + + )} + + + + + {!editing ? ( + { + setEditing(true) + }} + > + Edit Supplier Configuration + + ) : ( + <> + { + cancelEditing() + }} + > + Cancel Editing + + { + confirmEditing() + }} + > + Confirm Editing + + + )} + + + + + + + ) +} + +export default ConfigurSupplier diff --git a/src/views/partners/Configuration.tsx b/src/views/partners/Configuration.tsx index 31b4d1e3..40d570ba 100644 --- a/src/views/partners/Configuration.tsx +++ b/src/views/partners/Configuration.tsx @@ -1,3 +1,4 @@ +import { ContentCopy, RefreshOutlined } from '@mui/icons-material' import { Box, Button, @@ -5,97 +6,216 @@ import { Divider, FormControl, FormControlLabel, + InputAdornment, MenuItem, Select, TextField, Typography, } from '@mui/material' import React, { useEffect } from 'react' +import store from 'wallet/store' import Alert from '../../components/Alert' import Input from '../../components/Input' import MainButton from '../../components/MainButton' import { actionTypes, - PartnerConfigurationProvider, - services, usePartnerConfigurationContext, } from '../../helpers/partnerConfigurationContext' import { usePartnerConfig } from '../../helpers/usePartnerConfig' +import { useSmartContract } from '../../helpers/useSmartContract' +import useWalletBalance from '../../helpers/useWalletBalance' const Content = () => { + const { contractCMAccountAddress } = useSmartContract() const { state, dispatch } = usePartnerConfigurationContext() - const handleChange = event => { addService(event.target.value) } const partnerConfig = usePartnerConfig() const nextStep = () => { - /* grant roles */ - // partnerConfig - // .grantRoles() - // .then(result => console.log(result)) - // .catch(err => console.log(err)) - /* create CMAccount */ - // partnerConfig - // .createCMAccount() - // .then(result => console.log(result)) - // .catch(err => console.log(err)) - /* register Services */ - // partnerConfig - // .registerServices() - // .then(result => console.log(result)) - // .catch(err => console.log(err)) - /* get all services */ - // partnerConfig - // .getAllServices() - // .then(result => console.log(result)) - // .catch(err => console.log(err)) - /* add service */ - // partnerConfig - // .addServices() - // .then(result => console.log(result)) - // .catch(err => console.log(err)) - /* add wanted services */ - partnerConfig - .addWantedServices() - .then(result => console.log(result)) - .catch(err => console.log(err)) - /* check is cm account */ - // partnerConfig - // .isCMAccount() - // .then(result => console.log(result)) - // .catch(err => console.log(err)) - /* get all supported services */ - // partnerConfig - // .getSupportedServices() - // .then(result => console.log({ supportedServices: result })) - // .catch(err => console.log(err)) - //old - // console.log('click') - // if (state.step < state.stepsConfig.length - 1) - // dispatch({ type: actionTypes.UPDATE_STEP, payload: { step: state.step + 1 } }) + if (state.step < state.stepsConfig.length - 1) + dispatch({ type: actionTypes.UPDATE_STEP, payload: { step: state.step + 1 } }) } const prevStep = () => { - console.log('click') if (state.step > 0) dispatch({ type: actionTypes.UPDATE_STEP, payload: { step: state.step - 1 } }) } - const addService = service => { if (!state.stepsConfig[state.step].services.find(elem => elem.name === service)) dispatch({ type: actionTypes.ADD_SERVICE, payload: { step: state.step, - newService: services.find(elem => elem.name === service), + newService: { + name: state.registredServices.find(elem => elem === service), + fee: '0', + capabilities: [''], + rackRates: true, + }, }, }) } - function CreateConfiguration() { - console.log({ state }) + function submit() { + partnerConfig.CreateConfiguration(state) } + const { balance, getBalanceOfAnAddress, balanceOfAnAddress } = useWalletBalance() + useEffect(() => { + if (contractCMAccountAddress) getBalanceOfAnAddress(contractCMAccountAddress) + }, [contractCMAccountAddress]) + if (contractCMAccountAddress) + return ( + <> + + + Messenger setup + Add funds to CM address + + First you need to top up the CM Account with CAM, EURSH or USDC to work + properly. Transfer it to the newly generated CM address below. + + theme.palette.text.primary, + WebkitTextFillColor: theme => theme.palette.text.primary, + }, + }, + endAdornment: ( + + `${theme.palette.text.primary} !important`, + }} + /> + } + variant="outlined" + onClick={() => { + navigator.clipboard.writeText(contractCMAccountAddress) + }} + > + Copy + + ), + }} + /> + + When the necessary balance has arrived you can continue to the next + step. + + + theme.palette.text.primary, + WebkitTextFillColor: theme => theme.palette.text.primary, + }, + }, + startAdornment: ( + theme.palette.text.primary, + }} + > + CM Balance: + + ), + endAdornment: ( + + {balanceOfAnAddress < 100 ? ( + + + + + + ) : ( + + + + + + )} + + `${theme.palette.text.primary} !important`, + }} + /> + } + variant="outlined" + onClick={() => { + getBalanceOfAnAddress(contractCMAccountAddress) + }} + > + Refresh + + + ), + }} + /> + {balanceOfAnAddress !== '' && parseFloat(balanceOfAnAddress) < 100 && ( + + )} + + + + + + + ) return ( { )} {state.step === 0 && ( <> - - + + {balance !== '' && parseFloat(balance) < 100 && ( + + )} + {!store.getters['Accounts/kycStatus'] && ( + + )} )} {(state.step === 1 || state.step === 2) && ( @@ -163,20 +288,53 @@ const Content = () => { lineHeight: '20px', textAlign: 'left', height: '40px', - padding: '10px 16px', + // padding: '10px 16px', gap: '4px', borderRadius: '8px', border: '1px solid transparent', borderBottom: 'none', opacity: 1, + paddingRight: '0px !important', + // maxWidth: { xs: '100%', sm: '50%' }, + maxWidth: '100%', + overflow: 'hidden', + '.MuiSelect-select ': { + boxSizing: 'border-box', + height: '40px', + padding: '10px 16px 10px 16px', + borderRadius: '12px', + display: 'flex', + alignItems: 'center', + border: theme => + `solid 1px ${theme.palette.card.border}`, + }, + '& .MuiPopover-paper ul': { + paddingRight: 'unset !important', + width: '100% !important', + }, + '.MuiOutlinedInput-notchedOutline': { + border: 'none !important', + }, + '& [aria-expanded=true]': { + boxSizing: 'border-box', + height: '40px', + }, }} value="service" onChange={handleChange} + MenuProps={{ + PaperProps: { + style: { + maxHeight: '120px', + overflow: 'auto', + }, + }, + }} > Services - {services.map((item, index) => ( + {state.registredServices.map((item, index) => ( { borderBottom: 'none', opacity: 1, }} - value={item.name} + value={item} > - {item.name} + {item} ))} - + )} @@ -284,10 +442,8 @@ const Content = () => { > {elem.name} - Fee:{' '} - {elem.fee + - (elem.rackRates ? ', Rack Rates' : null)} - {/* {elem.rackRates && <>, Rack Rates} */} + Fee: + {elem.fee + (elem.rackRates ? ', Rack Rates' : '')} {!elem.capabilities.filter(str => str.trim() !== '') .length ? ( @@ -316,8 +472,12 @@ const Content = () => { Type Distributor - {state.stepsConfig[1].services.map((elem, index) => { - return {elem.name} + {state.stepsConfig[2].services.map((elem, index) => { + return ( + + {elem.name} + + ) })} )} @@ -325,8 +485,8 @@ const Content = () => { )} - - Cancel + + Previous Step {(state.step === 1 && !state.stepsConfig[state.step].isSupplier) || (state.step === 2 && !state.stepsConfig[state.step].isDistributor) ? ( @@ -334,11 +494,20 @@ const Content = () => { Skip this step ) : state.step !== 4 ? ( - + Next Step ) : ( - + Create Configuration )} @@ -361,15 +530,7 @@ const Content = () => { ) } -const Overreview = () => { - return ( - - - - ) -} - -function Configuration({ children, ...restProps }) { +export function Configuration({ children, ...restProps }) { return ( { dispatch({ @@ -468,14 +637,11 @@ Configuration.Services = function Services() { }) } - useEffect(() => { - console.log({ state }) - }, [state]) - return ( {state.stepsConfig[state.step].services.map((service, index) => ( {service.name} @@ -500,10 +668,12 @@ Configuration.Services = function Services() { display: 'flex', gap: '12px', alignItems: 'center', + flexShrink: '0', }} > {state.step === 1 && ( )}