feat: initial Pezkuwi Apps rebrand from polkadot-apps

Rebranded terminology:
- Polkadot → Pezkuwi
- Kusama → Dicle
- Westend → Zagros
- Rococo → PezkuwiChain
- Substrate → Bizinikiwi
- parachain → teyrchain

Custom logos with Kurdistan brand colors (#e6007a → #86e62a):
- bizinikiwi-hexagon.svg
- sora-bizinikiwi.svg
- hezscanner.svg
- heztreasury.svg
- pezkuwiscan.svg
- pezkuwistats.svg
- pezkuwiassembly.svg
- pezkuwiholic.svg
This commit is contained in:
Kurdistan Tech Ministry committed 2026-01-07 13:05:27 +03:00
commit d21bfb1320
5867 files changed
+329019

No files matched your search

@@ -0,0 +1,69 @@
// Copyright 2017-2025 @pezkuwi/app-assets authors & contributors
// SPDX-License-Identifier: Apache-2.0
// augment package
import '@pezkuwi/api-augment/bizinikiwi';
import type { PalletAssetsAssetAccount } from '@pezkuwi/types/lookup';
import type { bool } from '@pezkuwi/types-codec';
import type { BN } from '@pezkuwi/util';
import React from 'react';
import { AddressSmall } from '@pezkuwi/react-components';
import { FormatBalance } from '@pezkuwi/react-query';
import { useTranslation } from '../translate.js';
import Transfer from './Transfer.js';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This looks correct in the editor, but incorrect in composite mode
interface AccountExt extends PalletAssetsAssetAccount {
isFrozen?: bool;
sufficient?: bool
}
interface Props {
account: AccountExt;
accountId: string;
assetId: BN;
className?: string;
minBalance: BN;
siFormat: [number, string];
}
function Account ({ account: { balance, isFrozen, reason, sufficient }, accountId, assetId, minBalance, siFormat }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
return (
<>
<td className='address'>
<AddressSmall value={accountId} />
</td>
<td className='start'>
{isFrozen?.isTrue ? t('Yes') : t('No')}
</td>
<td className='start'>
{sufficient
? sufficient.isTrue ? t('Yes') : t('No')
: reason?.toString()}
</td>
<td className='number all'>
<FormatBalance
format={siFormat}
value={balance}
/>
</td>
<td className='button'>
<Transfer
accountId={accountId}
assetId={assetId}
minBalance={minBalance}
siFormat={siFormat}
/>
</td>
</>
);
}
export default React.memo(Account);
@@ -0,0 +1,83 @@
// Copyright 2017-2025 @pezkuwi/app-assets authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { AssetInfoComplete } from '@pezkuwi/react-hooks/types';
import React, { useMemo } from 'react';
import { styled } from '@pezkuwi/react-components';
import { formatNumber } from '@pezkuwi/util';
import Account from './Account.js';
import useBalances from './useBalances.js';
interface Props {
asset: AssetInfoComplete,
className?: string;
searchValue: string;
}
const Asset = ({ asset: { details, id, metadata }, className, searchValue }: Props) => {
const balances = useBalances(id);
const siFormat = useMemo(
(): [number, string] => metadata
? [metadata.decimals.toNumber(), metadata.symbol.toUtf8().toUpperCase()]
: [0, 'NONE'],
[metadata]
);
const shouldShowAsset = useMemo(() => metadata.name.toUtf8().toLowerCase().includes(searchValue) ||
formatNumber(id).toString().replaceAll(',', '').includes(searchValue), [id, metadata.name, searchValue]);
if (!balances?.length || !shouldShowAsset) {
return <></>;
}
return balances.map(({ account, accountId }, index) => {
return (
<StyledTr
className={`isExpanded ${className}`}
isFirstItem={index === 0}
isLastItem={index === balances.length - 1}
key={accountId}
>
<td className='all'>
{index === 0 && <>{metadata.name.toUtf8()} ({formatNumber(id)})</>}
</td>
<Account
account={account}
accountId={accountId}
assetId={id}
key={accountId}
minBalance={details.minBalance}
siFormat={siFormat}
/>
</StyledTr>
);
});
};
const BASE_BORDER = 0.125;
const BORDER_TOP = `${BASE_BORDER * 3}rem solid var(--bg-page)`;
const BORDER_RADIUS = `${BASE_BORDER * 4}rem`;
const StyledTr = styled.tr<{isFirstItem: boolean; isLastItem: boolean}>`
td {
border-top: ${(props) => props.isFirstItem && BORDER_TOP};
border-radius: 0rem !important;
&:first-child {
padding-block: 1rem !important;
border-top-left-radius: ${(props) => props.isFirstItem ? BORDER_RADIUS : '0rem'}!important;
border-bottom-left-radius: ${(props) => props.isLastItem ? BORDER_RADIUS : '0rem'}!important;
}
&:last-child {
border-top-right-radius: ${(props) => props.isFirstItem ? BORDER_RADIUS : '0rem'}!important;
border-bottom-right-radius: ${(props) => props.isLastItem ? BORDER_RADIUS : '0rem'}!important;
}
}
`;
export default Asset;
@@ -0,0 +1,109 @@
// Copyright 2017-2025 @pezkuwi/app-assets authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@pezkuwi/util';
import React, { useState } from 'react';
import { Button, InputAddress, InputBalance, Modal, Toggle, TxButton } from '@pezkuwi/react-components';
import { useApi, useToggle } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
interface Props {
accountId: string;
assetId: BN;
className?: string;
minBalance: BN;
siFormat: [number, string];
}
function Transfer ({ accountId, assetId, className, minBalance, siFormat: [siDecimals, siSymbol] }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [isOpen, toggleOpen] = useToggle();
const [amount, setAmount] = useState<BN | undefined>();
const [recipientId, setRecipientId] = useState<string | null>(null);
const [isProtected, setIsProtected] = useState(true);
return (
<>
<Button
icon='paper-plane'
label={t('send')}
onClick={toggleOpen}
/>
{isOpen && (
<Modal
className={className}
header={t('transfer asset')}
onClose={toggleOpen}
size='large'
>
<Modal.Content>
<Modal.Columns hint={t('The account to transfer from. This account should have sufficient assets for this transfer.')}>
<InputAddress
defaultValue={accountId}
isDisabled
label={t('send from')}
/>
</Modal.Columns>
<Modal.Columns hint={t('The beneficiary will have access to the transferred asset when the transaction is included in a block.')}>
<InputAddress
label={t('send to address')}
onChange={setRecipientId}
type='allPlus'
/>
</Modal.Columns>
<Modal.Columns hint={t('The amount of tokens to transfer to the account.')}>
<InputBalance
autoFocus
label={t('amount to transfer')}
onChange={setAmount}
siDecimals={siDecimals}
siSymbol={siSymbol}
/>
</Modal.Columns>
<Modal.Columns hint={t('The minimum balance allowed for the asset.')}>
<InputBalance
defaultValue={minBalance}
isDisabled
label={t('minimum balance')}
siDecimals={siDecimals}
siSymbol={siSymbol}
/>
</Modal.Columns>
<Modal.Columns hint={t('With the keep-alive option set, the account is protected against removal due to low balances.')}>
<Toggle
className='typeToggle'
label={
isProtected
? t('Transfer with account keep-alive checks')
: t('Normal transfer without keep-alive checks')
}
onChange={setIsProtected}
value={isProtected}
/>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={accountId}
icon='paper-plane'
isDisabled={!recipientId || !amount}
label={t('Send')}
onStart={toggleOpen}
params={[assetId, recipientId, amount]}
tx={
isProtected
? api.tx.assets.transferKeepAlive
: api.tx.assets.transfer}
/>
</Modal.Actions>
</Modal>
)}
</>
);
}
export default React.memo(Transfer);
@@ -0,0 +1,80 @@
// Copyright 2017-2025 @pezkuwi/app-assets authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { AssetInfo, AssetInfoComplete } from '@pezkuwi/react-hooks/types';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { Input, styled, Table } from '@pezkuwi/react-components';
import { useTranslation } from '../translate.js';
import Asset from './Asset.js';
interface Props {
className?: string;
infos?: AssetInfo[];
}
function Balances ({ className, infos = [] }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [{ searchValue }, onApplySearch] = useState({ searchValue: '' });
const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('asset'), 'start'],
[t('accounts'), 'start'],
[t('frozen'), 'start'],
[t('sufficient'), 'start'],
[t('free balance'), 'start'],
[]
]);
const onChangeInput = useCallback((e: string) => {
onApplySearch({ searchValue: e });
}, []);
const completeAssets = useMemo(
() => infos
.filter((i): i is AssetInfoComplete => !!(i.details && i.metadata) && !i.details.supply.isZero())
,
[infos]
);
return (
<StyledDiv className={className}>
<Input
aria-label={t('Search by asset id or name')}
className='full isSmall'
label={t('Search')}
onChange={onChangeInput}
placeholder={t('Search by asset id or name')}
value={searchValue}
/>
<Table
empty={t('No accounts with balances found for the asset')}
header={headerRef.current}
>
{completeAssets.map((asset) => {
return (
<Asset
asset={asset}
key={asset.id.toString()}
searchValue={searchValue.toLowerCase()}
/>
);
})}
</Table>
</StyledDiv>
);
}
const StyledDiv = styled.div`
input {
max-width: 250px !important;
}
table {
overflow: auto;
}
`;
export default React.memo(Balances);
@@ -0,0 +1,60 @@
// Copyright 2017-2025 @pezkuwi/app-assets authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAssetsAssetAccount } from '@pezkuwi/types/lookup';
import type { Option } from '@pezkuwi/types-codec';
import type { BN } from '@pezkuwi/util';
import { useMemo } from 'react';
import { createNamedHook, useAccounts, useApi, useCall } from '@pezkuwi/react-hooks';
interface AccountResult {
accountId: string;
account: PalletAssetsAssetAccount;
}
interface Result {
assetId: BN;
accounts: AccountResult[];
}
function isOptional (value: PalletAssetsAssetAccount | Option<PalletAssetsAssetAccount>): value is Option<PalletAssetsAssetAccount> {
return (value as Option<PalletAssetsAssetAccount>).isSome || (value as Option<PalletAssetsAssetAccount>).isNone;
}
const OPTS = {
transform: ([[params], accounts]: [[[BN, string][]], (PalletAssetsAssetAccount | Option<PalletAssetsAssetAccount>)[]]): Result => ({
accounts: params
.map(([, accountId], index) => {
const o = accounts[index];
return {
account: isOptional(o)
? o.unwrapOr(null)
: o,
accountId
};
})
.filter((a): a is AccountResult =>
!!a.account &&
!a.account.balance.isZero()
),
assetId: params[0][0]
}),
withParamsTransform: true
};
function useBalancesImpl (id?: BN | null): AccountResult[] | null {
const { api } = useApi();
const { allAccounts } = useAccounts();
const keys = useMemo(
() => [allAccounts.map((a) => [id, a]).filter((tup) => !!tup[0])],
[allAccounts, id]
);
const query = useCall(keys && api.query.assets.account.multi, keys, OPTS);
return (query && id && (query.assetId === id) && query.accounts) || null;
}
export default createNamedHook('useBalances', useBalancesImpl);