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:
2026-01-07 13:05:27 +03:00
commit d21bfb1320
5867 changed files with 329019 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { u32, u128 } from '@pezkuwi/types';
import type { BN } from '@pezkuwi/util';
import React, { useMemo, useState } from 'react';
import { Button, InputAddress, InputBalance, InputNumber, Modal, TxButton } from '@pezkuwi/react-components';
import { useApi, useToggle } from '@pezkuwi/react-hooks';
import { Available } from '@pezkuwi/react-query';
import { BN_ONE } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
interface Props {
className?: string;
isDisabled: boolean;
proxies: Record<string, string[]>;
}
function Bid ({ className, isDisabled, proxies }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [isOpen, toggleOpen] = useToggle();
const [accountId, setAccountId] = useState<string | null>(null);
const [proxyId, setProxyId] = useState<string | null>(null);
const [amount, setAmount] = useState<BN | undefined>();
const [duration, setDuration] = useState<BN | undefined>();
const tx = useMemo(
() => accountId && amount && duration
? api.tx.proxy.proxy(accountId, null, api.tx.gilt.placeBid(amount, duration))
: null,
[api, accountId, amount, duration]
);
const proxiedAccounts = Object.keys(proxies);
const isAmountError = !amount || amount.isZero() || amount.lt(api.consts.gilt.minFreeze as u128);
const isDurationError = !duration || !duration.gte(BN_ONE) || duration.gt(api.consts.gilt.queueCount as u128);
return (
<>
<Button
icon='plus'
isDisabled={!proxiedAccounts.length || isDisabled}
label={t('Submit Bid')}
onClick={toggleOpen}
/>
{isOpen && (
<Modal
className={className}
header={t('submit gilt bid')}
onClose={toggleOpen}
size='large'
>
<Modal.Content>
<Modal.Columns hint={t('This account will make the bid for the gilt and pay all associated fees.')}>
<InputAddress
filter={proxiedAccounts}
label={t('use proxied account')}
labelExtra={
<Available
label={<span className='label'>{t('transferable')}</span>}
params={accountId}
/>
}
onChange={setAccountId}
type='account'
/>
{accountId && (
<InputAddress
filter={proxies[accountId]}
label={t('send via proxy')}
onChange={setProxyId}
type='account'
/>
)}
</Modal.Columns>
<Modal.Columns hint={t('The amount you wish to lock for the duration. It needs to be more than the gilt minimum.')}>
<InputBalance
autoFocus
defaultValue={api.consts.gilt.minFreeze as u128}
isError={isAmountError}
isZeroable={false}
label={t('bid amount')}
onChange={setAmount}
/>
<InputBalance
defaultValue={api.consts.gilt.minFreeze as u128}
isDisabled
label={t('minimum freeze amount')}
/>
</Modal.Columns>
<Modal.Columns hint={t('The number of periods this bid is to be freezed for, less than the maximum period')}>
<InputNumber
defaultValue={BN_ONE}
isError={isDurationError}
isZeroable={false}
label={t('lock periods')}
onChange={setDuration}
/>
<InputNumber
defaultValue={api.consts.gilt.queueCount as u32}
isDisabled
label={t('maximum lock periods')}
/>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={proxyId}
extrinsic={tx}
icon='check'
isDisabled={isAmountError || isDurationError || !accountId}
label={t('Bid')}
onStart={toggleOpen}
/>
</Modal.Actions>
</Modal>
)}
</>
);
}
export default React.memo(Bid);
+31
View File
@@ -0,0 +1,31 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { QueueTotal } from './types.js';
import React from 'react';
import { Table } from '@pezkuwi/react-components';
import { FormatBalance } from '@pezkuwi/react-query';
import { formatNumber } from '@pezkuwi/util';
interface Props {
className?: string;
value: QueueTotal;
}
function Queue ({ className, value: { balance, index, numItems } }: Props): React.ReactElement<Props> {
return (
<tr className={className}>
<Table.Column.Id value={index} />
<td className='number all'>
{formatNumber(numItems)}
</td>
<td className='all'>
<FormatBalance value={balance} />
</td>
</tr>
);
}
export default React.memo(Queue);
@@ -0,0 +1,43 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { QueueTotal } from './types.js';
import React, { useRef } from 'react';
import { Table } from '@pezkuwi/react-components';
import { useTranslation } from '../translate.js';
import Queue from './Queue.js';
interface Props {
className?: string;
queueTotals?: QueueTotal[];
}
function Queues ({ className, queueTotals }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('queues'), 'start'],
[t('participants'), 'number'],
[t('balance'), 'number']
]);
return (
<Table
className={className}
empty={queueTotals && t('No active gilt queues found.')}
header={headerRef.current}
>
{queueTotals?.map((value) => (
<Queue
key={value.index}
value={value}
/>
))}
</Table>
);
}
export default React.memo(Queues);
@@ -0,0 +1,67 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { u128 } from '@pezkuwi/types';
import type { ActiveGiltsTotal } from '@pezkuwi/types/interfaces';
import React from 'react';
import { CardSummary, SummaryBox } from '@pezkuwi/react-components';
import { useApi, useBestNumber } from '@pezkuwi/react-hooks';
import { BN, BN_HUNDRED, BN_QUINTILL, formatNumber } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
interface Props {
activeTotal?: ActiveGiltsTotal;
className?: string;
isDisabled: boolean;
}
const DIVIDOR_NU = 10_000;
const DIVISOR_BN = new BN(10_000);
function Summary ({ activeTotal, className, isDisabled }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const bestNumber = useBestNumber();
return (
<SummaryBox className={className}>
<section>
<CardSummary label={t('active')}>
{isDisabled ? t('no') : t('yes')}
</CardSummary>
{activeTotal && (
<CardSummary label={t('index')}>
{formatNumber(activeTotal.index)}
</CardSummary>
)}
</section>
{activeTotal && (
<section>
<CardSummary label={t('proportion')}>
{(activeTotal.proportion.mul(DIVISOR_BN).imul(BN_HUNDRED).div(BN_QUINTILL).toNumber() / DIVIDOR_NU).toFixed(2)}%
</CardSummary>
<CardSummary label={t('target')}>
{(activeTotal.target.mul(DIVISOR_BN).imul(BN_HUNDRED).div(BN_QUINTILL).toNumber() / DIVIDOR_NU).toFixed(2)}%
</CardSummary>
</section>
)}
<section>
{bestNumber && (
<CardSummary
label={t('intake')}
progress={{
total: api.consts.gilt.intakePeriod as u128,
value: bestNumber.mod(api.consts.gilt.intakePeriod as u128),
withTime: true
}}
/>
)}
</section>
</SummaryBox>
);
}
export default React.memo(Summary);
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React, { useMemo } from 'react';
import { Button } from '@pezkuwi/react-components';
import { useProxies } from '../useProxies.js';
import BidAdd from './BidAdd.js';
import Queues from './Queues.js';
import Summary from './Summary.js';
import useInfo from './useInfo.js';
interface Props {
className?: string;
}
function Overview ({ className }: Props): React.ReactElement<Props> {
const proxies = useProxies();
const { info } = useInfo();
const isDisabled = useMemo(
() => !info?.activeTotal || info.activeTotal.target.isZero(),
[info]
);
return (
<div className={className}>
<Summary
activeTotal={info?.activeTotal}
isDisabled={isDisabled}
/>
<Button.Group>
<BidAdd
isDisabled={isDisabled}
proxies={proxies}
/>
</Button.Group>
<Queues queueTotals={info?.queueTotals} />
</div>
);
}
export default React.memo(Overview);
+17
View File
@@ -0,0 +1,17 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ActiveGiltsTotal, BalanceOf } from '@pezkuwi/types/interfaces';
import type { BN } from '@pezkuwi/util';
export interface QueueTotal {
balance: BalanceOf;
index: number;
numItems: BN;
}
export interface GiltInfo {
activeIndex?: BN | null;
activeTotal?: ActiveGiltsTotal;
queueTotals?: QueueTotal[];
}
@@ -0,0 +1,51 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { u32 } from '@pezkuwi/types';
import type { ActiveGiltsTotal, BalanceOf } from '@pezkuwi/types/interfaces';
import type { GiltInfo, QueueTotal } from './types.js';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCallMulti } from '@pezkuwi/react-hooks';
import { BN_ONE } from '@pezkuwi/util';
interface State {
info?: GiltInfo;
}
const OPT_GILT = {
defaultValue: {} as GiltInfo,
transform: ([activeTotal, queueTotals]: [ActiveGiltsTotal, [u32, BalanceOf][]]): GiltInfo => ({
activeIndex: activeTotal.index.isZero()
? null
: activeTotal.index.sub(BN_ONE),
activeTotal,
queueTotals: queueTotals
.map(([numItems, balance], index): QueueTotal => ({ balance, index: index + 1, numItems }))
.filter(({ balance }) => !balance.isZero())
})
};
function useInfoImpl (): State {
const { api } = useApi();
const info = useCallMulti<GiltInfo>([
api.query.gilt.activeTotal,
api.query.gilt.queueTotals
], OPT_GILT);
// useEffect((): void => {
// info.activeIndex &&
// api.query.gilt.active
// .entries()
// .then((value) => console.log(JSON.stringify(value)))
// .catch(console.error);
// }, [api, info?.activeIndex]);
return useMemo(
() => ({ info }),
[info]
);
}
export default createNamedHook('useInfo', useInfoImpl);
+48
View File
@@ -0,0 +1,48 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React, { useRef } from 'react';
import { Route, Routes } from 'react-router';
import { Tabs } from '@pezkuwi/react-components';
import Overview from './Overview/index.js';
import { useTranslation } from './translate.js';
interface Props {
basePath: string;
className?: string;
}
function GiltApp ({ basePath, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const tabsRef = useRef([
{
isRoot: true,
name: 'overview',
text: t('Overview')
}
]);
return (
<main className={className}>
<Tabs
basePath={basePath}
items={tabsRef.current}
/>
<Routes>
<Route path={basePath}>
<Route
element={
<Overview />
}
index
/>
</Route>
</Routes>
</main>
);
}
export default React.memo(GiltApp);
+8
View File
@@ -0,0 +1,8 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { useTranslation as useTranslationBase } from 'react-i18next';
export function useTranslation (): { t: (key: string, options?: { replace: Record<string, unknown> }) => string } {
return useTranslationBase('app-gilt');
}
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2017-2025 @pezkuwi/app-gilt authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Vec } from '@pezkuwi/types';
import type { BalanceOf, ProxyDefinition } from '@pezkuwi/types/interfaces';
import type { ITuple } from '@pezkuwi/types/types';
import { useEffect, useState } from 'react';
import { createNamedHook, useAccounts, useApi, useIsMountedRef } from '@pezkuwi/react-hooks';
type ProxyResult = ITuple<[Vec<ProxyDefinition>, BalanceOf]>;
function useProxiesImpl (): Record<string, string[]> {
const { api } = useApi();
const { allAccounts } = useAccounts();
const mountedRef = useIsMountedRef();
const [state, setState] = useState<Record<string, string[]>>({});
useEffect((): void => {
if (allAccounts.length) {
api.query.proxy.proxies
.multi<ProxyResult>(allAccounts)
.then((result) =>
mountedRef.current && setState(
result
.map(([p], index): [string, string[]] => [
allAccounts[index],
p.map(({ delegate }) => delegate.toString())
])
.filter(([, p]) => p.length)
.reduce((all, [a, p]) => ({ ...all, [a]: p }), {})
)
)
.catch(console.error);
}
}, [allAccounts, api, mountedRef]);
return state;
}
export const useProxies = createNamedHook('useProxies', useProxiesImpl);