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,74 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Member, Unscrupulous } from '../types.js';
import React, { useMemo, useState } from 'react';
import { InputAddress, InputBalance, Modal, TxButton } from '@pezkuwi/react-components';
import { useAccounts, useApi } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
interface Props {
className?: string;
members: Member[];
onClose: () => void;
unscrupulous: Unscrupulous;
}
function Join ({ className, members, onClose, unscrupulous: { accounts } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
const [accountId, setAccountId] = useState<string | null>(null);
const available = useMemo(
(): string[] =>
allAccounts.filter((a) =>
!members.some(({ accountId }) =>
accountId === a
) &&
!accounts.some((accountId) =>
accountId === a
)
),
[accounts, allAccounts, members]
);
return (
<Modal
className={className}
header={t('Join alliance')}
onClose={onClose}
size='large'
>
<Modal.Content>
<Modal.Columns hint={t('This account will be submitted to join the aliance. It will be allocated one of the alliance roles upon joining, starting with Ally.')}>
<InputAddress
filter={available}
label={t('alliance account')}
onChange={setAccountId}
type='account'
/>
</Modal.Columns>
<Modal.Columns hint={t('The bond will be reserved for the duration of your alliance membership.')}>
<InputBalance
defaultValue={api.consts.alliance.allyDeposit}
isDisabled
label={t('alliance deposit')}
/>
</Modal.Columns>
</Modal.Content>
<Modal.Actions>
<TxButton
accountId={accountId}
onStart={onClose}
tx={api.tx.alliance.joinAlliance}
/>
</Modal.Actions>
</Modal>
);
}
export default React.memo(Join);
@@ -0,0 +1,123 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@pezkuwi/util';
import type { Member as MemberType } from '../types.js';
import React, { useCallback, useMemo } from 'react';
import { AddressSmall, Menu, Popup, Tag } from '@pezkuwi/react-components';
import { useAccounts, useApi, useQueue } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
import useMemberInfo from '../useMemberInfo.js';
interface Props {
bestNumber?: BN;
className?: string;
info: MemberType;
isPrime: boolean;
isVoter: boolean;
}
function Member ({ bestNumber, className, info: { accountId, role }, isPrime, isVoter }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
const info = useMemberInfo(accountId);
const { queueExtrinsic } = useQueue();
const hasNotice = !!api.tx.alliance.giveRetirementNotice;
const hasActions = useMemo(
() => allAccounts.includes(accountId),
[allAccounts, accountId]
);
const doRetire = useCallback(
() => queueExtrinsic({
accountId,
extrinsic: api.tx.alliance.retire()
}),
[accountId, api, queueExtrinsic]
);
const doNotice = useCallback(
() => queueExtrinsic({
accountId,
extrinsic: api.tx.alliance.giveRetirementNotice()
}),
[accountId, api, queueExtrinsic]
);
return (
<tr className={className}>
<td className='address all relative'>
<AddressSmall value={accountId} />
<div className='absolute'>
{(info?.isRetiringAt && (
<Tag
color='yellow'
hover={t('Is retiring')}
label={t('retirting')}
/>
)) || (info?.isUpForKicking && (
<Tag
color='red'
hover={t('Up for kicking')}
label={t('kicking')}
/>
)) || (isPrime && (
<Tag
color='green'
hover={t('Current prime member, default voting')}
label={t('prime voter')}
/>
)) || (isVoter && (
<Tag
color='green'
hover={t('Allowed to vote on motions')}
label={t('voter')}
/>
))}
</div>
</td>
<td className='number'>
{role}
</td>
<td className='button'>
{hasActions && (
<Popup
key='settings'
value={
<Menu>
{hasNotice && (
<Menu.Item
isDisabled={!!(info?.isRetiringAt)}
label={t('Announce retirement')}
onClick={doNotice}
/>
)}
<Menu.Item
isDisabled={
!!info && (
info.isUpForKicking ||
(
hasNotice
? !bestNumber || !info.isRetiringAt || info.isRetiringAt.lt(bestNumber)
: false
)
)
}
label={t('Retire')}
onClick={doRetire}
/>
</Menu>
}
/>
)}
</td>
</tr>
);
}
export default React.memo(Member);
@@ -0,0 +1,52 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Member as MemberType, Rule } from '../types.js';
import React from 'react';
import { CardSummary, SummaryBox } from '@pezkuwi/react-components';
import { useIpfsLink } from '@pezkuwi/react-hooks';
import { formatNumber } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
interface Props {
className?: string;
members?: MemberType[];
rule?: Rule;
}
function Summary ({ className, members, rule }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const ipfsLink = useIpfsLink(rule?.cid?.ipfs);
return (
<SummaryBox className={className}>
<CardSummary label={t('rule')}>
{rule
? rule.hasRule
? ipfsLink
? (
<a
href={ipfsLink.ipfsUrl}
rel='noopener noreferrer'
target='_blank'
>{ipfsLink.ipfsShort}</a>
)
: t('yes')
: t('no')
: <span className='--tmp'>{t('no')}</span>
}
</CardSummary>
<CardSummary label={t('members')}>
{members
? formatNumber(members.length)
: <span className='--tmp'>99</span>
}
</CardSummary>
</SummaryBox>
);
}
export default React.memo(Summary);
@@ -0,0 +1,76 @@
// Copyright 2017-2025 @pezkuwi/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Member as MemberType, Rule, Unscrupulous } from '../types.js';
import React, { useRef } from 'react';
import { Button, Table } from '@pezkuwi/react-components';
import { useBestNumber, useToggle } from '@pezkuwi/react-hooks';
import { useTranslation } from '../translate.js';
import Join from './Join.js';
import Member from './Member.js';
import Summary from './Summary.js';
interface Props {
className?: string;
isVoter?: boolean;
members?: MemberType[];
prime?: string | null;
rule?: Rule;
unscrupulous?: Unscrupulous;
voters?: string[];
}
function Overview ({ className, members, prime, rule, unscrupulous, voters }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isJoinOpen, toggleJoin] = useToggle();
const bestNumber = useBestNumber();
const hdrRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
[t('members'), 'start', 3]
]);
return (
<div className={className}>
<Summary
members={members}
rule={rule}
/>
<Button.Group>
<Button
icon='add'
isDisabled={!members || !unscrupulous}
label={t('Join')}
onClick={toggleJoin}
/>
{members && unscrupulous && isJoinOpen && (
<Join
members={members}
onClose={toggleJoin}
unscrupulous={unscrupulous}
/>
)}
</Button.Group>
<Table
empty={members && t('No members')}
header={hdrRef.current}
isSplit
maxColumns={2}
>
{members?.map((m) => (
<Member
bestNumber={bestNumber}
info={m}
isPrime={prime === m.accountId}
isVoter={!!voters && voters.includes(m.accountId)}
key={m.accountId}
/>
))}
</Table>
</div>
);
}
export default React.memo(Overview);