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
@@ -0,0 +1,77 @@
// Copyright 2017-2025 @pezkuwi/app-explorer authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PeerInfo } from '@pezkuwi/types/interfaces';
import React, { useMemo, useRef } from 'react';
import { styled, Table } from '@pezkuwi/react-components';
import { formatNumber, stringPascalCase } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
interface Props {
className?: string;
peers?: PeerInfo[] | null;
}
function sortPeers (peers: PeerInfo[]) {
return peers
.map(({ bestHash, bestNumber, peerId, roles }) => ({
bestHash: bestHash.toHex(),
bestNumber,
peerId: peerId.toString(),
roles: stringPascalCase(roles)
}))
.sort((a, b) => a.peerId.localeCompare(b.peerId))
.sort((a, b) => a.roles.localeCompare(b.roles))
.sort((a, b) => b.bestNumber.cmp(a.bestNumber));
}
function Peers ({ className = '', peers }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('connected peers'), 'start', 2],
[t('best hash'), 'start'],
[t('best #'), 'number']
]);
const sorted = useMemo(
() => peers && sortPeers(peers),
[peers]
);
return (
<StyledTable
className={className}
empty={t('no peers connected')}
header={headerRef.current}
>
{sorted?.map(({ bestHash, bestNumber, peerId, roles }) => (
<tr key={peerId}>
<td className='roles'>{roles}</td>
<td className='hash overflow'>{peerId}</td>
<td className='hash overflow'>{bestHash}</td>
<td className='number bestNumber'>{formatNumber(bestNumber)}</td>
</tr>
))}
</StyledTable>
);
}
const StyledTable = styled(Table)`
overflow-x: auto;
td.roles {
max-width: 9ch;
width: 9ch;
}
td.bestNumber {
max-width: 11ch;
width: 11ch;
}
`;
export default React.memo(Peers);
@@ -0,0 +1,84 @@
// Copyright 2017-2025 @pezkuwi/app-explorer authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Info } from './types.js';
import React, { useEffect, useState } from 'react';
import { CardSummary, SummaryBox } from '@pezkuwi/react-components';
import { BestNumber, Elapsed } from '@pezkuwi/react-query';
import { BN_ZERO, formatNumber } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
interface Props {
nextRefresh: number;
info: Info;
}
const EMPTY_INFO = { extrinsics: null, health: null, peers: null };
function Summary ({ info: { extrinsics, health, peers } = EMPTY_INFO, nextRefresh }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [peerBest, setPeerBest] = useState(BN_ZERO);
useEffect((): void => {
if (peers) {
const bestPeer = peers.sort((a, b): number => b.bestNumber.cmp(a.bestNumber))[0];
setPeerBest(
bestPeer
? bestPeer.bestNumber
: BN_ZERO
);
}
}, [peers]);
return (
<SummaryBox>
<section>
<CardSummary label={t('refresh in')}>
<Elapsed value={nextRefresh} />
</CardSummary>
{health && (
<>
<CardSummary
className='media--800'
label={t('total peers')}
>
{formatNumber(health.peers)}
</CardSummary>
<CardSummary
className='media--800'
label={t('syncing')}
>
{health.isSyncing.valueOf()
? t('yes')
: t('no')
}
</CardSummary>
</>
)}
</section>
{extrinsics && (extrinsics.length > 0) && (
<section className='media--1200'>
<CardSummary label={t('queued tx')}>
{extrinsics.length}
</CardSummary>
</section>
)}
<section>
{peerBest?.gtn(0) && (
<CardSummary label={t('peer best')}>
{formatNumber(peerBest)}
</CardSummary>
)}
<CardSummary label={t('our best')}>
<BestNumber />
</CardSummary>
</section>
</SummaryBox>
);
}
export default React.memo(Summary);
@@ -0,0 +1,72 @@
// Copyright 2017-2025 @pezkuwi/app-explorer authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ApiPromise } from '@pezkuwi/api';
import type { Info } from './types.js';
import React, { useEffect, useState } from 'react';
import { useApi } from '@pezkuwi/react-hooks';
import Extrinsics from '../BlockInfo/Extrinsics.js';
import { useTranslation } from '../translate.js';
import Peers from './Peers.js';
import Summary from './Summary.js';
const POLL_TIMEOUT = 9900;
async function retrieveInfo (api: ApiPromise): Promise<Partial<Info>> {
try {
const [blockNumber, health, peers, extrinsics] = await Promise.all([
api.derive.chain.bestNumber(),
api.rpc.system.health().catch(() => null),
api.rpc.system.peers().catch(() => null),
api.rpc.author.pendingExtrinsics().catch(() => null)
]);
return { blockNumber, extrinsics, health, peers };
} catch {
return {};
}
}
function NodeInfo (): React.ReactElement {
const { t } = useTranslation();
const { api } = useApi();
const [info, setInfo] = useState<Partial<Info>>({});
const [nextRefresh, setNextRefresh] = useState(() => Date.now());
useEffect((): () => void => {
const getStatus = (): void => {
setNextRefresh(Date.now() + POLL_TIMEOUT);
retrieveInfo(api).then(setInfo).catch(console.error);
};
getStatus();
const timerId = window.setInterval(getStatus, POLL_TIMEOUT);
return (): void => {
window.clearInterval(timerId);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<Summary
info={info}
nextRefresh={nextRefresh}
/>
<Peers peers={info.peers} />
<Extrinsics
blockNumber={info.blockNumber}
label={t('pending extrinsics')}
value={info.extrinsics}
withLink
/>
</>
);
}
export default React.memo(NodeInfo);
@@ -0,0 +1,12 @@
// Copyright 2017-2025 @pezkuwi/app-explorer authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Vec } from '@pezkuwi/types';
import type { BlockNumber, Extrinsic, Health, PeerInfo } from '@pezkuwi/types/interfaces';
export interface Info {
blockNumber?: BlockNumber;
extrinsics?: Vec<Extrinsic> | null;
health?: Health | null;
peers?: PeerInfo[] | null;
}