react-query#useQueryClient JavaScript Examples
The following examples show how to use
react-query#useQueryClient.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: use-mutation.js From awesome-react-starter with MIT License | 7 votes |
useMutation = (fn, options = {}) => {
const {
successCallback,
errorCallback,
redirectOnSuccess,
invalidateQueries,
...rest // pass your own options
} = options;
const router = useRouter();
const queryClient = useQueryClient();
const mutation = useQueryMutation(fn, {
onSuccess: (data) => {
if (invalidateQueries) {
queryClient.invalidateQueries(invalidateQueries);
}
if (data?.message) {
toaster.success(data?.message);
}
if (redirectOnSuccess) {
router.push(redirectOnSuccess);
}
if (typeof successCallback === 'function') {
successCallback();
}
},
onError: (err) => {
if (err?.message) {
toaster.error(err?.message);
}
if (typeof errorCallback === 'function') {
errorCallback();
}
},
...rest,
});
return mutation;
}
Example #2
Source File: List.jsx From sitepoint-books-firebase with MIT License | 7 votes |
function ScreenAuthorList() {
const { data, isLoading, error, status } = useQuery(
'authors',
AuthorService.getAll
)
const queryClient = useQueryClient()
const deleteMutation = useMutation((id) => AuthorService.remove(id), {
onSuccess: () => {
queryClient.invalidateQueries('authors')
},
})
const deleteAction = async (id) => {
deleteMutation.mutateAsync(id)
}
return (
<>
<PageHeading title="Author List" />
<div className="mt-12">
{error && <Alert type="error" message={error.message} />}
{isLoading && (
<Alert
type="info"
message="Loading..."
innerClass="animate animate-pulse"
/>
)}
{status === 'success' && (
<AuthorList data={data} deleteAction={deleteAction} />
)}
</div>
</>
)
}
Example #3
Source File: CreateActivationKeyButton.js From sed-frontend with Apache License 2.0 | 7 votes |
CreateActivationKeyButton = (props) => {
const { onClick } = props;
const queryClient = useQueryClient();
const user = queryClient.getQueryData('user');
const isDisabled = () => {
return !user.rbacPermissions.canWriteActivationKeys;
};
return (
<Button variant="primary" onClick={onClick} isDisabled={isDisabled()}>
Create activation key
</Button>
);
}
Example #4
Source File: Authentication.js From sed-frontend with Apache License 2.0 | 7 votes |
Authentication = ({ children }) => {
const queryClient = useQueryClient();
const location = useLocation();
const { isLoading, isFetching, isSuccess, isError } = useUser();
useEffect(() => {
isSuccess && window.insights?.chrome?.hideGlobalFilter();
}, [isSuccess]);
useEffect(() => {
/**
* On every rerender, based on URL change (location.pathname),
* reset the user's status to loading before authenticating again.
*/
queryClient.invalidateQueries('user');
}, [location.pathname]);
if (isError === true) {
return <Unavailable />;
} else if (isLoading === true || isFetching === true) {
return <Loading />;
} else if (isSuccess === true) {
return <>{children}</>;
}
}
Example #5
Source File: List.jsx From sitepoint-books-firebase with MIT License | 6 votes |
function ScreenBookList() {
const { data, isLoading, error, status } = useQuery(
'books',
BookService.getAll
)
const queryClient = useQueryClient()
const deleteMutation = useMutation((id) => BookService.remove(id), {
onSuccess: () => {
queryClient.invalidateQueries('books')
},
})
const deleteAction = async (id) => {
deleteMutation.mutateAsync(id)
}
return (
<>
<PageHeading title="Book List" />
<div className="mt-12">
{error && <Alert type="error" message={error.message} />}
{isLoading && (
<Alert
type="info"
message="Loading..."
innerClass="animate animate-pulse"
/>
)}
{status === 'success' && (
<BookList data={data} deleteAction={deleteAction} />
)}
</div>
</>
)
}
Example #6
Source File: BookItem.jsx From react-query-3 with GNU General Public License v3.0 | 6 votes |
BookItem = ({id, title, author }) => {
const queryClient = useQueryClient()
const { mutateAsync, isLoading } = useMutation(removeBook)
const remove = async () => {
await mutateAsync(id)
queryClient.invalidateQueries('books')
}
return (
<Flex key={id} p={3} width="100%" alignItems="center">
<StyledLink as={RouterLink} to={`/update-book/${id}`} mr="auto">{title}</StyledLink>
<Text>{author}</Text>
<Button onClick={remove} ml="5">
{ isLoading ? <Loader type="ThreeDots" color="#fff" height={10} /> : "Remove" }
</Button>
</Flex>
);
}
Example #7
Source File: useTokenPrices.js From v3-ui with MIT License | 6 votes |
useTokenPrices = (chainId, addresses) => {
const queryClient = useQueryClient()
const flatAddresses = addresses ? Object.values(addresses).flat() : []
const blockNumbers = addresses ? Object.keys(addresses) : []
const enabled = Boolean(addresses && flatAddresses?.length > 0 && chainId)
return useQuery(
[QUERY_KEYS.tokenPrices, chainId, flatAddresses, blockNumbers],
async () => await getTokenPrices(chainId, addresses),
{
enabled,
onSuccess: (data) => populateCaches(chainId, queryClient, data, addresses)
}
)
}
Example #8
Source File: usePastPrizes.js From v3-ui with MIT License | 6 votes |
usePastPrizes = (pool, page, pageSize = PRIZE_PAGE_SIZE) => {
const queryClient = useQueryClient()
const chainId = pool?.chainId
const readProvider = useReadProvider(chainId)
const poolAddress = pool?.prizePool?.address
const { data: prizes, ...prizeData } = useQuery(
[QUERY_KEYS.poolPrizesQuery, chainId, poolAddress, page, pageSize],
async () => await getPoolPrizesData(chainId, readProvider, pool?.contract, page, pageSize),
{
enabled: Boolean(poolAddress && !!readProvider && chainId),
keepPreviousData: true,
onSuccess: (data) => populateCaches(chainId, poolAddress, queryClient, data)
}
)
const addresses = getErc20AddressesByBlockNumberFromPrizes(
prizes,
pool?.tokens?.underlyingToken?.address
)
const { data: tokenPrices, ...tokenPricesData } = useTokenPrices(chainId, addresses)
const count = pool?.prize?.currentPrizeId - 1 || 0
const pages = Math.ceil(Number(count / PRIZE_PAGE_SIZE))
const isFetched =
(prizeData.isFetched && tokenPricesData.isFetched) ||
(prizeData.isFetched && Boolean(prizes?.length === 0))
const data = formatAndCalculatePrizeValues(prizes, tokenPrices, pool?.tokens?.underlyingToken)
return {
data,
isFetched,
page,
pages,
count
}
}
Example #9
Source File: epoch-context.js From idena-web with MIT License | 6 votes |
export function EpochProvider(props) {
const queryClient = useQueryClient()
const {apiKey, url} = useSettingsState()
const timing = useNodeTiming()
const [lastModifiedEpochTime, setLastModifiedEpochTime] = useState(0)
const {data: epochData} = useQuery(
['get-epoch', apiKey, url],
() => fetchEpoch(),
{
retryDelay: 5 * 1000,
initialData: null,
}
)
useInterval(() => {
if (!epochData || !timing) return
if (shouldRefetchEpoch(epochData, timing)) {
const currentTime = new Date().getTime()
if (Math.abs(currentTime - lastModifiedEpochTime > 15 * 1000)) {
queryClient.invalidateQueries('get-epoch')
setLastModifiedEpochTime(currentTime)
}
}
}, 1000)
return <EpochContext.Provider {...props} value={epochData ?? null} />
}
Example #10
Source File: List.jsx From sitepoint-books-firebase with MIT License | 6 votes |
function ScreenCategoryList() {
const { data, isLoading, error, status } = useQuery(
'books',
CategoryService.getAll
)
const queryClient = useQueryClient()
const deleteMutation = useMutation((id) => CategoryService.remove(id), {
onSuccess: () => {
queryClient.invalidateQueries('categories')
},
})
const deleteAction = async (id) => {
deleteMutation.mutateAsync(id)
}
return (
<>
<PageHeading title="Category List" />
<div className="mt-12">
{error && <Alert type="error" message={error.message} />}
{isLoading && (
<Alert
type="info"
message="Loading..."
innerClass="animate animate-pulse"
/>
)}
{status === 'success' && (
<CategoryList data={data} deleteAction={deleteAction} />
)}
</div>
</>
)
}
Example #11
Source File: ActivationKeysTable.js From sed-frontend with Apache License 2.0 | 6 votes |
ActivationKeysTable = (props) => {
const { actions } = props;
const columnNames = {
name: 'Key Name',
role: 'Role',
serviceLevel: 'SLA',
usage: 'Usage',
};
const { isLoading, error, data } = useActivationKeys();
const queryClient = useQueryClient();
const user = queryClient.getQueryData('user');
const isActionsDisabled = () => {
return !user.rbacPermissions.canWriteActivationKeys;
};
const Results = () => {
return (
<TableComposable aria-label="ActivationKeys">
<Thead>
<Tr ouiaSafe={true}>
<Th width={40}>{columnNames.name}</Th>
<Th>{columnNames.role}</Th>
<Th>{columnNames.serviceLevel}</Th>
<Th>{columnNames.usage}</Th>
<Td></Td>
</Tr>
</Thead>
<Tbody>
{data.map((datum) => {
let rowActions = actions(datum.name);
return (
<Tr key={datum.name} ouiaSafe={true}>
<Td modifier="breakWord" dataLabel={columnNames.name}>
{datum.name}
</Td>
<Td dataLabel={columnNames.role}>{datum.role}</Td>
<Td dataLabel={columnNames.serviceLevel}>
{datum.serviceLevel}
</Td>
<Td dataLabel={columnNames.usage}>{datum.usage}</Td>
<Td isActionCell>
<ActionsColumn
items={rowActions}
isDisabled={isActionsDisabled()}
actionsToggle={customActionsToggle}
/>
</Td>
</Tr>
);
})}
</Tbody>
</TableComposable>
);
};
if (isLoading && !error) {
return <Loading />;
} else if (!isLoading && !error) {
return <Results />;
} else {
return <Unavailable />;
}
}
Example #12
Source File: CreateActivationKeyModal.js From sed-frontend with Apache License 2.0 | 5 votes |
CreateActivationKeyModal = (props) => {
const queryClient = useQueryClient();
const [created, setCreated] = React.useState(false);
const [error, setError] = React.useState(false);
const { handleModalToggle, isOpen } = props;
const { mutate, isLoading } = useCreateActivationKey();
const submitForm = (name, role, serviceLevel, usage) => {
mutate(
{ name, role, serviceLevel, usage },
{
onSuccess: () => {
setError(false);
setCreated(true);
queryClient.invalidateQueries('activation_keys');
},
onError: () => {
setError(true);
setCreated(false);
},
}
);
};
return (
<Modal
variant={ModalVariant.large}
title="Create new activation key"
description=""
isOpen={isOpen}
onClose={handleModalToggle}
>
{isLoading ? (
<Loading />
) : (
<ActivationKeyForm
handleModalToggle={handleModalToggle}
submitForm={submitForm}
isSuccess={created}
isError={error}
/>
)}
</Modal>
);
}
Example #13
Source File: DeleteActivationKeyConfirmationModal.js From sed-frontend with Apache License 2.0 | 5 votes |
DeleteActivationKeyConfirmationModal = (props) => { const { isOpen, handleModalToggle, name } = props; const { addSuccessNotification, addErrorNotification } = useNotifications(); const { mutate, isLoading } = useDeleteActivationKey(); const queryClient = useQueryClient(); const deleteActivationKey = (name) => { mutate(name, { onSuccess: (_data, name) => { queryClient.setQueryData('activation_keys', (oldData) => oldData.filter((entry) => entry.name != name) ); addSuccessNotification(`Activation Key ${name} deleted`); handleModalToggle(); }, onError: () => { addErrorNotification('Something went wrong. Please try again'); handleModalToggle(); }, }); mutate; }; const actions = [ <Button key="confirm" variant="danger" onClick={() => deleteActivationKey(name)} data-testid="delete-activation-key-confirmation-modal-confirm-button" > Delete </Button>, <Button key="cancel" variant="link" onClick={handleModalToggle}> Cancel </Button>, ]; const title = ( <> <TextContent> <Text component={TextVariants.h2}> <ExclamationTriangleIcon size="md" color="#F0AB00" /> Delete Activation Key? </Text> </TextContent> </> ); const content = () => { if (isLoading) { return <Loading />; } else { return ( <TextContent> <Text component={TextVariants.p}> <b>{name}</b> will no longer be available for use. This operation cannot be undone. </Text> </TextContent> ); } }; return ( <Modal title={title} isOpen={isOpen} onClose={handleModalToggle} variant={ModalVariant.small} actions={actions} > {content()} </Modal> ); }
Example #14
Source File: EditActivationKeyModal.js From sed-frontend with Apache License 2.0 | 5 votes |
EditActivationKeyModal = (props) => {
const { activationKeyName } = props;
const queryClient = useQueryClient();
const [updated, setUpdated] = React.useState(false);
const [error, setError] = React.useState(false);
const { handleModalToggle, isOpen } = props;
const { mutate, isLoading } = useUpdateActivationKey();
const {
isLoading: isKeyLoading,
error: keyError,
data: activationKey,
} = useActivationKey(activationKeyName);
const submitForm = (name, role, serviceLevel, usage) => {
mutate(
{ activationKeyName, role, serviceLevel, usage },
{
onSuccess: () => {
setError(false);
setUpdated(true);
queryClient.invalidateQueries('activation_keys');
queryClient.resetQueries(`activation_key_${activationKeyName}`);
},
onError: () => {
setError(true);
setUpdated(false);
},
}
);
};
return (
<Modal
variant={ModalVariant.large}
title="Edit activation key"
description=""
isOpen={isOpen}
onClose={handleModalToggle}
>
{(isLoading || isKeyLoading) && !keyError ? (
<Loading />
) : (
<ActivationKeyForm
activationKey={activationKey}
handleModalToggle={handleModalToggle}
submitForm={submitForm}
isSuccess={updated}
isError={error}
/>
)}
</Modal>
);
}
Example #15
Source File: Form.jsx From sitepoint-books-firebase with MIT License | 5 votes |
function ScreenAuthorForm() {
const { id } = useParams()
const { data, isLoading, error, status } = useQuery(
['author', { id }],
AuthorService.getOne
)
const queryClient = useQueryClient()
const saveData = (data) => {
if (id) {
return AuthorService.update(id, data)
} else {
AuthorService.create(data)
}
}
const mutation = useMutation((data) => saveData(data), {
onSuccess: () => {
if (id) queryClient.invalidateQueries(['author', { id }])
},
})
const { isSuccess } = mutation
const onSubmit = async (submittedData) => {
mutation.mutate(submittedData)
}
if (isSuccess) {
return <Redirect to="/author" />
}
if (!id) {
return (
<>
<PageHeading title="Create Author" />
<div className="mt-12">
{error && <Alert type="error" message={error.message} />}
<AuthorForm submit={onSubmit} />
</div>
</>
)
}
return (
<>
<PageHeading title="Edit Author" />
<div className="mt-12">
{error && <Alert type="error" message={error.message} />}
{isLoading && (
<Alert
type="info"
message="Loading..."
innerClass="animate animate-pulse"
/>
)}
{status === 'success' && <AuthorForm values={data} submit={onSubmit} />}
</div>
</>
)
}
Example #16
Source File: Form.jsx From sitepoint-books-firebase with MIT License | 5 votes |
function ScreenCategoryForm() {
let { id } = useParams()
const { data, isLoading, error, status } = useQuery(
['category', { id }],
CategoryService.getOne
)
const queryClient = useQueryClient()
const saveData = (data) => {
if (id) {
return CategoryService.update(id, data)
} else {
CategoryService.create(data)
}
}
const mutation = useMutation((data) => saveData(data), {
onSuccess: () => {
if (id) queryClient.invalidateQueries(['category', { id }])
},
})
const { isSuccess } = mutation
const onSubmit = async (submittedData) => {
mutation.mutate(submittedData)
}
if (isSuccess) {
return <Redirect to="/category" />
}
if (!id) {
return (
<>
<PageHeading title="Create Category" />
<div className="mt-12">
<CategoryForm values={{ cover: 'nocover' }} action={onSubmit} />
</div>
</>
)
}
return (
<>
<PageHeading title="Edit Category" />
<div className="mt-12">
{error && <Alert type="error" message={error.message} />}
{isLoading && (
<Alert
type="info"
message="Loading..."
innerClass="animate animate-pulse"
/>
)}
{status === 'success' && (
<CategoryForm values={data} action={onSubmit} />
)}
</div>
</>
)
}
Example #17
Source File: offers.js From idena-web with MIT License | 5 votes |
export default function AdOfferList() {
const {t} = useTranslation()
const queryClient = useQueryClient()
const {data: burntCoins, status: burntCoinsStatus} = useApprovedBurntCoins()
const isFetched = burntCoinsStatus === 'success'
const isEmpty = isFetched && burntCoins.length === 0
const [selectedAd, setSelectedAd] = React.useState({})
const burnDisclosure = useDisclosure()
const {
onOpen: onOpenBurnDisclosure,
onClose: onCloseBurnDisclosure,
} = burnDisclosure
const handlePreviewBurn = React.useCallback(
ad => {
setSelectedAd(ad)
onOpenBurnDisclosure()
},
[onOpenBurnDisclosure]
)
const handleBurn = React.useCallback(() => {
onCloseBurnDisclosure()
queryClient.invalidateQueries(['bcn_burntCoins', []])
}, [onCloseBurnDisclosure, queryClient])
return (
<Layout skipBanner>
<Page>
<PageHeader>
<PageTitle mb={4}>{t('All offers')}</PageTitle>
<PageCloseButton href="/adn/list" />
</PageHeader>
<Table>
<Thead>
<Tr>
<RoundedTh isLeft>{t('Banner/author')}</RoundedTh>
<RoundedTh>{t('Website')}</RoundedTh>
<RoundedTh>{t('Target')}</RoundedTh>
<RoundedTh>{t('Burn')}</RoundedTh>
<RoundedTh isRight />
</Tr>
</Thead>
<Tbody>
{isFetched &&
burntCoins.map(burn => (
<AdOfferListItem
key={burn.key}
burn={burn}
onBurn={handlePreviewBurn}
/>
))}
</Tbody>
</Table>
{isEmpty && (
<Center color="muted" mt="4" w="full">
{t('No active offers')}
</Center>
)}
<BurnDrawer ad={selectedAd} onBurn={handleBurn} {...burnDisclosure} />
</Page>
</Layout>
)
}
Example #18
Source File: hooks.js From idena-web with MIT License | 5 votes |
export function useApprovedBurntCoins() {
const queryClient = useQueryClient()
const {decodeProfile, decodeAdBurnKey} = useProtoProfileDecoder()
const {data: burntCoins, status: burntCoinsStatus} = useBurntCoins({
select: data =>
data?.map(({address, key, amount}) => ({
address,
key,
...decodeAdBurnKey(key),
amount,
})) ?? [],
notifyOnChangeProps: 'tracked',
})
const rpcFetcher = useRpcFetcher()
return useQuery({
queryKey: ['approvedAdOffers'],
queryFn: () =>
Promise.all(
burntCoins.map(async burn => {
const identity = await queryClient.fetchQuery({
queryKey: ['dna_identity', [burn.address]],
queryFn: rpcFetcher,
staleTime: 5 * 60 * 1000,
})
if (identity.profileHash) {
const profile = await queryClient.fetchQuery({
queryKey: ['ipfs_get', [identity.profileHash]],
queryFn: rpcFetcher,
staleTime: Infinity,
})
const {ads} = decodeProfile(profile)
const ad = ads.find(({cid}) => cid === burn.cid)
if (ad) {
const voting = await getAdVoting(ad.contract)
return isApprovedVoting(voting) ? burn : null
}
}
})
),
enabled: burntCoinsStatus === 'success',
select: React.useCallback(data => data.filter(Boolean), []),
notifyOnChangeProps: 'tracked',
})
}
Example #19
Source File: hooks.js From idena-web with MIT License | 5 votes |
export function useProfileAds() {
const rpcFetcher = useRpcFetcher()
const [{profileHash}, {forceUpdate: forceIdentityUpdate}] = useIdentity()
const {decodeProfile, decodeAd, decodeAdTarget} = useProtoProfileDecoder()
const {data: profile, status: profileStatus} = useQuery({
queryKey: ['ipfs_get', [profileHash]],
queryFn: rpcFetcher,
enabled: Boolean(profileHash),
select: decodeProfile,
staleTime: Infinity,
notifyOnChangeProps: 'tracked',
})
const queryClient = useQueryClient()
const decodedProfileAds = useQueries(
profile?.ads?.map(({cid, target, ...ad}) => ({
queryKey: ['decodedProfileAd', [cid]],
queryFn: async () => ({
...decodeAd(
await queryClient
.fetchQuery({
queryKey: ['ipfs_get', [cid]],
queryFn: rpcFetcher,
staleTime: Infinity,
})
.catch(() => '')
),
...decodeAdTarget(target),
cid,
...ad,
}),
enabled: Boolean(cid),
staleTime: Infinity,
})) ?? []
)
const profileAds = useQueries(
decodedProfileAds
.filter(({data}) => Boolean(data?.contract))
.map(({data}) => {
const {cid, contract, ...ad} = data
return {
queryKey: ['profileAd', cid, contract],
queryFn: async () => ({
...ad,
cid,
status: AdStatus.Published,
}),
}
})
)
const status =
profileStatus === 'loading' ||
(profileHash === undefined
? true
: Boolean(profileHash) && profileStatus === 'idle') ||
decodedProfileAds.some(ad => ad.status === 'loading') ||
profileAds.some(ad => ad.status === 'loading')
? 'loading'
: 'done'
return {
data: profileAds.map(({data}) => data) ?? [],
status,
refetch: forceIdentityUpdate,
}
}
Example #20
Source File: index.js From flame-coach-web with MIT License | 4 votes |
Account = ({
customerIdentifier,
email
}) => {
const queryClient = useQueryClient();
const [notification, setNotification] = useState({
enable: false,
message: '',
level: 'INFO'
});
const resetNotificationHandler = () => {
setNotification(update(notification,
{
enable: { $set: false }
}));
};
const updateNotificationHandler = (enable, message, level) => {
setNotification({
enable,
message,
level
});
};
const updateUserDetailsHandler = (event) => {
let newValue = event.target.value;
queryClient.setQueryData(['getContactInformation', customerIdentifier], (oldData) => {
if (event.target.name === 'country') {
logDebug('AccountCoach', 'updateUserDetailsHandler', 'Event Native:', event.nativeEvent.target);
newValue = {
value: event.nativeEvent.target.textContent,
code: event.target.value.length === 0 ? null : event.target.value
};
}
return {
...oldData,
[event.target.name]: newValue
};
});
};
const updatePhotoHandler = () => {
logger.debug('Update photo');
};
const contactInformation = useQuery(['getContactInformation', customerIdentifier],
() => getCoachContactInformation(customerIdentifier), {
onError: async (err) => {
logError('AccountCoach',
'useQuery getCoachContactInformation',
'Error:', err);
}
});
const updateContactInformation = useMutation(
({
// eslint-disable-next-line no-shadow
customerIdentifier,
newContactInformation
}) => updateCoachContactInformation(customerIdentifier, newContactInformation),
{
onError: (error) => {
logError('AccountCoach', 'updateContactInformation', 'Error Details:', error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
updateNotificationHandler(true, errorCode.msg, errorCode.level);
},
onSuccess: () => {
const infoCode = InfoMessage.CODE_2001;
updateNotificationHandler(true, infoCode.msg, infoCode.level);
}
}
);
const saveContactInformationHandler = () => {
logDebug('AccountCoach', 'saveContactInformationHandler', 'Contact Information:', contactInformation.data);
const newContactInformation = {
firstName: contactInformation.data.firstName,
lastName: contactInformation.data.lastName,
phoneCode: contactInformation.data.phoneCode,
phoneNumber: contactInformation.data.phoneNumber,
countryCode: (contactInformation.data.country) ? contactInformation.data.country.code : null
};
updateContactInformation.mutate({
customerIdentifier,
newContactInformation
});
};
return (
<Page
title="Account"
isError={contactInformation.isError}
isLoading={contactInformation.isFetching}
>
<Grid
container
spacing={3}
>
<Grid
item
lg={4}
md={6}
xs={12}
>
<Profile
user={contactInformation.data ? {
city: '',
country: (contactInformation.data.country)
? contactInformation.data.country.value : '',
avatar: ''
} : null}
updatePhotoHandler={updatePhotoHandler}
/>
</Grid>
<Grid
item
lg={8}
md={6}
xs={12}
>
<ProfileDetails
userDetails={contactInformation.data ? {
firstName: contactInformation.data.firstName,
lastName: contactInformation.data.lastName,
email,
phoneCode: contactInformation.data.phoneCode,
phoneNumber: contactInformation.data.phoneNumber,
country: contactInformation.data.country && contactInformation.data.country.code
? contactInformation.data.country.code : '',
} : null}
saveContactInformationHandler={saveContactInformationHandler}
updateUserDetailsHandler={updateUserDetailsHandler}
/>
{notification.enable
? (
<Notification
collapse
open={notification.enable}
openHandler={resetNotificationHandler}
level={notification.level}
message={notification.message}
/>
)
: null}
</Grid>
</Grid>
</Page>
);
}
Example #21
Source File: index.js From flame-coach-web with MIT License | 4 votes |
Appointments = ({
customerIdentifier
}) => {
const queryClient = useQueryClient();
const classes = useStyles();
const [appointment, setAppointment] = React.useState({
id: null,
title: null,
allDay: false,
dttmStarts: new Date(),
dttmEnds: new Date(),
resource: {
notes: "",
price: null,
clientIdentifier: null
}
});
const [notification, setNotification] = useState({
enable: false,
message: "",
level: "INFO"
});
const [openDialog, setOpenDialog] = React.useState(false);
const [operation, setOperation] = React.useState("ADD");
const [calendarView, setCalendarView] = React.useState(Views.DAY);
React.useEffect(() => {
setNotification(update(notification, {
enable: { $set: false }
}));
}, [openDialog]);
const {
register,
handleSubmit
} = useForm();
const { mutate: addAppointment } = useAddAppointmentClient();
const editAppointment = useEditAppointmentClient();
const { mutate: deleteAppointment } = useDeleteAppointmentClient();
const appointments = useFetchAppointmentsCoach(customerIdentifier,
{
select: (data) => {
if (data === undefined || !data.appointments) {
return [];
}
return data.appointments.map((item) => {
const startDate = moment.tz(item.dttmStarts, defaultTimezone);
const endDate = moment.tz(item.dttmEnds, defaultTimezone);
return {
id: item.identifier,
title: `${item.client.firstName} ${item.client.lastName}`,
start: new Date(startDate.format("YYYY-MM-DDTHH:mm:ss")),
end: new Date(endDate.format("YYYY-MM-DDTHH:mm:ss")),
allDay: false, //TODO: Check if this is all day
resource: {
clientIdentifier: item.client.identifier,
price: item.price,
notes: item.notes
}
};
}
);
}
}
);
const clientsCoach = useFetchClientsCoach(customerIdentifier);
const okDialogHandler = (data, event) => {
event.preventDefault();
logInfo("Appointments", "okDialogHandler", "value", appointment);
if (!appointment.title || !appointment.dttmStarts ||
!appointment.dttmEnds || !appointment.resource?.price ||
!appointment.resource?.clientIdentifier) {
setNotification(update(notification,
{
enable: { $set: true },
message: { $set: ErrorMessage.CODE_0009.msg },
level: { $set: ErrorMessage.CODE_0009.level }
}));
} else {
if (appointment.dttmStarts.isAfter(appointment.dttmEnds)) {
setNotification(update(notification,
{
enable: { $set: true },
message: { $set: ErrorMessage.CODE_0008.msg },
level: { $set: ErrorMessage.CODE_0008.level }
}));
} else {
if (operation === "ADD") {
addAppointment({
appointment: appointment,
clientIdentifier: appointment.resource?.clientIdentifier,
coachIdentifier: customerIdentifier
}, {
onSuccess: () => {
queryClient.invalidateQueries(["getCoachAppointments", customerIdentifier]);
setOpenDialog(false);
},
onError: (error) => {
logError("Appointments", "useMutation addAppointment", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
setNotification(update(notification,
{
enable: { $set: true },
message: { $set: errorCode.msg },
level: { $set: errorCode.level }
}));
}
}
);
}
if (operation === "EDIT/DELETE") {
editAppointment.mutate({
appointmentIdentifier: appointment.id,
appointment: appointment
}, {
onSuccess: () => {
queryClient.invalidateQueries(["getCoachAppointments", customerIdentifier]);
setOpenDialog(false);
},
onError: (error) => {
logError("Appointments", "useMutation editAppointment", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
setNotification(update(notification,
{
enable: { $set: true },
message: { $set: errorCode.msg },
level: { $set: errorCode.level }
}));
}
}
);
}
}
}
};
const deleteHandler = () => {
deleteAppointment({
appointmentIdentifier: appointment.id
}, {
onSuccess: () => {
queryClient.invalidateQueries(["getCoachAppointments", customerIdentifier]);
setOpenDialog(false);
},
onError: (error) => {
logError("Appointments", "useMutation deleteAppointment", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
setNotification(update(notification,
{
enable: { $set: true },
message: { $set: errorCode.msg },
level: { $set: errorCode.level }
}));
}
});
};
const doubleClickSlotHandler = (slot) => {
setOperation("EDIT/DELETE");
let dateTimeZoneStart;
let dateTimeZoneEnd;
let allDay = false;
//Entire day event
if (slot?.allDay) {
dateTimeZoneStart = getTimezoneDateTime(slot.start);
dateTimeZoneEnd = getTimezoneDateTime(slot.start)
.add(1, "days")
.subtract(1, "seconds");
allDay = true;
} else {
//Select the first and the last date
dateTimeZoneStart = getTimezoneDateTime(slot.start);
dateTimeZoneEnd = getTimezoneDateTime(slot.end);
}
setAppointment(update(appointment, {
id: { $set: slot.id },
title: { $set: slot.title },
dttmStarts: { $set: dateTimeZoneStart },
dttmEnds: { $set: dateTimeZoneEnd },
allDay: { $set: allDay },
resource: {
notes: { $set: slot.resource?.notes },
price: { $set: slot.resource?.price },
clientIdentifier: { $set: slot.resource?.clientIdentifier }
}
}));
setOpenDialog(true);
};
const selectSlotHandler = (slots) => {
setOperation("ADD");
let dateTimeZoneStart;
let dateTimeZoneEnd;
let allDay = false;
//Selected entire day
if (slots.length === 1) {
dateTimeZoneStart = getTimezoneDateTime(slots[0]);
dateTimeZoneEnd = getTimezoneDateTime(slots[0])
.add(1, "days")
.subtract(1, "seconds");
allDay = true;
} else {
//Select the first and the last date
dateTimeZoneStart = getTimezoneDateTime(slots[0]);
dateTimeZoneEnd = getTimezoneDateTime(slots[slots.length - 1]);
}
setAppointment(update(appointment, {
id: { $set: null },
title: { $set: "" },
dttmStarts: { $set: dateTimeZoneStart },
dttmEnds: { $set: dateTimeZoneEnd },
allDay: { $set: allDay },
resource: {
notes: { $set: "" },
price: { $set: 0.0 },
clientIdentifier: { $set: null }
}
}));
setOpenDialog(true);
};
return (
<Page
title={"Appointments"}
isError={clientsCoach.isError || appointments.isError}
isLoading={clientsCoach.isFetching || appointments.isFetching}>
<Card className={clsx(classes.calendarCard)}>
<CardHeader
title="Appointments"
className={clsx(classes.calendarCardHeader)}
/>
<Divider />
<CardContent className={clsx(classes.calendarCard, classes.calendarCardContent)}>
<BigCalendar
view={calendarView}
events={appointments.data}
localizer={localizer}
doubleClickSlotHandler={doubleClickSlotHandler}
selectSlotHandler={selectSlotHandler}
onView={(value) => setCalendarView(value)}
/>
</CardContent>
</Card>
<FormDialog
submitHandler={handleSubmit}
dialogTitle={operation === "ADD" ? "New Appointment" : "Edit Appointment"}
dialogDescription="Please complete the following fields below:"
open={openDialog}
deleteHandler={operation === "EDIT/DELETE" ? deleteHandler : null}
okHandler={okDialogHandler}
closeHandler={() => setOpenDialog(false)}>
<Grid
container
spacing={1}
direction="row">
<Grid
item
xs={12}
md={12}>
<SearchClient
clients={!clientsCoach.isLoading ? clientsCoach?.data.clientsCoach : []}
clientDefault={appointment.resource?.clientIdentifier}
disabled={operation === "EDIT/DELETE"}
searchSelectedHandler={(newValue) => {
setAppointment(update(appointment, {
title: { $set: newValue ? `${newValue.firstname} ${newValue.lastname}` : "" },
resource: {
clientIdentifier: { $set: newValue.identifier }
}
}));
}}
error={Boolean(!appointment.resource?.clientIdentifier)}
margin="dense"
inputRef={register("client")}
/>
</Grid>
<Grid
item
xs={12}
md={6}
>
<KeyboardDateTimePicker
autoOk
fullWidth
name="dttmStarts"
label="Starts"
inputVariant="outlined"
inputRef={register("dttmStarts")}
onChange={(newDate) => {
logDebug("Appointments", "onChange KeyboardDateTimePicker", "New Date", newDate);
setAppointment(update(appointment, {
dttmStarts: { $set: newDate }
}));
}}
error={Boolean(!appointment.dttmStarts)}
value={appointment.dttmStarts}
/>
</Grid>
<Grid
item
xs={12}
md={6}
>
<KeyboardDateTimePicker
autoOk
fullWidth
name="dttmEnds"
label="Ends"
inputVariant="outlined"
inputRef={register("dttmEnds")}
onChange={(newDate) => {
logDebug("Appointments", "onChange KeyboardDateTimePicker", "New Date", newDate);
setAppointment(update(appointment, {
dttmEnds: { $set: newDate }
}));
}}
error={Boolean(!appointment.dttmEnds)}
value={appointment.dttmEnds}
variant="outlined"
/>
</Grid>
<Grid
item
xs={12}
md={12}
>
<TextField
fullWidth
label="Price"
name="price"
type="number"
inputProps={{
step: 0.05,
min: 0
}}
InputProps={{
startAdornment: <InputAdornment position="start">£</InputAdornment>
}}
onChange={(event) => {
setAppointment(update(appointment, {
resource: {
price: { $set: event.target.value }
}
}));
}}
value={appointment.resource?.price}
error={Boolean(!appointment.resource?.price)}
margin="dense"
inputRef={register("price")}
variant="outlined"
/>
</Grid>
<Grid
item
xs={12}
md={12}
>
<TextField
fullWidth
label="Notes"
name="notes"
margin="dense"
inputRef={register("notes")}
onChange={(event) => {
setAppointment(update(appointment, {
resource: {
notes: { $set: event.target.value }
}
}));
}}
value={appointment.resource?.notes}
variant="outlined"
/>
</Grid>
{notification.enable
? (
<Grid item xs={12}>
<Notification
collapse
open={notification.enable}
openHandler={() => setNotification(update(notification,
{
enable: { $set: false }
}))
}
level={notification.level}
message={notification.message}
/>
</Grid>
)
: null}
</Grid>
</FormDialog>
</Page>
);
}
Example #22
Source File: index.js From flame-coach-web with MIT License | 4 votes |
CustomersView = ({ customerIdentifier }) => {
const classes = useStyles();
const isMobile = useIsMediumMobile();
const options = {
filterType: "dropdown",
selectableRows: "none",
tableBodyMaxHeight: "70vh",
sortOrder: {
name: "Name",
direction: "asc"
},
print: false
};
const [notification, setNotification] = useState({
enable: false,
message: "",
level: "INFO"
});
const queryClient = useQueryClient();
const [clientLoading, setClientLoading] = useState(false);
const [isClientLoading, setIsClientLoading] = useState(null);
const { mutate: inviteClient } = useInviteClient();
const resetNotificationHandler = () => {
setNotification(update(notification,
{
enable: { $set: false }
}));
};
const updateNotificationHandler = (enable, message, level) => {
setNotification({
enable,
message,
level
});
};
const formikSendInviteClient = useFormik({
initialValues: {
email: ""
},
validationSchema,
validateOnBlur: false,
validateOnChange: false,
onSubmit: (values) => {
inviteClient({
coachIdentifier: customerIdentifier,
clientEmail: values.email
}, {
onError: (error) => {
logError("Customer",
"useMutation inviteClient",
"Error:", error.response);
logError("Customer", "useMutation inviteClient", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
updateNotificationHandler(true, errorCode.msg, errorCode.level);
},
onSuccess: (data) => {
queryClient.invalidateQueries(["getClientsCoachPlusClientsAvailableForCoaching", customerIdentifier]);
const successMessage = data.registrationInvite ? InfoMessage.CODE_0004
: InfoMessage.CODE_0003;
updateNotificationHandler(true, successMessage.msg, successMessage.level);
}
});
}
});
const {
isError,
isLoading,
data
} = useQuery(["getClientsCoachPlusClientsAvailableForCoaching", customerIdentifier],
() => getClientsCoachPlusClientsAvailableForCoaching(customerIdentifier), {
onError: async (err) => {
logError("Customer",
"useQuery getClientsCoachPlusClientsAvailableForCoaching",
"Error:", err);
},
select: (data) => {
const filteredClients = data.clientsCoach.filter((client) => client.status === "PENDING"
|| client.status === "ACCEPTED");
return {
identifier: data.identifier,
clientsCoach: filteredClients
};
}
});
const unlinkClient = useMutation(
({
clientIdentifier,
// eslint-disable-next-line no-unused-vars
coachIdentifier
}) => enrollmentProcessBreak(clientIdentifier),
{
onMutate: async ({ clientIdentifier }) => {
setClientLoading(clientIdentifier);
setIsClientLoading(true);
resetNotificationHandler();
},
onError: async (error) => {
logError("Customer",
"useMutation enrollmentProcessBreak",
"Error:", error.response);
setIsClientLoading(false);
logError("Customer", "useMutation enrollmentProcessBreak", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
updateNotificationHandler(true, errorCode.msg, errorCode.level);
},
onSuccess: async (data, variables) => {
await queryClient.cancelQueries(["getClientsCoachPlusClientsAvailableForCoaching", variables.coachIdentifier]);
queryClient.setQueryData(["getClientsCoachPlusClientsAvailableForCoaching", customerIdentifier], (oldData) => {
const index = oldData.clientsCoach.findIndex(
(customer) => customer.identifier === variables.clientIdentifier
);
return update(oldData, {
clientsCoach: {
[index]: {
status: { $set: data.status }
}
}
});
});
setIsClientLoading(false);
}
}
);
const unlinkClientHandler = (client) => {
unlinkClient.mutate({
clientIdentifier: client.identifier,
coachIdentifier: customerIdentifier
});
};
const getStatus = (status) => {
switch (status) {
case "AVAILABLE":
return "Available";
case "PENDING":
return "Pending";
case "ACCEPTED":
return "My client";
default:
return "Unknown";
}
};
const columnActions = {
label: "Actions",
options: {
filter: false,
sort: false,
setCellHeaderProps: () => {
return {
className: clsx({
[classes.rightTableHead]: true
})
};
},
// eslint-disable-next-line react/display-name
customBodyRender: (value) => {
const disableButtonMinus = value.client.identifier === value.clientLoading
? value.isClientLoading || !(value.client.status !== "AVAILABLE")
: !(value.client.status !== "AVAILABLE");
return (
<Grid
container
justifyContent={isMobile ? "flex-start" : "flex-end"}
spacing={1}
className={clsx({
[classes.actionColumnTable]: true
})}
>
<Grid item>
<Button
className={classes.minusUserButton}
variant="contained"
disabled={disableButtonMinus}
onClick={() => unlinkClientHandler(value.client)}
>
<SvgIcon
fontSize="small"
color="inherit"
>
<UserMinusIcon />
</SvgIcon>
</Button>
</Grid>
</Grid>
);
}
}
};
const columns = ["Name", "Email", "Registration date", "Status", columnActions];
return (
<Page
title="Customers"
isError={isError}
isLoading={isLoading}>
<Grid
direction="row"
container
>
<Grid item xs={12}>
<Box marginBottom={2}>
<form onSubmit={formikSendInviteClient.handleSubmit}>
<Card>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
Send Invite
</Typography>
<Box className={classes.sendCard}>
<TextField
error={Boolean(formikSendInviteClient.errors.email)}
fullWidth
helperText={formikSendInviteClient.errors.email}
label="Email"
margin="normal"
name="email"
onBlur={formikSendInviteClient.handleBlur}
onChange={formikSendInviteClient.handleChange}
value={formikSendInviteClient.values.email}
variant="outlined"
/>
<Button
className={classes.sendInviteButton}
variant="contained"
type="submit"
>
<SvgIcon
fontSize="small"
color="inherit"
>
<SendIcon />
</SvgIcon>
</Button>
</Box>
</CardContent>
</Card>
</form>
</Box>
</Grid>
<Grid item xs={12}>
<Table
title="Clients List"
data={data ? data.clientsCoach.map((client) => ([
`${client.firstname} ${client.lastname}`,
client.email,
client.registrationDate,
getStatus(client.status),
{
client,
clientLoading,
isClientLoading
}
])) : null}
columns={columns}
options={options}
themeTable={outerTheme => outerTheme}
/>
</Grid>
{notification.enable
? (
<Grid item xs={12}>
<Notification
collapse
open={notification.enable}
openHandler={resetNotificationHandler}
level={notification.level}
message={notification.message}
/>
</Grid>
)
: null}
</Grid>
</Page>
);
}
Example #23
Source File: index.js From flame-coach-web with MIT License | 4 votes |
Dashboard = ({
customerIdentifier
}) => {
const queryClient = useQueryClient();
const classes = useStyles();
// region Native reactj state
const [enrollment, setEnrollment] = useState({});
const [activeCoachStep, setActiveCoachStep] = React.useState(0);
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const [taskPeriod, setTaskPeriod] = useState("today");
const [tasksProgress, setTaskProgress] = useState(0);
const [notification, setNotification] = useState({
enable: false,
message: "",
level: "INFO"
});
// endregion
// region General methods
const updateNotificationHandler = (enable, message, level) => {
setNotification({
enable,
message,
level
});
};
const notificationHandler = () => {
setNotification(update(notification,
{
enable: { $set: false }
}));
};
const progressPercentage = (data) => {
if (!data || !data.dailyTasks) {
return 0.0;
}
const listOfDailyTasks = data.dailyTasks;
const numberOfDailyTasks = listOfDailyTasks.length;
logDebug("DashboardClient", "progressPercentage", "numberOfDailyTasks", numberOfDailyTasks);
const numberOfDailyTasksTicked = listOfDailyTasks
.filter((dailyTask) => dailyTask.ticked).length;
if (numberOfDailyTasksTicked === 0) {
return 0.0;
}
logDebug("DashboardClient", "progressPercentage", "numberOfDailyTasksTicked", numberOfDailyTasksTicked);
const result = (numberOfDailyTasksTicked * 100) / numberOfDailyTasks;
logDebug("DashboardClient", "progressPercentage", "total", result);
return result % 1 === 0 ? result : result.toFixed(1);
};
const progressLabel = () => {
switch (taskPeriod) {
case "today":
return "TODAY";
case "thisWeek":
return "THIS WEEK";
case "lastWeek":
return "LAST WEEK";
case "nextWeek":
return "NEXT WEEK";
default:
return "TODAY";
}
};
const updateTaskHandler = (task, event) => {
const newDailyTask = {
name: task.taskName,
description: task.taskDescription,
date: task.date,
ticked: event.target.checked
};
updateDailyTask.mutate({
taskIdentifier: task.identifier,
newTask: newDailyTask
});
};
const taskPeriodRefreshHandler = () => {
clientTasks.refetch();
};
const taskPeriodHandler = (event) => {
logDebug("Dashboard", "taskPeriodHandler", "value", event.target.value);
setTaskPeriod(event.target.value);
const now = moment();
let initDate;
let finalDate;
switch (event.target.value) {
case "today": {
const today = now.toDate();
initDate = today;
finalDate = today;
break;
}
case "thisWeek": {
initDate = now.startOf("week")
.toDate();
finalDate = now.endOf("week")
.toDate();
break;
}
case "lastWeek": {
const firstDayOfWeek = now.startOf("week");
initDate = moment(firstDayOfWeek)
.subtract(7, "days")
.toDate();
finalDate = moment(firstDayOfWeek)
.subtract(1, "days")
.toDate();
break;
}
case "nextWeek": {
const firstDayOfWeek = now.startOf("week");
initDate = moment(firstDayOfWeek)
.add(7, "days")
.toDate();
finalDate = moment(firstDayOfWeek)
.add(13, "days")
.toDate();
break;
}
default: {
const today = now.toDate();
initDate = today;
finalDate = today;
break;
}
}
logDebug("Dashboard", "taskPeriodHandler", "date range", initDate, finalDate);
setStartDate(initDate);
setEndDate(finalDate);
};
// endregion
// region Code with queries to API
const clientTasks = useQuery(["getDailyTasksByClientAndDay", customerIdentifier, startDate, endDate],
() => getDailyTasksByClientAndDay(customerIdentifier, startDate, endDate), {
onError: async (err) => {
logError("DashboardClient",
"useQuery getDailyTasksByClientAndDay",
"Error:", err);
},
onSettled: (data) => {
setTaskProgress(progressPercentage(data));
}
});
const enrollmentStatus = useQuery(["getEnrollmentStatus", customerIdentifier],
() => enrollmentProcessStatus(customerIdentifier), {
onError: async (err) => {
logError("DashboardClient",
"useQuery getEnrollmentStatus",
"Error:", err);
},
onSuccess: (data) => {
const coach = data.coach ? {
name: `${data.coach.firstName} ${data.coach.lastName}`
} : null;
setEnrollment({
coach,
status: data.status
});
if (data.status === "ACCEPTED") {
setActiveCoachStep(3);
} else if (data.status === "PENDING") {
setActiveCoachStep(1);
}
}
});
const enrollmentFinish = useMutation(
({
// eslint-disable-next-line no-shadow
customerIdentifier,
flag
}) => enrollmentProcessFinish(customerIdentifier, flag),
{
onError: async (error) => {
logError("DashboardClient",
"useMutation enrollmentProcessBreak",
"Error:", error.response);
logError("DashboardClient", "useMutation enrollmentProcessBreak", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
updateNotificationHandler(true, errorCode.msg, errorCode.level);
},
// eslint-disable-next-line no-unused-vars
onSuccess: async (data, variables) => {
if (data.status === "AVAILABLE") {
setEnrollment(update(enrollment, {
coach: { $set: null },
status: { $set: "AVAILABLE" }
}));
setActiveCoachStep((prevState) => prevState - 1);
notificationHandler();
} else if (data.status === "ACCEPTED") {
setEnrollment(update(enrollment, {
status: { $set: "ACCEPTED" }
}));
setActiveCoachStep((prevState) => prevState + 1);
notificationHandler();
} else {
const errorCode = ErrorMessage.CODE_9999;
updateNotificationHandler(true, errorCode.msg, errorCode.level);
}
}
}
);
const nextAppointment = useFetchNextClientAppointment(customerIdentifier);
const updateDailyTask = useMutation(
({
taskIdentifier,
newTask
}) => updateDailyTaskByUUID(taskIdentifier, newTask),
{
onError: async (error) => {
logError("DashboardClient", "updateDailyTaskMutation", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
updateNotificationHandler(true, errorCode.msg, errorCode.level);
},
onSuccess: async (data, variables) => {
await queryClient.cancelQueries(["getDailyTasksByClientAndDay", customerIdentifier, startDate, endDate]);
logDebug("Planner",
"DashboardClient",
"Response:", data);
queryClient.setQueryData(["getDailyTasksByClientAndDay", customerIdentifier, startDate, endDate], (oldData) => {
logDebug("Planner",
"DashboardClient",
"Old Data:", oldData);
const index = oldData.dailyTasks.findIndex(
(dailyTask) => dailyTask.identifier === variables.taskIdentifier
);
const newData = update(oldData, {
dailyTasks: {
[index]: { $set: data.dailyTasks[0] }
}
});
logDebug("Planner",
"DashboardClient",
"New Data:", newData);
return newData;
});
setTaskProgress(progressPercentage(data));
notificationHandler();
}
}
);
// endregion
// region Native reactj code
useEffect(() => {
if (startDate && endDate) {
clientTasks.refetch();
}
}, [startDate, endDate]);
// endregion
return (
<Page
title="Dashboard"
isError={clientTasks.isError || enrollmentStatus.isError}
isLoading={enrollmentStatus.isLoading}
>
<>
<Grid
container
spacing={3}
>
<Grid
item
lg={3}
sm={6}
xs={12}
>
<TasksProgress
isLoading={clientTasks.isFetching}
type={progressLabel(taskPeriod)}
progress={tasksProgress}
/>
</Grid>
<Grid
item
lg={3}
sm={6}
xs={12}
>
<MyCoach
isLoading={enrollmentStatus.isFetching}
coachName={enrollment.coach ? enrollment.coach.name : null}
/>
</Grid>
<Grid
item
lg={4}
sm={6}
xs={12}
>
<NextAppointment isLoading={nextAppointment.isFetching}
date={nextAppointment.isError ? null : getTimezoneDateTime(nextAppointment.data?.appointments[0].dttmStarts)}
/>
</Grid>
</Grid>
<Grid
container
spacing={3}
>
<Grid
item
xs={12}
>
<EnrollmentCard
isLoading={enrollmentStatus.isFetching}
activeCoachStep={activeCoachStep}
setActiveCoachStep={setActiveCoachStep}
customerIdentifier={customerIdentifier}
enrollmentFinish={enrollmentFinish}
enrollmentStatus={enrollment?.status}
/>
</Grid>
{notification.enable
? (
<Grid
item
xs={12}
>
<Notification
className={classes.notification}
collapse
open={notification.enable}
openHandler={notificationHandler}
level={notification.level}
message={notification.message}
/>
</Grid>
)
: null}
<Grid
item
lg={8}
md={12}
xl={9}
xs={12}
>
<Tasks
tasks={clientTasks.data ? clientTasks.data.dailyTasks : []}
taskPeriod={taskPeriod}
taskPeriodHandler={taskPeriodHandler}
taskPeriodRefreshHandler={taskPeriodRefreshHandler}
updateTaskHandler={updateTaskHandler}
/>
</Grid>
</Grid>
</>
</Page>
);
}
Example #24
Source File: GameListItem.jsx From airboardgame with MIT License | 4 votes |
GameListItem = ({
game: {
owner,
id,
board: {
minAge,
materialLanguage,
duration,
playerCount,
published,
imageUrl,
keepTitle,
},
},
game,
userId,
onClick: propOnClick,
onDelete,
isAdmin = false,
studio = false,
}) => {
const { t, i18n } = useTranslation();
const history = useHistory();
const queryClient = useQueryClient();
const deleteMutation = useMutation((gameId) => deleteGame(gameId), {
onSuccess: () => {
queryClient.invalidateQueries("ownGames");
queryClient.invalidateQueries("games");
},
});
const realImageUrl = media2Url(imageUrl);
const [showImage, setShowImage] = React.useState(Boolean(realImageUrl));
const translation = React.useMemo(
() => getBestTranslationFromConfig(game.board, i18n.languages),
[game, i18n.languages]
);
const onClick = React.useCallback(
(e) => {
e.stopPropagation();
e.preventDefault();
if (propOnClick) {
return propOnClick(id);
} else {
history.push(`/playgame/${id}`);
}
},
[history, id, propOnClick]
);
const onShare = React.useCallback(
async (e) => {
e.stopPropagation();
e.preventDefault();
await navigator.clipboard.writeText(getGameUrl(id));
toast.info(t("Url copied to clipboard!"), { autoClose: 1000 });
},
[id, t]
);
const deleteGameHandler = async () => {
confirmAlert({
title: t("Confirmation"),
message: t("Do you really want to remove this game?"),
buttons: [
{
label: t("Yes"),
onClick: async () => {
try {
deleteMutation.mutate(id);
if (onDelete) onDelete(id);
toast.success(t("Game deleted"), { autoClose: 1500 });
} catch (e) {
if (e.message === "Forbidden") {
toast.error(t("Action forbidden. Try logging in again."));
} else {
console.log(e);
toast.error(t("Error while deleting game. Try again later..."));
}
}
},
},
{
label: t("No"),
onClick: () => {},
},
],
});
};
let playerCountDisplay = undefined;
if (playerCount && playerCount.length) {
const [min, max] = playerCount;
if (min === max) {
if (max === 9) {
playerCountDisplay = ["9+"];
} else {
playerCountDisplay = [max];
}
} else {
if (max === 9) {
playerCountDisplay = [min, "9+"];
} else {
playerCountDisplay = [min, max];
}
}
}
let durationDisplay = undefined;
if (duration && duration.length) {
const [min, max] = duration;
if (min === max) {
if (max === 90) {
durationDisplay = "90+";
} else {
durationDisplay = `~${max}`;
}
} else {
if (max === 90) {
durationDisplay = `${min}~90+`;
} else {
durationDisplay = `${min}~${max}`;
}
}
}
let materialLanguageDisplay = t(materialLanguage);
const owned = userId && (userId === owner || !owner);
return (
<Game other={!owned && studio}>
<a href={`/playgame/${id}`} className="img-wrapper button">
<span onClick={onClick}>
{showImage && (
<>
<span
className="back"
style={{ backgroundImage: `url(${realImageUrl})` }}
/>
<img
className="img"
src={realImageUrl}
alt={translation.name}
onError={() => setShowImage(false)}
/>
</>
)}
{(!showImage || keepTitle) && <h2>{translation.name}</h2>}
</span>
</a>
<span className="extra-actions">
<a
href={getGameUrl(id)}
className="button edit icon-only success"
onClick={onShare}
>
<img
src="https://icongr.am/feather/share-2.svg?size=16&color=ffffff"
alt={t("Share game link")}
title={t("Share game link")}
/>
</a>
{(owned || isAdmin) && (
<>
<button
onClick={deleteGameHandler}
className="button edit icon-only error"
>
<img
src="https://icongr.am/feather/trash.svg?size=16&color=ffffff"
alt={t("Delete")}
title={t("Delete")}
/>
</button>
<Link to={`/game/${id}/edit`} className="button edit icon-only ">
<img
src="https://icongr.am/feather/edit.svg?size=16&color=ffffff"
alt={t("Edit")}
title={t("Edit")}
/>
</Link>
</>
)}
</span>
{!published && (
<img
className="unpublished"
src="https://icongr.am/entypo/eye-with-line.svg?size=32&color=888886"
alt={t("Unpublished")}
/>
)}
<div className="details">
{playerCountDisplay && (
<span>
{playerCountDisplay.length === 2 &&
t("{{min}} - {{max}} players", {
min: playerCountDisplay[0],
max: playerCountDisplay[1],
})}
{playerCountDisplay.length === 1 &&
t("{{count}} player", {
count: playerCountDisplay[0],
})}
</span>
)}
{durationDisplay && <span>{durationDisplay} mins</span>}
{minAge && <span>age {minAge}+</span>}
{materialLanguageDisplay && <span>{materialLanguageDisplay}</span>}
</div>
<h2 className="game-name">{translation.name}</h2>
<p className="baseline">{translation.baseline}</p>
</Game>
);
}
Example #25
Source File: MediaLibraryModal.jsx From airboardgame with MIT License | 4 votes |
MediaLibraryModal = ({ show, setShow, onSelect }) => {
const { t } = useTranslation();
const {
getLibraryMedia,
addMedia,
removeMedia,
libraries,
} = useMediaLibrary();
const queryClient = useQueryClient();
const [tab, setTab] = React.useState(libraries[0].id);
const currentLibrary = libraries.find(({ id }) => id === tab);
const { isLoading, data = [] } = useQuery(
`media__${tab}`,
() => getLibraryMedia(currentLibrary),
{
enabled: show,
}
);
const handleSelect = React.useCallback(
(media) => {
onSelect(media);
setShow(false);
},
[onSelect, setShow]
);
const uploadMediaMutation = useMutation(
async (files) => {
if (files.length === 1) {
return [await addMedia(currentLibrary, files[0])];
} else {
return Promise.all(files.map((file) => addMedia(currentLibrary, file)));
}
},
{
onSuccess: (result) => {
if (result.length === 1) {
// If only one file is processed
handleSelect(result[0].content);
}
queryClient.invalidateQueries(`media__${tab}`);
},
}
);
const onRemove = React.useCallback(
(key) => {
confirmAlert({
title: t("Confirmation"),
message: t("Do you really want to remove this media?"),
buttons: [
{
label: t("Yes"),
onClick: async () => {
try {
await removeMedia(key);
toast.success(t("Media deleted"), { autoClose: 1500 });
} catch (e) {
if (e.message === "Forbidden") {
toast.error(t("Action forbidden. Try logging in again."));
} else {
console.log(e);
toast.error(
t("Error while deleting media. Try again later...")
);
}
}
},
},
{
label: t("No"),
onClick: () => {},
},
],
});
},
[removeMedia, t]
);
const { getRootProps, getInputProps } = useDropzone({
onDrop: uploadMediaMutation.mutate,
});
return (
<Modal
title={t("Media library")}
show={show}
setShow={setShow}
position="left"
>
<nav className="tabs">
{libraries.map(({ id, name }) => (
<a
onClick={() => setTab(id)}
className={tab === id ? "active" : ""}
style={{ cursor: "pointer" }}
key={id}
>
{name}
</a>
))}
</nav>
<section>
{libraries.map(({ id, name }, index) => {
if (tab === id) {
return (
<div key={id}>
{index === 0 && (
<>
<h3>{t("Add file")}</h3>
<div
{...getRootProps()}
style={{
border: "3px dashed white",
margin: "0.5em",
padding: "0.5em",
textAlign: "center",
}}
>
<input {...getInputProps()} />
<p>{t("Click or drag'n'drop file here")}</p>
</div>
</>
)}
<h3>{name}</h3>
{!isLoading && (
<ImageGrid>
{data.map((key) => (
<div key={key}>
<img
src={`${API_BASE}/${key}`}
onClick={() => handleSelect(key)}
/>
<button
onClick={() => onRemove(key)}
className="button icon-only remove"
title={t("Remove")}
>
X
</button>
</div>
))}
</ImageGrid>
)}
</div>
);
} else {
return null;
}
})}
</section>
</Modal>
);
}
Example #26
Source File: index.js From flame-coach-web with MIT License | 4 votes |
MeasuresView = ({
customerIdentifier
}) => {
const classes = useStyles();
const queryClient = useQueryClient();
const isMobile = useIsMobile();
const [timeFrameWeight, setTimeFrameWeight] = useState("1_MONTH");
const [dateWeightAdding, setDateWeightAdding] = useState(moment()
.utc());
const [weightAdding, setWeightAdding] = useState(NaN);
const [notification, setNotification] = useState({
enable: false,
message: "",
level: "INFO"
});
const updateNotificationHandler = (enable, message, level) => {
setNotification({
enable,
message,
level
});
};
const notificationHandler = () => {
setNotification(update(notification,
{
enable: { $set: false }
}));
};
const personalData = useFetchClientPersonalInformation(customerIdentifier);
const {
isFetching,
isError,
data
} = useFetchWeightClient(customerIdentifier);
const { mutate: mutateAddWeight } = useAddWeightClient();
const { mutate: mutateDeleteWeight } = useDeleteWeightClient();
const filteredData = filterWeightsPerTimeRange(data, moment()
.utc(), timeFrameWeight);
const addWeightHandler = (weight, date) => {
if (Number.isNaN(weight)) {
const errorMessage = ErrorMessage.CODE_0006;
updateNotificationHandler(true, errorMessage.msg, errorMessage.level);
} else if (date === null) {
const errorMessage = ErrorMessage.CODE_0007;
updateNotificationHandler(true, errorMessage.msg, errorMessage.level);
} else {
mutateAddWeight({
clientIdentifier: customerIdentifier,
weight,
utcDate: date
}, {
onError: (error) => {
logError("Measures",
"useMutation addNewWeight",
"Error:", error.response);
logError("Measures", "useMutation addNewWeight", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
updateNotificationHandler(true, errorCode.msg, errorCode.level);
},
onSuccess: () => {
queryClient.invalidateQueries(["getWeightClient", customerIdentifier]);
const successMessage = InfoMessage.CODE_0002;
updateNotificationHandler(true, successMessage.msg, successMessage.level);
}
});
}
};
const deleteWeightHandler = (event) => {
mutateDeleteWeight({
clientIdentifier: customerIdentifier,
identifier: event.identifier
}, {
onError: (error) => {
logError("Measures",
"useMutation deleteHandler",
"Error:", error.response);
logError("Measures", "useMutation deleteHandler", "Error Details:", error.response.data.detail);
const errorCode = ErrorMessage.fromCode(error.response.data.code);
updateNotificationHandler(true, errorCode.msg, errorCode.level);
},
onSuccess: () => {
queryClient.invalidateQueries(["getWeightClient", customerIdentifier]);
}
});
};
return (
<Page
title="Measures"
isError={isError || personalData.isError}
isLoading={isFetching || personalData.isFetching}
>
<Grid
container
spacing={1}
direction="row"
>
<Grid
item
xs={12}
md={9}
>
<Grid
container
spacing={1}
direction="column"
>
<Grid
item
xs={12}
>
<Grid container spacing={1}>
<Grid item md={9} xs={12}>
<WeightChart
className={isMobile ? classes.weightGraphicMobileCardContent
: classes.weightGraphicCardContent}
isMobile={isMobile}
timeFrame={timeFrameWeight}
dataChart={filteredData}
measureUnit={extractWeightType(personalData.data?.measureType.value)}
/>
</Grid>
<Grid item md={3} xs={12}>
<Events
className={classes.eventsCardContent}
dataEvents={filteredData}
onDeleteHandle={deleteWeightHandler}
measureUnit={extractWeightType(personalData.data?.measureType.value)}
/>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid
item
md={3}
xs={12}
>
<Filters
className={isMobile ? null : classes.filtersCardContent}
enableAddingWeight
timeFrame={timeFrameWeight}
onChangeTimeFrameHandler={(newTimeFrame) => setTimeFrameWeight(newTimeFrame)}
date={dateWeightAdding}
onChangeDateHandler={(newDate) => setDateWeightAdding(newDate)}
weight={weightAdding}
onChangeWeightHandler={(newWeight) => setWeightAdding(newWeight)}
onAddWeightHandler={addWeightHandler}
/>
</Grid>
{notification.enable
? (
<Grid
item
xs={12}
md={9}
>
<Notification
collapse
open={notification.enable}
openHandler={notificationHandler}
level={notification.level}
message={notification.message}
/>
</Grid>
)
: null}
</Grid>
</Page>
);
}
Example #27
Source File: hooks.js From idena-web with MIT License | 4 votes |
export function useDeferredVotes() {
const queryClient = useQueryClient()
const {coinbase, privateKey} = useAuthState()
const failToast = useFailToast()
const {t} = useTranslation()
const router = useRouter()
const {data: deferredVotes, isFetched} = useQuery(
'useDeferredVotes',
() => getDeferredVotes(coinbase),
{
enabled: !!coinbase,
initialData: [],
}
)
const {
data: {currentBlock},
isFetched: isBlockFetched,
} = useSyncing({
refetchIntervalInBackground: true,
refetchInterval: REFETCH_INTERVAL,
enabled: deferredVotes.length > 0,
})
const addVote = async vote => {
await addDeferredVote({coinbase, ...vote})
queryClient.invalidateQueries('useDeferredVotes')
}
const deleteVote = async id => {
await deleteDeferredVote(id)
queryClient.invalidateQueries('useDeferredVotes')
}
const estimateSendVote = async vote => {
const voteData = {
method: 'sendVote',
contractHash: vote.contractHash,
amount: vote.amount,
args: vote.args,
}
return estimateCallContract(privateKey, voteData)
}
const estimateProlong = async contractHash => {
try {
await estimateCallContract(privateKey, {
method: 'prolongVoting',
contractHash,
})
return true
} catch (e) {
return false
}
}
const sendVote = async (vote, skipToast) => {
function showError(message) {
failToast(
`${t('Can not send scheduled transaction:', {
nsSeparator: '|',
})} ${message}`
)
}
try {
const voteData = {
method: 'sendVote',
contractHash: vote.contractHash,
amount: vote.amount,
args: vote.args,
}
console.log(`sending deferred vote, contract: ${vote.contractHash}`)
const {
receipt: {gasCost, txFee},
} = await estimateCallContract(privateKey, voteData)
const voteResponse = await callContract(privateKey, {
...voteData,
gasCost: Number(gasCost),
txFee: Number(txFee),
})
await updateDeferredVote(vote.id, {
type: DeferredVoteType.Success,
txHash: voteResponse,
})
queryClient.invalidateQueries('useDeferredVotes')
} catch (e) {
switch (e.message) {
case 'too early to accept open vote': {
try {
const readContractData = createContractDataReader(vote.contractHash)
const startBlock = await readContractData('startBlock', 'uint64')
const votingDuration = await readContractData(
'votingDuration',
'uint64'
)
const nextVoteBlock = startBlock + votingDuration
if (nextVoteBlock > vote.block) {
await updateDeferredVote(vote.id, {
block: nextVoteBlock,
})
queryClient.invalidateQueries('useDeferredVotes')
}
} catch (err) {
console.error(err)
} finally {
if (!skipToast) showError(e.message)
}
break
}
case 'too late to accept open vote':
case 'quorum is not reachable': {
if (await estimateProlong(vote.contractHash)) {
if (!skipToast)
failToast({
title: t('Can not cast public vote. Please, prolong voting'),
onAction: () => {
router.push(`/oracles/view?id=${vote.contractHash}`)
},
actionContent: t('Open voting'),
})
const readContractData = createContractDataReader(vote.contractHash)
const votingDuration = await readContractData(
'votingDuration',
'uint64'
)
await updateDeferredVote(vote.id, {
block: vote.block + votingDuration,
})
queryClient.invalidateQueries('useDeferredVotes')
} else {
if (!skipToast) showError(e.message)
deleteVote(vote.id)
}
break
}
case 'insufficient funds': {
showError(e.message)
break
}
default: {
showError(e.message)
deleteVote(vote.id)
}
}
}
}
const available = deferredVotes.filter(x => x.block < currentBlock)
return [
{
votes: available,
all: deferredVotes,
isReady: isFetched && isBlockFetched,
},
{addVote, sendVote, estimateSendVote, estimateProlong, deleteVote},
]
}
Example #28
Source File: hooks.js From idena-web with MIT License | 4 votes |
export function useRotatingAds(limit = 3) {
const rpcFetcher = useRpcFetcher()
const burntCoins = useTargetedAds()
const addresses = [...new Set(burntCoins?.map(({address}) => address))]
const profileHashes = useQueries(
addresses.map(address => ({
queryKey: ['dna_identity', [address]],
queryFn: rpcFetcher,
staleTime: 5 * 60 * 1000,
notifyOnChangeProps: ['data', 'error'],
select: selectProfileHash,
}))
)
const {decodeAd, decodeProfile} = useProtoProfileDecoder()
const profiles = useQueries(
profileHashes.map(({data: cid}) => ({
queryKey: ['ipfs_get', [cid]],
queryFn: rpcFetcher,
enabled: Boolean(cid),
select: decodeProfile,
staleTime: Infinity,
notifyOnChangeProps: ['data'],
}))
)
const profileAdVotings = useQueries(
profiles
.map(({data}) => data?.ads)
.flat()
.map(ad => ({
queryKey: ['profileAdVoting', ad?.contract],
queryFn: () => getAdVoting(ad?.contract),
enabled: Boolean(ad?.contract),
staleTime: 5 * 60 * 1000,
select: data => ({...data, cid: ad?.cid}),
}))
)
const approvedProfileAdVotings = profileAdVotings?.filter(({data}) =>
isApprovedVoting(data)
)
const queryClient = useQueryClient()
const decodedProfileAds = useQueries(
burntCoins
?.filter(({key}) =>
approvedProfileAdVotings.some(
({data}) => data?.cid === AdBurnKey.fromHex(key).cid
)
)
.slice(0, limit)
.map(({key, address, amount}) => {
const {cid} = AdBurnKey.fromHex(key)
return {
queryKey: ['decodedRotatingAd', [cid]],
queryFn: async () => ({
...decodeAd(
await queryClient
.fetchQuery({
queryKey: ['ipfs_get', [cid]],
queryFn: rpcFetcher,
staleTime: Infinity,
})
.catch(() => '')
),
cid,
author: address,
amount: Number(amount),
}),
enabled: Boolean(cid),
staleTime: Infinity,
}
}) ?? []
)
return decodedProfileAds?.map(x => x.data).filter(Boolean) ?? []
}
Example #29
Source File: index.js From idena-web with MIT License | 4 votes |
export default function HomePage() {
const queryClient = useQueryClient()
const {
t,
i18n: {language},
} = useTranslation()
const [identity] = useIdentity()
const {
address,
state,
online,
delegatee,
delegationEpoch,
pendingUndelegation,
canMine,
canInvite,
canTerminate,
canActivateInvite,
} = identity
const router = useRouter()
const epoch = useEpoch()
const {privateKey} = useAuthState()
const userStatAddress = useBreakpointValue([
address ? `${address.substr(0, 3)}...${address.substr(-4, 4)}` : '',
address,
])
const [showValidationResults, setShowValidationResults] = React.useState()
const {onCopy} = useClipboard(address)
const successToast = useSuccessToast()
const {
isOpen: isOpenKillForm,
onOpen: onOpenKillForm,
onClose: onCloseKillForm,
} = useDisclosure()
const {
data: {balance, stake, replenishedStake},
} = useQuery(['get-balance', address], () => fetchBalance(address), {
initialData: {balance: 0, stake: 0, replenishedStake: 0},
enabled: !!address,
refetchInterval: 30 * 1000,
})
const [validationResultSeen, setValidationResultSeen] = useValidationResults()
useEffect(() => {
if (epoch) {
const {epoch: epochNumber} = epoch
if (epochNumber) {
queryClient.invalidateQueries('get-balance')
setShowValidationResults(!validationResultSeen)
}
}
}, [epoch, queryClient, validationResultSeen])
const [dnaUrl] = React.useState(() =>
typeof window !== 'undefined'
? JSON.parse(sessionStorage.getItem('dnaUrl'))
: null
)
React.useEffect(() => {
if (dnaUrl) {
if (isValidDnaUrl(dnaUrl.route))
router.push({pathname: dnaUrl.route, query: dnaUrl.query})
sessionStorage.removeItem('dnaUrl')
}
}, [dnaUrl, router])
const toDna = toLocaleDna(language, {maximumFractionDigits: 4})
const [
currentOnboarding,
{dismissCurrentTask, next: nextOnboardingTask},
] = useOnboarding()
const eitherOnboardingState = (...states) =>
eitherState(currentOnboarding, ...states)
const {
isOpen: isOpenActivateInvitePopover,
onOpen: onOpenActivateInvitePopover,
onClose: onCloseActivateInvitePopover,
} = useDisclosure()
const activateInviteDisclosure = useDisclosure()
const activateInviteRef = React.useRef()
const {scrollTo: scrollToActivateInvite} = useScroll(activateInviteRef)
React.useEffect(() => {
if (
isOpenActivateInvitePopover ||
eitherState(
currentOnboarding,
onboardingShowingStep(OnboardingStep.StartTraining),
onboardingShowingStep(OnboardingStep.ActivateInvite)
)
) {
scrollToActivateInvite()
onOpenActivateInvitePopover()
} else onCloseActivateInvitePopover()
}, [
currentOnboarding,
isOpenActivateInvitePopover,
onCloseActivateInvitePopover,
onOpenActivateInvitePopover,
scrollToActivateInvite,
])
const canSubmitFlip = [
IdentityStatus.Verified,
IdentityStatus.Human,
IdentityStatus.Newbie,
].includes(state)
const [{idenaBotConnected}, {persistIdenaBot, skipIdenaBot}] = useAppContext()
const shouldStartIdenaJourney = currentOnboarding.matches(
OnboardingStep.StartTraining
)
const onboardingPopoverPlacement = useBreakpointValue(['top', 'bottom'])
const replenishStakeDisclosure = useDisclosure()
const {
onOpen: onOpenReplenishStakeDisclosure,
onClose: onCloseReplenishStakeDisclosure,
} = replenishStakeDisclosure
React.useEffect(() => {
if (Object.keys(router.query).find(q => q === 'replenishStake')) {
onOpenReplenishStakeDisclosure()
router.push('/home')
}
}, [onOpenReplenishStakeDisclosure, router])
const failToast = useFailToast()
const toast = useSuccessToast()
const stakingApy = useStakingApy()
const ads = useRotatingAds()
const isDesktop = useIsDesktop()
const spoilInviteDisclosure = useDisclosure()
return (
<Layout canRedirect={!dnaUrl} didConnectIdenaBot={idenaBotConnected}>
{!idenaBotConnected && (
<MyIdenaBotAlert onConnect={persistIdenaBot} onSkip={skipIdenaBot} />
)}
<Page pt="10" position="relative">
<MobileApiStatus top={idenaBotConnected ? 4 : 5 / 2} left={4} />
<Stack
w={['100%', '480px']}
direction={['column', 'row']}
spacing={['6', 10]}
>
<Box>
<Stack
spacing={[1, 8]}
w={['100%', '480px']}
align={['center', 'initial']}
ref={activateInviteRef}
>
<UserProfileCard
identity={identity}
my={[4, 0]}
></UserProfileCard>
{canActivateInvite && (
<Box w={['100%', 'initial']} pb={[8, 0]}>
<OnboardingPopover
isOpen={isOpenActivateInvitePopover}
placement={onboardingPopoverPlacement}
>
<PopoverTrigger>
{shouldStartIdenaJourney ? (
<StartIdenaJourneyPanel
onHasActivationCode={activateInviteDisclosure.onOpen}
/>
) : state === IdentityStatus.Invite ? (
<AcceptInvitationPanel />
) : (
<ActivateInvitationPanel />
)}
</PopoverTrigger>
{shouldStartIdenaJourney ? (
<StartIdenaJourneyOnboardingContent
onDismiss={() => {
dismissCurrentTask()
onCloseActivateInvitePopover()
}}
/>
) : state === IdentityStatus.Invite ? (
<AcceptInviteOnboardingContent
onDismiss={() => {
dismissCurrentTask()
onCloseActivateInvitePopover()
}}
/>
) : (
<ActivateInviteOnboardingContent
onDismiss={() => {
dismissCurrentTask()
onCloseActivateInvitePopover()
}}
/>
)}
</OnboardingPopover>
</Box>
)}
{showValidationResults && (
<ValidationReportSummary
onClose={() => setValidationResultSeen()}
/>
)}
<UserStatList title={t('My Wallet')}>
<UserStatistics label={t('Address')} value={userStatAddress}>
<ExternalLink
display={['none', 'initial']}
href={`https://scan.idena.io/address/${address}`}
>
{t('Open in blockchain explorer')}
</ExternalLink>
<CopyIcon
display={['inline', 'none']}
mt="3px"
ml="4px"
boxSize={4}
fill="#96999e"
onClick={() => {
onCopy()
successToast({
title: 'Address copied!',
duration: '5000',
})
}}
/>
</UserStatistics>
<UserStatistics label={t('Balance')} value={toDna(balance)}>
<TextLink display={['none', 'initial']} href="/wallets">
<Stack isInline spacing={0} align="center" fontWeight={500}>
<Text as="span">{t('Send')}</Text>
<ChevronRightIcon boxSize={4} />
</Stack>
</TextLink>
</UserStatistics>
<Button
display={['initial', 'none']}
onClick={() => {
router.push('/wallets')
}}
w="100%"
h={10}
fontSize="15px"
variant="outline"
color="blue.500"
border="none"
borderColor="transparent"
>
{t('Send iDNA')}
</Button>
</UserStatList>
<Stack spacing="2" w="full">
{Boolean(state) && state !== IdentityStatus.Undefined && (
<UserStatList title={t('Stake')}>
<Stack direction={['column', 'row']} spacing={['5', 0]}>
<Stack spacing={['5', '3']} flex={1}>
<Stack spacing="5px">
<UserStat>
<Flex
direction={['row', 'column']}
justify={['space-between', 'flex-start']}
>
<UserStatLabel
color={[null, 'muted']}
fontSize={['mdx', 'md']}
fontWeight={[400, 500]}
lineHeight="4"
>
{t('Balance')}
</UserStatLabel>
<UserStatValue
fontSize={['mdx', 'md']}
lineHeight="4"
mt={[null, '3px']}
>
{toDna(
state === IdentityStatus.Newbie
? (stake - (replenishedStake ?? 0)) * 0.25
: stake
)}
</UserStatValue>
</Flex>
</UserStat>
<Button
display={['none', 'inline-flex']}
variant="link"
color="blue.500"
fontWeight={500}
lineHeight="4"
w="fit-content"
_hover={{
background: 'transparent',
textDecoration: 'underline',
}}
_focus={{
outline: 'none',
}}
onClick={replenishStakeDisclosure.onOpen}
>
{t('Add stake')}
<ChevronRightIcon boxSize="4" />
</Button>
</Stack>
{stake > 0 && state === IdentityStatus.Newbie && (
<AnnotatedUserStatistics
annotation={t(
'You need to get Verified status to get the locked funds into the normal wallet'
)}
label={t('Locked')}
value={toDna(
(stake - (replenishedStake ?? 0)) * 0.75
)}
/>
)}
</Stack>
<Stack spacing="5px" flex={1}>
<UserStat flex={0}>
<Flex
direction={['row', 'column']}
justify={['space-between', 'flex-start']}
>
<UserStatLabel
color={[null, 'muted']}
fontSize={['mdx', 'md']}
fontWeight={[400, 500]}
lineHeight="4"
>
{t('APY')}
</UserStatLabel>
<UserStatValue
fontSize={['mdx', 'md']}
lineHeight="4"
mt={[null, '3px']}
>
{stakingApy > 0 ? toPercent(stakingApy) : '--'}
</UserStatValue>
</Flex>
</UserStat>
<ExternalLink
href={`https://idena.io/staking?amount=${Math.floor(
state === IdentityStatus.Newbie
? (stake - (replenishedStake ?? 0)) * 0.25
: stake
)}`}
display={['none', 'inline-flex']}
>
{t('Staking calculator')}
</ExternalLink>
</Stack>
</Stack>
<Stack display={['inline-flex', 'none']}>
<Button
onClick={replenishStakeDisclosure.onOpen}
w="100%"
h={10}
fontSize="15px"
variant="outline"
color="blue.500"
border="none"
borderColor="transparent"
>
{t('Add stake')}
</Button>
<Button
onClick={() => {
openExternalUrl(
`https://idena.io/staking?amount=${Math.floor(
state === IdentityStatus.Newbie
? (stake - (replenishedStake ?? 0)) * 0.25
: stake
)}`
)
}}
w="100%"
h={10}
fontSize="15px"
variant="outline"
color="blue.500"
border="none"
borderColor="transparent"
>
{t('Staking calculator')}
</Button>
</Stack>
</UserStatList>
)}
<StakingAlert />
</Stack>
</Stack>
{ads?.length > 0 && !isDesktop && (
<Box display={['block', 'none']} mt="6">
<AdCarousel ads={ads} />
</Box>
)}
</Box>
<Stack spacing={[0, 10]} flexShrink={0} w={['100%', 200]}>
{address && privateKey && canMine && (
<Box minH={62} mt={[1, 6]}>
<OnboardingPopover
isOpen={eitherOnboardingState(
onboardingShowingStep(OnboardingStep.ActivateMining)
)}
>
<PopoverTrigger>
<Box
bg="white"
position={
eitherOnboardingState(
onboardingShowingStep(OnboardingStep.ActivateMining)
)
? 'relative'
: 'initial'
}
borderRadius={['mdx', 'md']}
p={[0, 2]}
m={[0, -2]}
zIndex={2}
>
<ActivateMiningForm
privateKey={privateKey}
isOnline={online}
delegatee={delegatee}
delegationEpoch={delegationEpoch}
pendingUndelegation={pendingUndelegation}
onShow={nextOnboardingTask}
/>
</Box>
</PopoverTrigger>
<OnboardingPopoverContent
title={t('Activate mining status')}
onDismiss={nextOnboardingTask}
>
<Text>
{t(
`To become a validator of Idena blockchain you can activate your mining status. Keep your node online to mine iDNA coins.`
)}
</Text>
</OnboardingPopoverContent>
</OnboardingPopover>
</Box>
)}
<Stack spacing={[0, 1]} align="flex-start">
<WideLink
display={['initial', 'none']}
label="Open in blockchain explorer"
href={`https://scan.idena.io/address/${address}`}
isNewTab
>
<Box
boxSize={8}
backgroundColor="brandBlue.10"
borderRadius="10px"
>
<OpenExplorerIcon boxSize={5} mt="6px" ml="6px" />
</Box>
</WideLink>
<WideLink
mt={[0, '2px']}
label={t('Training validation')}
onClick={() => router.push('/try')}
>
<Box
boxSize={[8, 5]}
backgroundColor={['brandBlue.10', 'initial']}
borderRadius="10px"
>
<TestValidationIcon
color="blue.500"
boxSize={5}
mt={['6px', 0]}
ml={['6px', 0]}
/>
</Box>
</WideLink>
<WideLink
label={t('New voting')}
onClick={() => router.push('/oracles/new')}
>
<Box
boxSize={[8, 5]}
backgroundColor={['brandBlue.10', 'initial']}
borderRadius="10px"
>
<OracleIcon
color="blue.500"
boxSize={5}
mt={['6px', 0]}
ml={['6px', 0]}
/>
</Box>
</WideLink>
<WideLink
label={t('New ad')}
onClick={() => router.push('/adn/new')}
>
<Box
boxSize={[8, 5]}
backgroundColor={['brandBlue.10', 'initial']}
borderRadius="10px"
>
<AdsIcon
color="blue.500"
boxSize={5}
mt={['6px', 0]}
ml={['6px', 0]}
/>
</Box>
</WideLink>
<WideLink
label={t('New flip')}
isDisabled={!canSubmitFlip}
onClick={() => router.push('/flips/new')}
>
<Box
boxSize={[8, 5]}
backgroundColor={['brandBlue.10', 'initial']}
borderRadius="10px"
>
<PhotoIcon
color="blue.500"
boxSize={5}
mt={['6px', 0]}
ml={['6px', 0]}
/>
</Box>
</WideLink>
<WideLink
label={t('Invite')}
onClick={() => router.push('/contacts?new')}
isDisabled={!canInvite}
>
<Box
boxSize={[8, 5]}
backgroundColor={['brandBlue.10', 'initial']}
borderRadius="10px"
>
<AddUserIcon
color="blue.500"
boxSize={5}
mt={['6px', 0]}
ml={['6px', 0]}
/>
</Box>
</WideLink>
<WideLink
label={t('Spoil invite')}
onClick={spoilInviteDisclosure.onOpen}
>
<Box
boxSize={[8, 5]}
backgroundColor={['brandBlue.10', 'initial']}
borderRadius="10px"
>
<PooIcon
color="blue.500"
boxSize="5"
mt={['6px', 0]}
ml={['6px', 0]}
/>
</Box>
</WideLink>
<WideLink
label={t('Terminate')}
onClick={onOpenKillForm}
isDisabled={!canTerminate}
>
<Box
boxSize={[8, 5]}
backgroundColor={['brandBlue.10', 'initial']}
borderRadius="10px"
>
<DeleteIcon
color="blue.500"
boxSize={5}
mt={['6px', 0]}
ml={['6px', 0]}
/>
</Box>
</WideLink>
</Stack>
</Stack>
</Stack>
<KillForm isOpen={isOpenKillForm} onClose={onCloseKillForm}></KillForm>
<ActivateInvitationDialog {...activateInviteDisclosure} />
<ReplenishStakeDrawer
{...replenishStakeDisclosure}
onSuccess={React.useCallback(
hash => {
toast({
title: t('Transaction sent'),
description: hash,
})
onCloseReplenishStakeDisclosure()
},
[onCloseReplenishStakeDisclosure, t, toast]
)}
onError={failToast}
/>
<SpoilInviteDrawer
{...spoilInviteDisclosure}
onSuccess={() => {
successToast(t('Invitation is successfully spoiled'))
spoilInviteDisclosure.onClose()
}}
onFail={failToast}
/>
</Page>
</Layout>
)
}