diff --git a/frontend/src/components/Dialogs/ClaimRewardDialog.tsx b/frontend/src/components/Dialogs/ClaimRewardDialog.tsx new file mode 100644 index 00000000..79b23a22 --- /dev/null +++ b/frontend/src/components/Dialogs/ClaimRewardDialog.tsx @@ -0,0 +1,102 @@ +import React, { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Typography, + Alert, + TextField, +} from '@mui/material'; +import RedeemIcon from '@mui/icons-material/Redeem'; + +interface Props { + open: boolean; + onClose: () => void; + t: (key: string) => string; // Pass the translation function +} + +const ClaimRewardDialog = ({ open, onClose, t }: Props): JSX.Element => { + const [invoiceAmount, setInvoiceAmount] = useState(''); + const [showFailedClaimInfo, setShowFailedClaimInfo] = useState(false); + const [claimLoading, setClaimLoading] = useState(false); + + const handleClaimSubmit = () => { + setClaimLoading(true); + setShowFailedClaimInfo(false); // Reset alert + // Simulate an API call for claiming rewards + setTimeout(() => { + setClaimLoading(false); + // In a real scenario, this would depend on the API response + const success = Math.random() > 0.5; // Simulate success/failure + if (!success) { + setShowFailedClaimInfo(true); + } else { + // Handle successful claim (e.g., show a success message, close dialog) + alert(t('Reward claimed successfully!')); + onClose(); + } + }, 1500); + }; + + return ( + + + {t('Claim Your Rewards')} + + + + {t('You have 0 Sats in compensations.')} {/* Placeholder for actual sats */} + + + {/* This would be a more complex form for invoice submission */} + setInvoiceAmount(e.target.value)} + sx={{ marginBottom: '16px' }} + /> + + {showFailedClaimInfo && ( + + {t( + 'To claim your rewards, please contact the coordinator of your last order. If the payment fails, you must contact the coordinator - do not generate a new invoice.', + )} + + )} + + + + + + + ); +}; + +export default ClaimRewardDialog; diff --git a/frontend/src/components/Dialogs/Profile.tsx b/frontend/src/components/Dialogs/Profile.tsx index e9cb224c..dee31cc6 100644 --- a/frontend/src/components/Dialogs/Profile.tsx +++ b/frontend/src/components/Dialogs/Profile.tsx @@ -1,6 +1,5 @@ import React, { useContext, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; - import { Dialog, DialogContent, @@ -11,24 +10,27 @@ import { ListItem, Typography, LinearProgress, + Button, } from '@mui/material'; - import BoltIcon from '@mui/icons-material/Bolt'; +import RedeemIcon from '@mui/icons-material/Redeem'; import RobotAvatar from '../RobotAvatar'; import RobotInfo from '../RobotInfo'; import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { type Coordinator } from '../../models'; +import ClaimRewardDialog from './ClaimRewardDialog'; // New import interface Props { open: boolean; onClose: () => void; } -const ProfileDialog = ({ open = false, onClose }: Props): React.JSX.Element => { +const ProfileDialog = ({ open = false, onClose }: Props): JSX.Element => { const { federation } = useContext(FederationContext); const { garage, slotUpdatedAt } = useContext(GarageContext); const { t } = useTranslation(); + const [isClaimRewardDialogOpen, setIsClaimRewardDialogOpen] = useState(false); const slot = garage.getSlot(); @@ -42,87 +44,157 @@ const ProfileDialog = ({ open = false, onClose }: Props): React.JSX.Element => { setLoadingRobots(Object.values(slot?.robots ?? {}).filter((robot) => robot.loading).length); }, [slotUpdatedAt]); - return ( - -
- -
- - - {t('Your Robot')} - - - + const handleClaimReward = () => { + setIsClaimRewardDialogOpen(true); + }; - - - - {!garage.getSlot()?.nickname && ( -
+ const handleCloseClaimRewardDialog = () => { + setIsClaimRewardDialogOpen(false); + }; + + // Determine if there are any rewards to claim + const hasClaimableRewards = federation.getCoordinators().some((coordinator: Coordinator) => { + const coordinatorRobot = garage.getSlot()?.getRobot(coordinator.shortAlias); + // Assuming 'earnedRewards' property exists on the robot object + return coordinatorRobot && coordinatorRobot.earnedRewards > 0; + }); + + return ( + <> + +
+ +
+ +
+ + {t('Your Robot')} + + +
+ + + + + + + + {garage.getSlot()?.nickname ? (
- - - {garage.getSlot()?.nickname} - - + + + {garage.getSlot()?.nickname} + +
+ ) : ( + {t('No nickname set')} + )} +
+ + {loadingRobots > 0 && ( +
+ + {t('Looking for your robot!')} + +
)} - +
- {loadingRobots > 0 ? ( - <> - {t('Looking for your robot!')} - - - ) : ( - <> - )} - + {/* Adjusted minWidth and marginLeft */} + + +
- - - - + +
- - + + {t('Coordinators that know your robot:')} + - - {t('Coordinators that know your robot:')} - - - {federation.getCoordinators().map((coordinator: Coordinator): React.JSX.Element => { - const coordinatorRobot = garage.getSlot()?.getRobot(coordinator.shortAlias); - return ( -
- -
- ); - })} -
-
+ {federation.getCoordinators().map((coordinator: Coordinator) => { + const coordinatorRobot = garage.getSlot()?.getRobot(coordinator.shortAlias); + return ( +
+ +
+ ); + })} + +
+ {/* New ClaimRewardDialog */} + + ); };