@headlessui/react#Tab TypeScript Examples
The following examples show how to use
@headlessui/react#Tab.
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: index.tsx From interbtc-ui with Apache License 2.0 | 6 votes |
InterlayTabList = ({ className, ...rest }: InterlayTabListProps): JSX.Element => (
<Tab.List
className={clsx(
'flex',
'p-1',
'space-x-1',
{ 'bg-interlayDenim-900': process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT },
'bg-opacity-20',
{ 'dark:bg-kintsugiMidnight-100': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA },
{ 'dark:bg-opacity-20': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA },
'rounded-xl',
className
)}
{...rest}
/>
)
Example #2
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 6 votes |
InterlayTab = ({ className, ...rest }: InterlayTabProps): JSX.Element => (
<Tab
className={({ selected }) =>
clsx(
'w-full',
'py-2.5',
'text-sm',
'leading-5',
'font-medium',
{ 'text-interlayDenim-700': process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT },
{ 'dark:text-kintsugiMidnight-700': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA },
'rounded-lg',
'focus:outline-none',
'focus:ring-2',
'ring-offset-2',
{ 'ring-offset-interlayDenim-400': process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT },
{ 'dark:ring-offset-kintsugiMidnight': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA },
'ring-white',
'ring-opacity-60',
selected
? clsx('bg-white', 'shadow')
: clsx(
{ 'text-interlayDenim-100': process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT },
{ 'dark:text-kintsugiTextSecondaryInDarkMode': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA },
'hover:bg-white',
'hover:bg-opacity-10',
'hover:text-white'
),
className
)
}
{...rest}
/>
)
Example #3
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 6 votes |
InterlayTabPanel = ({ className, ...rest }: InterlayTabPanelProps): JSX.Element => (
<Tab.Panel
className={clsx(
'rounded-xl',
'p-1.5',
'focus:outline-none',
'focus:ring-2',
'ring-offset-2',
{ 'ring-offset-interlayDenim-400': process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT },
{ 'dark:ring-offset-kintsugiMidnight': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA },
'ring-white',
'ring-opacity-60',
className
)}
{...rest}
/>
)
Example #4
Source File: CardTab.tsx From Meshtastic with GNU General Public License v3.0 | 6 votes |
CardTab = ({ title }: CardTabProps): JSX.Element => {
return (
<Tab
className={({ selected }) =>
`w-1/3 truncate rounded-md px-3 py-2 text-sm font-medium hover:bg-tertiary ${
selected ? 'bg-secondary shadow-md' : ''
}`
}
>
{title}
</Tab>
);
}
Example #5
Source File: InfoTab.tsx From Meshtastic with GNU General Public License v3.0 | 6 votes |
InfoTab = ({ device }: InfoTabProps): JSX.Element => {
return (
<Tab.Panel>
<div className="px-4 py-5 sm:p-0">
<dl className="sm:divide-y sm:divide-gray-200">
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<dt className="text-sm font-medium text-secondaryInv">
BLE/WiFi Version
</dt>
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
<span className="rounded-md bg-secondary px-0.5">
{device.specifications.BLEVersion}
</span>
/
<span className="rounded-md bg-secondary px-0.5">
{device.specifications.WiFiVersion}
</span>
</dd>
</div>
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<dt className="text-sm font-medium text-secondaryInv">
BLE/WiFi Antenna
</dt>
<dd className="mt-1 flex gap-1 text-sm text-tertiaryInv sm:col-span-2 sm:mt-0">
<span className="rounded-md bg-secondary px-0.5">
{device.specifications.BLEAntenna}
</span>
/
<span className="rounded-md bg-secondary px-0.5">
{device.specifications.WiFiAntenna}
</span>
</dd>
</div>
</dl>
</div>
</Tab.Panel>
);
}
Example #6
Source File: PinoutTab.tsx From Meshtastic with GNU General Public License v3.0 | 6 votes |
PinoutTab = ({ device }: PinoutTabProps): JSX.Element => {
return (
<Tab.Panel className="flex">
<div className="m-auto flex gap-4 rounded-lg bg-slate-700 px-2 py-1 shadow-md">
{[
device.pinout.slice(0, device.misc.pinoutSplit),
device.pinout.slice(device.misc.pinoutSplit, device.pinout.length),
].map((group, index) => (
<div key={index}>
{group.map((pin, pinIndex) => (
<div
className={`flex gap-1 ${
index === 0 ? 'flex-row' : 'flex-row-reverse'
}`}
key={pinIndex}
>
<div className="m-auto h-3 w-3 rounded-full border bg-yellow-500" />
<span className="m-auto font-mono text-white">{pin.label}</span>
</div>
))}
</div>
))}
</div>
</Tab.Panel>
);
}
Example #7
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 5 votes |
InterlayTabGroup = Tab.Group
Example #8
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 5 votes |
InterlayTabPanels = Tab.Panels
Example #9
Source File: PowerTab.tsx From Meshtastic with GNU General Public License v3.0 | 5 votes |
PowerTab = ({ device }: PowerTabProps): JSX.Element => {
return <Tab.Panel className="h-32">Content 1</Tab.Panel>;
}
Example #10
Source File: liquidations.tsx From arkadiko with GNU General Public License v3.0 | 4 votes |
Liquidations: React.FC = () => {
const { doContractCall } = useConnect();
const stxAddress = useSTXAddress();
const contractAddress = process.env.REACT_APP_CONTRACT_ADDRESS || '';
const [state, setState] = useContext(AppContext);
const [isLoading, setIsLoading] = useState(true);
const [startLoadingRewards, setStartLoadingRewards] = useState(false);
const [isLoadingRewards, setIsLoadingRewards] = useState(true);
const [rewardData, setRewardData] = useState([]);
const [stakeAmount, setStakeAmount] = useState(0);
const [unstakeAmount, setUnstakeAmount] = useState(0);
const [userPooled, setUserPooled] = useState(0);
const [totalPooled, setTotalPooled] = useState(0);
const [currentBlockHeight, setCurrentBlockHeight] = useState(0);
const [dikoEndBlock, setDikoEndBlock] = useState(0);
const [dikoRewardsToAdd, setDikoRewardsToAdd] = useState(0);
const [dikoApr, setDikoApr] = useState(0);
const [buttonUnstakeDisabled, setButtonUnstakeDisabled] = useState(true);
const [buttonStakeDisabled, setButtonStakeDisabled] = useState(true);
const [redeemableStx, setRedeemableStx] = useState(0);
const [lockupBlocks, setLockupBlocks] = useState(0);
const [stakerLockupBlocks, setStakerLockupBlocks] = useState(0);
const [rewardLoadingPercentage, setRewardLoadingPercentage] = useState(0);
const onInputStakeChange = (event: any) => {
const value = event.target.value;
setStakeAmount(value);
};
const onInputUnstakeChange = (event: any) => {
const value = event.target.value;
setUnstakeAmount(value);
};
const stakeMaxAmount = () => {
setStakeAmount((state.balance['usda'] / 1000000).toString());
};
const unstakeMaxAmount = () => {
setUnstakeAmount((userPooled / 1000000).toString());
};
const redeemStx = async () => {
await doContractCall({
network,
contractAddress,
stxAddress,
contractName: 'arkadiko-freddie-v1-1',
functionName: 'redeem-stx',
functionArgs: [uintCV(state.balance['xstx'])],
postConditionMode: 0x01,
onFinish: data => {
setState(prevState => ({
...prevState,
currentTxId: data.txId,
currentTxStatus: 'pending',
}));
},
anchorMode: AnchorMode.Any,
});
};
const stake = async () => {
const postConditions = [
makeStandardFungiblePostCondition(
stxAddress || '',
FungibleConditionCode.Equal,
uintCV(Number((parseFloat(stakeAmount) * 1000000).toFixed(0))).value,
createAssetInfo(contractAddress, 'usda-token', 'usda')
),
];
await doContractCall({
network,
contractAddress,
stxAddress,
contractName: 'arkadiko-liquidation-pool-v1-1',
functionName: 'stake',
functionArgs: [
uintCV(Number((parseFloat(stakeAmount) * 1000000).toFixed(0)))
],
postConditions,
onFinish: data => {
setState(prevState => ({
...prevState,
currentTxId: data.txId,
currentTxStatus: 'pending',
}));
},
anchorMode: AnchorMode.Any,
});
};
const unstake = async () => {
const postConditions = [
makeContractFungiblePostCondition(
contractAddress,
'arkadiko-liquidation-pool-v1-1',
FungibleConditionCode.Equal,
uintCV(Number((parseFloat(unstakeAmount) * 1000000).toFixed(0))).value,
createAssetInfo(contractAddress, 'usda-token', 'usda')
),
];
await doContractCall({
network,
contractAddress,
stxAddress,
contractName: 'arkadiko-liquidation-pool-v1-1',
functionName: 'unstake',
functionArgs: [
uintCV(Number((parseFloat(unstakeAmount) * 1000000).toFixed(0)))
],
postConditions,
onFinish: data => {
setState(prevState => ({
...prevState,
currentTxId: data.txId,
currentTxStatus: 'pending',
}));
},
anchorMode: AnchorMode.Any,
});
};
const getRewardCount = async () => {
const call = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-liquidation-rewards-v1-1',
functionName: 'get-total-reward-ids',
functionArgs: [],
senderAddress: stxAddress || '',
network: network,
});
const maxRewardId = cvToJSON(call).value;
console.log("Reward IDs: ", maxRewardId);
return parseInt(maxRewardId);
};
const getRewardsData = async (startId: Number, endId: Number, totalIds: number, loadedIds: number) => {
var rewardIds = [];
for (let rewardId = startId; rewardId <= endId; rewardId++) {
rewardIds.push(rewardId);
}
const rewardsData: LiquidationRewardProps[] = [];
await asyncForEach(rewardIds, async (rewardId: number) => {
try {
const callUserPending = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-liquidation-ui-v1-2',
functionName: 'get-user-reward-info',
functionArgs: [
uintCV(rewardId),
],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(callUserPending).value.value;
if (result['pending-rewards'].value > 0){
rewardsData.push({
rewardIds: [rewardId],
token: result['token'].value,
claimable: result['pending-rewards'].value,
tokenIsStx: result['token-is-stx'].value,
});
}
} catch (e) {
console.error(e);
}
loadedIds = loadedIds + 1;
const percentage = parseInt(Math.floor((loadedIds / (totalIds * 1.01)) * 100.0));
setRewardLoadingPercentage(percentage);
});
return rewardsData;
};
const createGroups = (rewardsData: LiquidationRewardProps[]) => {
// Merge in groups to bulk claim
const rewardsDataMerged: LiquidationRewardProps[] = [];
for (const rewardData of rewardsData) {
const result = rewardsDataMerged.filter(data => {
return data.rewardIds.length < 50 && data.token == rewardData.token && data.tokenIsStx == rewardData.tokenIsStx;
});
if (result.length == 0) {
rewardsDataMerged.push(rewardData);
} else {
let existingData = result[0];
if (!existingData.rewardIds.includes(rewardData.rewardIds[0])) {
existingData.rewardIds.push(rewardData.rewardIds[0]);
existingData.claimable = parseInt(existingData.claimable) + parseInt(rewardData.claimable);
}
}
}
return rewardsDataMerged;
};
const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
};
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
const loadRewards = async () => {
setStartLoadingRewards(true);
// Fetch all reward info
const rewardCount = await getRewardCount();
var rewards: LiquidationRewardProps[] = [];
const batchAmount = 15;
const batches = Math.ceil(rewardCount / batchAmount);
for (let batch = batches-1; batch >= 0; batch--) {
// Sleep 10 sec
await sleep(10000);
const startRewardId = batch * batchAmount;
const endRewardId = Math.min((batch+1) * batchAmount - 1, rewardCount-1);
const rewardsLoaded = (batches-1-batch) * (endRewardId - startRewardId);
const newRewards = await getRewardsData(startRewardId, endRewardId, rewardCount, rewardsLoaded);
rewards = rewards.concat(newRewards);
// Group rewards
const rewardGroups = createGroups(rewards);
const rewardItems = rewardGroups.map((reward: object) => (
<LiquidationReward
key={reward.rewardIds}
rewardIds={reward.rewardIds}
token={reward.token}
claimable={reward.claimable}
tokenIsStx={reward.tokenIsStx}
/>
));
setRewardData(rewardItems);
}
setIsLoadingRewards(false);
};
useEffect(() => {
// TODO: Replace by API price
const getDikoPrice = async () => {
const call = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-swap-v2-1',
functionName: 'get-pair-details',
functionArgs: [
contractPrincipalCV(contractAddress, 'arkadiko-token'),
contractPrincipalCV(contractAddress, 'usda-token'),
],
senderAddress: stxAddress || '',
network: network,
});
const resultPairDetails = cvToJSON(call).value.value.value;
const balanceX = resultPairDetails["balance-x"].value;
const balanceY = resultPairDetails["balance-y"].value;
return balanceY / balanceX;
};
const getTotalPooled = async () => {
const call = await callReadOnlyFunction({
contractAddress,
contractName: 'usda-token',
functionName: 'get-balance',
functionArgs: [
contractPrincipalCV(contractAddress, 'arkadiko-liquidation-pool-v1-1'),
],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(call).value.value;
return result;
};
const getUserPooled = async () => {
const call = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-liquidation-pool-v1-1',
functionName: 'get-tokens-of',
functionArgs: [
standardPrincipalCV(stxAddress || ''),
],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(call).value.value;
return result;
};
const getCurrentBlockHeight = async () => {
const client = getRPCClient();
const response = await fetch(`${client.url}/v2/info`, { credentials: 'omit' });
const data = await response.json();
return data['stacks_tip_height'];
};
const getEpochInfo = async () => {
const call = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-liquidation-rewards-diko-v1-1',
functionName: 'get-epoch-info',
functionArgs: [],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(call).value.value;
return result;
};
const getDikoEpochRewardsToAdd = async () => {
const call = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-liquidation-rewards-diko-v1-1',
functionName: 'get-rewards-to-add',
functionArgs: [],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(call).value;
return result;
};
const getStxRedeemable = async () => {
const stxRedeemable = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-freddie-v1-1',
functionName: 'get-stx-redeemable',
functionArgs: [],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(stxRedeemable).value.value;
return result;
};
const getLockup = async () => {
const stxRedeemable = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-liquidation-pool-v1-1',
functionName: 'get-lockup-blocks',
functionArgs: [],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(stxRedeemable).value.value;
return result;
};
const getStakerLockup = async () => {
const call = await callReadOnlyFunction({
contractAddress,
contractName: 'arkadiko-liquidation-pool-v1-1',
functionName: 'get-staker-lockup',
functionArgs: [
standardPrincipalCV(stxAddress || ''),
],
senderAddress: stxAddress || '',
network: network,
});
const result = cvToJSON(call).value;
return result["start-block"].value;
};
const fetchInfo = async () => {
// Fetch info
const [
totalPooled,
userPooled,
epochInfo,
dikoEpochRewardsToAdd,
currentBlockHeight,
stxRedeemable,
stakerLockup,
lockupBlocks,
dikoPrice,
] = await Promise.all([
getTotalPooled(),
getUserPooled(),
getEpochInfo(),
getDikoEpochRewardsToAdd(),
getCurrentBlockHeight(),
getStxRedeemable(),
getStakerLockup(),
getLockup(),
getDikoPrice(),
]);
setTotalPooled(totalPooled);
setUserPooled(userPooled);
setDikoEndBlock(epochInfo["end-block"].value);
setDikoRewardsToAdd(dikoEpochRewardsToAdd);
setCurrentBlockHeight(currentBlockHeight);
setButtonStakeDisabled(false);
setButtonUnstakeDisabled(userPooled == 0)
if (userPooled == 0) {
setStakerLockupBlocks(0);
} else {
setStakerLockupBlocks(parseInt(stakerLockup) + parseInt(lockupBlocks));
}
setLockupBlocks(lockupBlocks);
setRedeemableStx(stxRedeemable);
const dikoPerYear = (52560 / epochInfo["blocks"].value) * dikoEpochRewardsToAdd;
setDikoApr((dikoPerYear * dikoPrice) / totalPooled * 100.0);
setIsLoading(false);
};
fetchInfo();
}, []);
const tabs = [
{ name: 'Add', icon: <PlusCircleIcon className="w-4 h-4 mr-2" aria-hidden="true" /> },
{ name: 'Remove', icon: <MinusCircleIcon className="w-4 h-4 mr-2" aria-hidden="true"/> },
]
return (
<>
<Helmet>
<title>Liquidations</title>
</Helmet>
{state.userData ? (
<Container>
<main className="relative flex-1 py-12">
{state.balance['xstx'] > 0 ? (
<section>
<header className="pb-5 border-b border-gray-200 dark:border-zinc-600 sm:flex sm:justify-between sm:items-end">
<div>
<h3 className="text-lg leading-6 text-gray-900 font-headings dark:text-zinc-50">Trade xSTX for STX</h3>
</div>
</header>
<div className="mt-4">
{isLoading ? (
<>
<Placeholder className="py-2" width={Placeholder.width.FULL} />
<Placeholder className="py-2" width={Placeholder.width.FULL} />
</>
) : (
<div className="mt-4 shadow sm:rounded-md sm:overflow-hidden">
<div className="px-4 py-5 bg-white dark:bg-zinc-800 sm:p-6">
{(redeemableStx / 1000000) === 0 ? (
<>
<p>There are <span className="font-semibold">no redeemable STX</span> in the Arkadiko pool.</p>
<p className="mt-1">Be sure to check again later to redeem your xSTX for STX.</p>
</>
) : (
<p>There are <span className="text-lg font-semibold">{redeemableStx / 1000000}</span> STX redeemable in the Arkadiko pool.</p>
)}
<div className="flex items-center justify-between mt-4">
<p>You have <span className="text-lg font-semibold">{state.balance['xstx'] / 1000000}</span> xSTX.</p>
<button
type="button"
onClick={() => redeemStx()}
disabled={(redeemableStx / 1000000) === 0}
className="inline-flex justify-center px-4 py-2 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:col-start-2 sm:text-sm disabled:bg-gray-100 disabled:text-gray-500 disabled:cursor-not-allowed"
>
Redeem
</button>
</div>
</div>
</div>
)}
</div>
</section>
): null}
<section>
<header className="pt-10 pb-5 border-b border-gray-200 dark:border-zinc-600 sm:flex sm:justify-between sm:items-end">
<div>
<h3 className="text-lg leading-6 text-gray-900 font-headings dark:text-zinc-50">Your rewards</h3>
</div>
</header>
<div className="mt-4">
{!startLoadingRewards ? (
<div className="mt-4 shadow sm:rounded-md sm:overflow-hidden">
<div className="px-4 py-5 bg-white dark:bg-zinc-800 sm:p-6">
<div className="flex items-center justify-between">
<p>
It can take a couple of minutes to check all liquidated vaults. Thanks for your patience!
</p>
<button
type="button"
onClick={() => loadRewards()}
className="inline-flex justify-center px-4 py-2 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:col-start-2 sm:text-sm disabled:bg-gray-100 disabled:text-gray-500 disabled:cursor-not-allowed"
>
Load rewards
</button>
</div>
</div>
</div>
) : isLoadingRewards ? (
<div className="mt-4 shadow sm:rounded-md sm:overflow-hidden">
<div className="px-4 py-5 bg-white dark:bg-zinc-800 sm:p-6">
<div className="flex justify-between mb-3">
<span className="text-base font-medium dark:text-white">Checking liquidated vaults…</span>
<span className="text-sm font-medium text-indigo-700 dark:text-white">{rewardLoadingPercentage}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
<div className="bg-indigo-600 h-2.5 rounded-full font-semibold" style={{ width: rewardLoadingPercentage + "%" }}></div>
</div>
</div>
</div>
): null}
</div>
<div className="mt-4">
{rewardData.length == 0 && startLoadingRewards && !isLoadingRewards ? (
<EmptyState
Icon={CashIcon}
title="You have no rewards to claim."
description="DIKO and liquidation rewards will appear here."
/>
) : rewardData.length != 0 && startLoadingRewards ? (
<>
<table className="min-w-full divide-y divide-gray-200 dark:divide-zinc-600 shadow sm:rounded-md sm:overflow-hidden">
<thead className="bg-gray-50 dark:bg-zinc-900 dark:bg-opacity-80">
<tr>
<th className="px-6 py-3 text-xs font-medium tracking-wider text-left text-gray-500 dark:text-zinc-400">
Token
</th>
<th className="px-6 py-3 text-xs font-medium tracking-wider text-center text-gray-500 dark:text-zinc-400">
Amount
</th>
<th className="px-6 py-3 text-xs font-medium tracking-wider text-left text-gray-500 dark:text-zinc-400"></th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200 dark:bg-zinc-900 dark:divide-zinc-600">{rewardData}</tbody>
</table>
</>
): null}
</div>
</section>
<section>
<header className="pt-10 pb-5 border-b border-gray-200 dark:border-zinc-600 sm:flex sm:justify-between sm:items-end">
<div>
<h3 className="text-lg leading-6 text-gray-900 font-headings dark:text-zinc-50">DIKO emissions</h3>
</div>
</header>
<div className="mt-4">
<div className="grid grid-cols-1 gap-5 mt-4 sm:grid-cols-4">
<div className="p-4 overflow-hidden border border-gray-300 rounded-lg shadow-sm bg-zinc-200/30 dark:bg-gray-500 dark:border-gray-700">
<p className="text-xs font-semibold text-gray-500 uppercase dark:text-gray-300">Current Block Height</p>
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} color={Placeholder.color.GRAY} />
) : (
<p className="mt-1 text-xl font-semibold text-gray-600 dark:text-gray-50">
#{currentBlockHeight}
</p>
)}
</div>
<div className="p-4 overflow-hidden border border-gray-300 rounded-lg shadow-sm bg-zinc-200/30 dark:bg-gray-500 dark:border-gray-700">
<p className="text-xs font-semibold text-gray-500 uppercase dark:text-gray-300">Next DIKO rewards at block</p>
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} color={Placeholder.color.GRAY} />
) : (
<p className="mt-1 text-xl font-semibold text-gray-600 dark:text-gray-50">#{dikoEndBlock}</p>
)}
</div>
<div className="p-4 overflow-hidden border border-indigo-200 rounded-lg shadow-sm bg-indigo-50 dark:bg-indigo-200">
<p className="text-xs font-semibold text-indigo-600 uppercase">Rewards to distribute</p>
{isLoading ? (
<>
<Placeholder className="py-2" width={Placeholder.width.THIRD} />
<Placeholder className="py-2" width={Placeholder.width.FULL} />
</>
) : (
<>
<p className="mt-1 text-xl font-semibold text-indigo-800">
{microToReadable(dikoRewardsToAdd).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 6,
})} DIKO
</p>
</>
)}
</div>
<div className="p-4 overflow-hidden border border-indigo-200 rounded-lg shadow-sm bg-indigo-50 dark:bg-indigo-200">
<p className="text-xs font-semibold text-indigo-600 uppercase">APR</p>
{isLoading ? (
<>
<Placeholder className="py-2" width={Placeholder.width.THIRD} />
<Placeholder className="py-2" width={Placeholder.width.FULL} />
</>
) : (
<>
<p className="mt-1 text-xl font-semibold text-indigo-800">
{dikoApr.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}%
</p>
</>
)}
</div>
</div>
</div>
</section>
<section>
<header className="pt-10 pb-5 border-b border-gray-200 dark:border-zinc-600 sm:flex sm:justify-between sm:items-end">
<div>
<h3 className="text-lg leading-6 text-gray-900 font-headings dark:text-zinc-50">USDA pool</h3>
</div>
</header>
<div className="mt-4">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div className="w-full p-4 border border-indigo-200 rounded-lg shadow-sm bg-indigo-50 dark:bg-indigo-200">
<h4 className="text-xs text-indigo-700 uppercase font-headings">Pool info</h4>
<dl className="mt-2 space-y-1">
<div className="sm:grid sm:grid-cols-2 sm:gap-4">
<dt className="inline-flex items-center text-sm font-medium text-indigo-500 dark:text-indigo-700">
Total tokens in pool
<div className="ml-2">
<Tooltip
className="z-10"
shouldWrapChildren={true}
label={`Amount of USDA that is currently in the pool, ready to be used for liquidations.`}
>
<InformationCircleIcon
className="block w-4 h-4 text-indigo-400 dark:text-indigo-500"
aria-hidden="true"
/>
</Tooltip>
</div>
</dt>
<dt className="mt-1 text-sm font-semibold text-indigo-900 sm:mt-0 sm:text-right">
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} />
) : (
<>
{microToReadable(totalPooled).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 6,
})} USDA
</>
)}
</dt>
</div>
<div className="sm:grid sm:grid-cols-2 sm:gap-4">
<dt className="inline-flex items-center text-sm font-medium text-indigo-500 dark:text-indigo-700">
Lockup duration
<div className="ml-2">
<Tooltip
className="z-10"
shouldWrapChildren={true}
label={`Deposited USDA will be locked.`}
>
<InformationCircleIcon
className="block w-4 h-4 text-indigo-400 dark:text-indigo-500"
aria-hidden="true"
/>
</Tooltip>
</div>
</dt>
<dt className="mt-1 text-sm font-semibold text-indigo-900 sm:mt-0 sm:text-right">
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} />
) : (
<>
{lockupBlocks} blocks
</>
)}
</dt>
</div>
<div className="sm:grid sm:grid-cols-2 sm:gap-4">
<dt className="inline-flex items-center text-sm font-medium text-indigo-500 dark:text-indigo-700">
Your tokens in pool
<div className="ml-2">
<Tooltip
className="z-10"
shouldWrapChildren={true}
label={`The amount of USDA you still have in the pool. Will decrease if USDA is used in liquidations`}
>
<InformationCircleIcon
className="block w-4 h-4 text-indigo-400 dark:text-indigo-500"
aria-hidden="true"
/>
</Tooltip>
</div>
</dt>
<dt className="mt-1 text-sm font-semibold text-indigo-900 sm:mt-0 sm:text-right">
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} />
) : (
<>
{microToReadable(userPooled).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 6,
})} USDA
</>
)}
</dt>
</div>
<div className="sm:grid sm:grid-cols-2 sm:gap-4">
<dt className="inline-flex items-center text-sm font-medium text-indigo-500 dark:text-indigo-700">
Unlocking at
<div className="ml-2">
<Tooltip
className="z-10"
shouldWrapChildren={true}
label={`Your deposited USDA will unlock at this block.`}
>
<InformationCircleIcon
className="block w-4 h-4 text-indigo-400 dark:text-indigo-500"
aria-hidden="true"
/>
</Tooltip>
</div>
</dt>
<dt className="mt-1 text-sm font-semibold text-indigo-900 sm:mt-0 sm:text-right">
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} />
) : (
<>
Block {stakerLockupBlocks}
</>
)}
</dt>
</div>
</dl>
</div>
<div className="sm:col-span-2">
<div className="relative bg-white rounded-lg shadow dark:bg-zinc-900">
<div className="flex flex-col p-4">
<Tab.Group>
<Tab.List className="group p-0.5 rounded-lg flex w-full bg-gray-50 hover:bg-gray-100 dark:bg-zinc-300 dark:hover:bg-zinc-200">
{tabs.map((tab, tabIdx) => (
<Tab as={Fragment} key={tabIdx}>
{({ selected }) => (
<button className={
classNames(
`p-1.5 lg:pl-2.5 lg:pr-3.5 rounded-md flex items-center justify-center flex-1 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus:outline-none focus-visible:ring-offset-gray-100 ${tabIdx === 1 ? 'ml-0.5': ''}`,
selected
? 'text-sm text-gray-600 font-medium bg-white ring-1 ring-black ring-opacity-5'
: ''
)}
>
<span className="inline-flex items-center text-sm font-medium rounded-md">
<span className={
selected
? 'text-indigo-500'
: 'text-gray-500 group-hover:text-gray-900 dark:group-hover:text-zinc-900'
}
>
{tab.icon}
</span>
<span className="text-gray-900">{tab.name}</span>
</span>
</button>
)}
</Tab>
))}
</Tab.List>
<Tab.Panels className="mt-4">
<Tab.Panel>
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} />
) : (
<>
<InputAmount
balance={microToReadable(state.balance['usda']).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 6,
})}
token='USDA'
inputValue={stakeAmount}
onInputChange={onInputStakeChange}
onClickMax={stakeMaxAmount}
/>
<button
type="button"
className="inline-flex justify-center px-4 py-2 mb-4 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:col-start-2 sm:text-sm disabled:bg-gray-100 disabled:text-gray-500 disabled:cursor-not-allowed"
disabled={buttonStakeDisabled}
onClick={stake}
>
Add USDA to pool
</button>
</>
)}
</Tab.Panel>
<Tab.Panel>
{isLoading ? (
<Placeholder className="py-2" width={Placeholder.width.FULL} />
) : currentBlockHeight < stakerLockupBlocks ? (
<div className="">
<Alert type={Alert.type.WARNING} title="Locked">
<p>
Your USDA is locked until block #{stakerLockupBlocks}
</p>
</Alert>
</div>
) : (
<>
<InputAmount
balance={microToReadable(userPooled).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 6,
})}
token='USDA'
inputValue={unstakeAmount}
onInputChange={onInputUnstakeChange}
onClickMax={unstakeMaxAmount}
/>
<button
type="button"
className="inline-flex justify-center px-4 py-2 mb-4 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:col-start-2 sm:text-sm disabled:bg-gray-100 disabled:text-gray-500 disabled:cursor-not-allowed"
disabled={buttonUnstakeDisabled}
onClick={unstake}
>
Remove USDA from pool
</button>
</>
)}
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
</Container>
) : (
<Redirect to={{ pathname: '/' }} />
)}
</>
);
}
Example #11
Source File: Festival.tsx From platform with MIT License | 4 votes |
export default function Festival() {
const { id } = useParams();
const event = rounds.find(({ type }) => type === "festival");
const [isLoading, setIsLoading] = useState(false);
const [eventEntries, setEventEntries] = useState({});
const [entriesCount, setEntriesCount] = useState(0);
useEffect(() => {
document.title = "The Chess Centre | Festival";
const fetchEvent = async () => {
setIsLoading(true);
const response = await API.graphql({
query: getEvent,
variables: { id },
authMode: "AWS_IAM",
}).catch((error) => {
console.log("Error fetching event.", id);
console.log(error.response);
});
if (response && response.data) {
const {
data: { getEvent: entries },
} = response;
setEventEntries(entries);
if (entries?.entries?.items) {
setEntriesCount(entries?.entries?.items.length);
}
}
setIsLoading(false);
};
fetchEvent();
}, [id]);
return (
<div className="relative bg-white">
<div className=" bg-gray-50 pt-6 pb-6 sm:pb-6 md:pb-6 lg:pb-6 xl:pb-6">
<LandingNav />
</div>
<div className="mx-auto py-10 px-4 sm:px-6 lg:max-w-7xl lg:px-8">
<div className="lg:grid lg:grid-rows-1 lg:grid-cols-7 lg:gap-x-8 lg:gap-y-10 xl:gap-x-16">
<div className="lg:row-end-1 lg:col-span-4">
<div className="aspect-w-4 aspect-h-3 rounded-lg bg-gray-100 overflow-hidden">
<img
src={FestivalHero}
alt="hero"
className="object-center object-cover"
/>
</div>
</div>
<div className="max-w-2xl text-center sm:text-left mx-auto mt-4 sm:mt-16 lg:max-w-none lg:mt-0 lg:row-end-2 lg:row-span-2 lg:col-span-3">
{/* TITLE */}
<div className="flex flex-col-reverse">
<div className="mt-4">
<h1 className="text-3xl font-extrabold tracking-tight text-teal-brand sm:text-5xl">
<span className="text-orange-brand">Ilkley</span> Chess
Festival
</h1>
<p className="text-md text-blue-brand mt-2">
<time dateTime={festival.datetime}>{festival.date}</time>
</p>
</div>
</div>
{/* PRIZES */}
<div className="border-t border-gray-200 mt-6 pt-6 mb-4">
<h3 className="text-lg font-medium text-gray-900">
Prizes{" "}
<span className="text-sm text-gray-500">for all sections</span>
</h3>
<div className="mt-4 sm:mx-0">
<Prizes />
</div>
</div>
{/* ENTRY FORM */}
<div className="hidden sm:block">
<EntryForm id={id} />
</div>
{/* LOCATION */}
<div className="border-t border-gray-200 mt-10 pt-10">
<div className="grid grid-cols-1 sm:grid-cols-2">
<div className="order-2 sm:order-1">
<h3 className="text-lg font-medium text-gray-900">
Location
</h3>
<p className="mt-4 text-sm text-gray-500">
King's Hall & Winter Garden
</p>
<p className="mt-2 text-sm text-gray-500">Station Road</p>
<p className="mt-2 mb-10 text-sm text-gray-500">
Ilkley, LS29 8HB
</p>
</div>
<div className="order-1 sm:order-2">
<img
className="w-6/7 -mt-6"
alt="festival building"
src={FestivalBuilding}
/>
</div>
</div>
<FestivalMap />
</div>
</div>
{/* MORE DETAILS */}
<div className="w-full max-w-2xl mx-auto sm:mt-16 mt-6 lg:max-w-none lg:mt-0 lg:col-span-4">
<Tab.Group as="div">
<div className="border-b border-gray-200">
<Tab.List className="-mb-px flex space-x-8">
<Tab
className={({ selected }) =>
classNames(
selected
? "border-teal-600 text-teal-600"
: "border-transparent text-gray-700 hover:text-gray-800 hover:border-gray-300",
"whitespace-nowrap py-6 border-b-2 font-medium text-sm focus:ring-transparent"
)
}
>
Details
</Tab>
<Tab
className={({ selected }) =>
classNames(
selected
? "border-teal-600 text-teal-600"
: "border-transparent text-gray-700 hover:text-gray-800 hover:border-gray-300",
"whitespace-nowrap py-6 border-b-2 font-medium text-sm focus:ring-transparent"
)
}
>
Schedule
</Tab>
<Tab
className={({ selected }) =>
classNames(
selected
? "border-teal-600 text-teal-600"
: "border-transparent text-gray-700 hover:text-gray-800 hover:border-gray-300",
"whitespace-nowrap py-6 border-b-2 font-medium text-sm focus:ring-transparent"
)
}
>
Entries
</Tab>
<Tab
className={({ selected }) =>
classNames(
selected
? "border-teal-600 text-teal-600"
: "border-transparent text-gray-700 hover:text-gray-800 hover:border-gray-300",
"whitespace-nowrap py-6 border-b-2 font-medium text-sm focus:ring-transparent"
)
}
>
FAQs
</Tab>
</Tab.List>
</div>
<Tab.Panels as={Fragment}>
<Tab.Panel className="-mb-10 py-5 focus:ring-transparent">
<div className="relative">
<div className="prose prose-blue text-gray-500 mx-auto lg:max-w-none text-justify">
<h2>Sections</h2>
<ul className="font-medium text-teal-brand">
<li>Open</li>
<li>
Major{" "}
<span className="text-xs text-gray-500">
(2000 ECF and below)
</span>
</li>
<li>
Intermediate{" "}
<span className="text-xs text-gray-500">
(1750 ECF and below)
</span>
</li>
<li>
Minor{" "}
<span className="text-xs text-gray-500">
(1500 ECF and below)
</span>
</li>
</ul>
<p className="text-sm">
Unrated players will not be eligible for section
specific grading prizes.
</p>
</div>
<div className="rounded-md bg-yellow-50 p-4 my-4 hidden sm:block">
<div className="flex">
<div className="flex-shrink-0">
<ExclamationIcon
className="h-5 w-5 text-yellow-400"
aria-hidden="true"
/>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-yellow-800">
ECF Membership Required
</h3>
<div className="mt-2 text-sm text-yellow-700">
<p>
All entries <span className="italic">should</span>{" "}
have an ECF membership, create yours here:{" "}
<a
target="_blank"
rel="noreferrer"
href={`https://www.englishchess.org.uk/ecf-membership-rates-and-joining-details/`}
className="font-medium underline text-yellow-700 hover:text-yellow-600"
>
ECF Membership
</a>
</p>
</div>
</div>
</div>
</div>
<div className="prose prose-blue text-gray-500 mx-auto lg:max-w-none text-justify mt-4">
<h2>Event Structure</h2>
<ul className="font-medium text-teal-brand">
<li>
Rounds: <span className="text-blue-brand">5</span>{" "}
<span className="text-gray-600 text-sm font-normal">
see schedule
</span>
</li>
<li>
Time Control:{" "}
<span className="text-blue-brand text-md">
90{" "}
<span className="text-sm text-gray-600 font-normal">
mins per player
</span>{" "}
+ 10{" "}
<span className="text-sm text-gray-600 font-normal">
second increment
</span>
</span>
</li>
<li>
Entry fee:{" "}
<span className="text-blue-brand">£30</span>
</li>
</ul>
<p className="text-sm">
Standard ECF rules apply. All games will be submited to
the ECF for offical rating calculation.
</p>
</div>
</div>
</Tab.Panel>
<Tab.Panel
as="dl"
className="text-gray-500 py-5 focus:ring-transparent"
>
<div className="prose prose-blue mb-4">
<h2>Schedule</h2>
</div>
<Schedule event={event} />
</Tab.Panel>
<Tab.Panel
as="dl"
className="text-sm text-gray-500 py-5 focus:ring-transparent"
>
<div className="prose prose-blue text-gray-500 mx-auto lg:max-w-none text-justify">
<h2>
Entries{" "}
<span className="text-gray-500 font-medium text-sm">
( {entriesCount} )
</span>
</h2>
{!isLoading && eventEntries && (
<EntriesTable eventDetails={eventEntries} />
)}
{isLoading && (
<div className="text-gray-300 italic text-center">
<i className="fas fa-spinner-third fa-spin fa-fw text-teal-500"></i>{" "}
fetching entry details ...
</div>
)}
</div>
</Tab.Panel>
<Tab.Panel
as="dl"
className="text-sm text-gray-500 py-5 focus:ring-transparent"
>
<div className="prose prose-teal text-gray-500 mx-auto lg:max-w-none text-justify">
<h2>FAQs</h2>
<ul>
<li>
<span className="font-bold text-teal-600">
Missing my ECF rating
</span>
<p>
We automatically search and check ECF rating data upon
account registration. If we are unable to accurately
determine your ECF rating (if you have one) we will
contact you. Otherwise, leave it with us, your rating
will appear within a few hours of any event entry!
</p>
</li>
<li>
<span className="font-bold text-teal-600">
Withdraw entry
</span>
<p>
If you are unable to make this event, please{" "}
<a
className="text-teal-600 hover:underline"
href="mailto:[email protected]?subject=Withdraw%20Festival%20Entry"
>
contact us
</a>{" "}
as soon as possible so we can process any refund.
Unfortunately, those who withdraw on the day or fail
to attend will not be eligible for refunds as we offer
your place to other players.
</p>
</li>
<li>
<span className="font-bold text-teal-600">
Rating list
</span>
<p>
We will use the latest ECF ratings published on the{" "}
<span className="font-bold">1st September 2022</span>{" "}
for this event, this may mean entries have to switch
sections if their rating jumps beyond the specified
rating cap. We will contact all players where this
occurs but will automatically move players up to the
next eligible section.
</p>
</li>
<li>
<span className="font-bold text-teal-600">
Anti-Cheating{" "}
<span className="text-red-600 font-medium text-sm">
(TBC)
</span>
</span>
<p>
We are working with an official FIDE registered
tournament arbiter to provide clear guidance for
electronic devices.
</p>
<p>
<span className="font-bold">ECF</span>{" "}
<a href="https://www.englishchess.org.uk/wp-content/uploads/2019/12/Anti-Cheating-Document.pdf">
anti-cheating policy
</a>
</p>
<p>
<span className="font-bold">FIDE</span>{" "}
<a href="https://www.fide.com/FIDE/handbook/Anti%20Cheating%20Guidelines.pdf">
anti-cheating guidelines
</a>
</p>
</li>
</ul>
</div>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
{/* ENTRY FORM */}
<div className="block sm:hidden mt-12">
<EntryForm id={id} />
</div>
</div>
</div>
</div>
<FooterLanding />
</div>
);
}
Example #12
Source File: HardwareModal.tsx From Meshtastic with GNU General Public License v3.0 | 4 votes |
HardwareModal = ({
device,
open,
close,
}: HardwareModal): JSX.Element => {
const [hideDetails, setHideDetails] = useState(false);
const { breakpoint } = useBreakpoint(BREAKPOINTS, 'md');
return (
<Modal open={open} onClose={close}>
<div className="absolute right-0 z-20 m-2 md:flex">
<Button onClick={close}>
<FiX />
</Button>
</div>
<div className="absolute inset-0">
<motion.div
layout
animate={
breakpoint === 'sm'
? hideDetails
? 'hiddenSm'
: 'visibleSm'
: hideDetails
? 'hidden'
: 'visible'
}
variants={{
hidden: { width: '100%', height: '100%' },
hiddenSm: { height: '100%', width: '100%' },
visible: { width: '20%', height: '100%' },
visibleSm: { height: '33%', width: '100%' },
}}
transition={{
type: 'just',
}}
className="flex flex-col md:h-full md:flex-row"
>
<motion.div
layout
className={`relative z-10 flex h-full w-full rounded-t-2xl md:rounded-l-2xl md:rounded-tr-none ${device.misc.Gradient}`}
>
<motion.img
layout
src={device.images.Front}
alt=""
className="pointer-events-none m-auto max-h-full max-w-full object-cover p-2"
/>
<div className="absolute -bottom-5 z-20 flex w-full md:bottom-auto md:-right-5 md:h-full md:w-auto">
<Button
animate={
breakpoint === 'sm'
? hideDetails
? 'hiddenSm'
: 'visibleSm'
: hideDetails
? 'hidden'
: 'visible'
}
variants={{
hidden: { rotate: 180 },
hiddenSm: { rotate: -90 },
visible: { rotate: 0 },
visibleSm: { rotate: 90 },
}}
onClick={() => {
setHideDetails(!hideDetails);
}}
>
<FiChevronRight />
</Button>
</div>
<AnimatePresence>
{!hideDetails && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className={`absolute -bottom-5 z-20 flex md:mt-0 md:hidden md:pb-2 ${
hideDetails ? 'opacity-0' : 'opacity-100'
}`}
>
<VariantSelectButton options={device.variants} />
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute -bottom-3 right-0 m-auto mr-2 ml-auto flex md:inset-x-1 md:bottom-4 md:mt-2"
>
<div className="m-auto flex gap-2">
{device.features.BLE && (
<Badge
name="Bluetooth"
color="bg-blue-500"
icon={<FiBluetooth />}
/>
)}
{device.features.WiFi && (
<Badge
name="WiFi"
color="bg-orange-500"
icon={<FiWifi />}
/>
)}
</div>
</motion.div>
</>
)}
</AnimatePresence>
</motion.div>
<div
className={`h-7 bg-base opacity-0 md:h-auto md:w-7 ${
hideDetails ? 'flex' : 'hidden'
}`}
/>
</motion.div>
</div>
<div className="z-[1] mt-[25%] flex h-full flex-col md:ml-[20%] md:mt-0 md:w-4/5">
<div className="z-0 hidden pb-2 md:flex">
<VariantSelectButton options={device.variants} />
</div>
<div
className={`mt-1 flex flex-grow rounded-2xl bg-base p-2 shadow-inner transition-opacity duration-100 ease-linear md:mt-0 md:rounded-l-none md:rounded-r-2xl md:p-4 ${
hideDetails ? 'opacity-0' : 'opacity-100'
}`}
>
<Tab.Group
as="div"
className="flex flex-grow flex-col rounded-2xl bg-primary p-2"
>
<Tab.List className="flex gap-2">
<CardTab title="Info" />
<CardTab title="Power" />
<CardTab title="Pinout" />
</Tab.List>
<Tab.Panels as="div" className="flex-grow overflow-y-auto">
<InfoTab device={device} />
<PowerTab device={device} />
<PinoutTab device={device} />
</Tab.Panels>
</Tab.Group>
</div>
</div>
</Modal>
);
}
Example #13
Source File: index.tsx From interactsh-web with MIT License | 4 votes |
NotificationsPopup = ({ handleCloseDialog }: NotificationsPopupP) => {
const data = getStoredData();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [inputData, setInputData] = useState<any>({
telegram: data.telegram,
slack: data.slack,
discord: data.discord,
});
const handleTelegramConfirm = () => {
setIsLoading(true);
const currentStoredData = getStoredData();
setTimeout(() => {
localStorage.clear();
writeStoredData({ ...currentStoredData, telegram: inputData.telegram });
setInputData({ ...inputData });
setIsLoading(false);
}, 500);
};
const handleDiscordConfirm = () => {
setIsLoading(true);
const currentStoredData = getStoredData();
setTimeout(() => {
localStorage.clear();
writeStoredData({ ...currentStoredData, discord: inputData.discord });
setInputData({ ...inputData });
setIsLoading(false);
}, 500);
};
const handleSlackConfirm = () => {
setIsLoading(true);
const currentStoredData = getStoredData();
setTimeout(() => {
localStorage.clear();
writeStoredData({ ...currentStoredData, slack: inputData.slack });
setInputData({ ...inputData });
setIsLoading(false);
}, 500);
};
const handleInput = (e: any) => {
if (e.target.id === "telegram_bot_token") {
setInputData({ ...inputData, telegram: { ...inputData.telegram, botToken: e.target.value } });
} else if (e.target.id === "telegram_chat_id") {
setInputData({ ...inputData, telegram: { ...inputData.telegram, chatId: e.target.value } });
} else if (e.target.id === "slack_hook_key") {
setInputData({ ...inputData, slack: { ...inputData.slack, hookKey: e.target.value } });
} else if (e.target.id === "slack_channel") {
setInputData({ ...inputData, slack: { ...inputData.slack, channel: e.target.value } });
} else if (e.target.id === "discord_webhook") {
setInputData({ ...inputData, discord: { ...inputData.discord, webhook: e.target.value } });
} else if (e.target.id === "discord_channel") {
setInputData({ ...inputData, discord: { ...inputData.discord, channel: e.target.value } });
}
};
const handleToggleBtn = (e: any) => {
if (e.target.name === "telegram") {
setInputData({ ...inputData, telegram: { ...inputData.telegram, enabled: e.target.checked } });
writeStoredData({ ...data, telegram: { ...data.telegram, enabled: e.target.checked } });
} else if (e.target.name === "slack") {
setInputData({ ...inputData, slack: { ...inputData.slack, enabled: e.target.checked } });
writeStoredData({ ...data, slack: { ...data.slack, enabled: e.target.checked } });
} else if (e.target.name === "discord") {
setInputData({ ...inputData, discord: { ...inputData.discord, enabled: e.target.checked } });
writeStoredData({ ...data, discord: { ...data.discord, enabled: e.target.checked } });
}
};
return (
<div className="backdrop_container">
<div className="dialog_box">
<div className="header">
<span>Notifications</span>
<CloseIcon onClick={handleCloseDialog} />
</div>
<div className="body">
{/* <div className="toggle_btns">
<div className="toggle_btn">
<span>Telegram: </span>
<ToggleBtn
name="telegram"
onChangeHandler={handleToggleBtn}
value={inputData.telegram.enabled}
/>
</div>
<div className="toggle_btn">
<span>Slack: </span>
<ToggleBtn
name="slack"
onChangeHandler={handleToggleBtn}
value={inputData.slack.enabled}
/>
</div>
<div className="toggle_btn">
<span>Discord: </span>
<ToggleBtn
name="discord"
onChangeHandler={handleToggleBtn}
value={inputData.discord.enabled}
/>
</div>
</div> */}
<Tab.Group>
<Tab.List className="tab_list">
{({ selectedIndex }) => (
<>
<Tab
className="tab"
style={{
borderColor: selectedIndex === 0 ? "#3254c5" : "#444444",
opacity: selectedIndex === 0 ? "1" : "0.7",
}}
>
<div
id="editor_button"
style={{
color: inputData.telegram.enabled ? "#36AE7C" : "#bdbdbd",
}}
>
Telegram
</div>
<ToggleBtn
name="telegram"
onChangeHandler={handleToggleBtn}
value={inputData.telegram.enabled}
/>
</Tab>
<Tab
className="tab"
style={{
borderColor: selectedIndex === 1 ? "#3254c5" : "#444444",
opacity: selectedIndex === 1 ? "1" : "0.7",
}}
>
<div
id="editor_button"
style={{
color: inputData.slack.enabled ? "#36AE7C" : "#bdbdbd",
}}
>
Slack
</div>
<ToggleBtn
name="slack"
onChangeHandler={handleToggleBtn}
value={inputData.slack.enabled}
/>
</Tab>
<Tab
className="tab"
style={{
borderColor: selectedIndex === 2 ? "#3254c5" : "#444444",
opacity: selectedIndex === 2 ? "1" : "0.7",
}}
>
<div
id="editor_button"
style={{
color: inputData.discord.enabled ? "#36AE7C" : "#bdbdbd",
}}
>
Discord
</div>
<ToggleBtn
name="discord"
onChangeHandler={handleToggleBtn}
value={inputData.discord.enabled}
/>
</Tab>
</>
)}
</Tab.List>
<Tab.Panels>
<Tab.Panel className="panel">
<input
id="telegram_bot_token"
type="text"
placeholder="Enter telegram bot token"
onChange={handleInput}
value={inputData.telegram.botToken}
/>
<input
id="telegram_chat_id"
type="text"
placeholder="Enter telegram chat ID"
onChange={handleInput}
value={inputData.telegram.chatId}
/>
<div>
<button
type="button"
className="submit_button"
disabled={
inputData.telegram.botToken === "" ||
inputData.telegram.chatId === "" ||
(inputData.telegram.botToken === data.telegram.botToken &&
inputData.telegram.chatId === data.telegram.chatId)
}
onClick={handleTelegramConfirm}
>
Confirm
{isLoading ? <LoadingIcon /> : <ArrowRightIcon />}
</button>
</div>
</Tab.Panel>
<Tab.Panel className="panel">
<input
id="slack_hook_key"
type="text"
placeholder="https://hooks.slack.com/services/XXX/XXX/XXXXXXXX"
onChange={handleInput}
value={inputData.slack.hookKey}
/>
<input
id="slack_channel"
type="text"
placeholder="Enter slack channel (optional)"
onChange={handleInput}
value={inputData.slack.channel}
/>
<div>
<button
type="button"
className="submit_button"
disabled={
inputData.slack.hookKey === "" ||
(inputData.slack.hookKey === data.slack.hookKey && inputData.slack.channel === data.slack.channel)
}
onClick={handleSlackConfirm}
>
Confirm
{isLoading ? <LoadingIcon /> : <ArrowRightIcon />}
</button>
</div>
</Tab.Panel>
<Tab.Panel className="panel">
<input
id="discord_webhook"
type="text"
placeholder="https://discord.com/api/webhooks/XXXXX/XXXXXXXXXX"
onChange={handleInput}
value={inputData.discord.webhook}
/>
<input
id="discord_channel"
type="text"
placeholder="Enter discord channel (optional)"
onChange={handleInput}
value={inputData.discord.channel}
/>
<div>
<button
type="button"
className="submit_button"
disabled={
inputData.discord.webhook === "" ||
(inputData.discord.webhook === data.discord.webhook && inputData.discord.channel === data.discord.channel)
}
onClick={handleDiscordConfirm}
>
Confirm
{isLoading ? <LoadingIcon /> : <ArrowRightIcon />}
</button>
</div>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
</div>
{/* <span>
Please confirm the action, this action can’t be undone and all the client data will be
deleted immediately. You can download a copy of your data in JSON format by clicking the
Export button below or in top right.
</span>
<div className="buttons">
<button type="button" title="Export" className="button" onClick={handleDataExport}>
Export <DownloadIcon />
</button>
</div>
<div className="buttons">
<button
type="button"
disabled={isLoading}
className="confirm_button"
onClick={handleConfirm}
>
Confirm {isLoading ? <LoadingIcon /> : <DeleteIcon />}
</button>
</div> */}
</div>
</div>
);
}