auto-commit for bed65b0f-c949-4d15-b953-0bf08c7c9e55

This commit is contained in:
emergent-agent-e1
2025-10-24 02:45:21 +00:00
parent b734c7ac00
commit 208ae2176c
40 changed files with 9223 additions and 56 deletions
+12
View File
@@ -0,0 +1,12 @@
import React from 'react';
import { StatusBar } from 'expo-status-bar';
import RootNavigator from './src/navigation/RootNavigator';
export default function App() {
return (
<>
<StatusBar style="auto" />
<RootNavigator />
</>
);
}
+14 -26
View File
@@ -1,42 +1,30 @@
{ {
"expo": { "expo": {
"name": "frontend", "name": "pezkuwi-mobile-app",
"slug": "frontend", "slug": "pezkuwi-mobile-app",
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/images/icon.png", "icon": "./assets/icon.png",
"scheme": "frontend", "userInterfaceStyle": "light",
"userInterfaceStyle": "automatic",
"newArchEnabled": true, "newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": { "ios": {
"supportsTablet": true "supportsTablet": true
}, },
"android": { "android": {
"adaptiveIcon": { "adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png", "foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#000" "backgroundColor": "#ffffff"
}, },
"edgeToEdgeEnabled": true "edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false
}, },
"web": { "web": {
"bundler": "metro", "favicon": "./assets/favicon.png"
"output": "static",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
"expo-router",
[
"expo-splash-screen",
{
"image": "./assets/images/splash-icon.png",
"imageWidth": 200,
"resizeMode": "contain",
"backgroundColor": "#000"
}
]
],
"experiments": {
"typedRoutes": true
} }
} }
} }
-30
View File
@@ -1,30 +0,0 @@
import { Text, View, StyleSheet, Image } from "react-native";
const EXPO_PUBLIC_BACKEND_URL = process.env.EXPO_PUBLIC_BACKEND_URL;
export default function Index() {
console.log(EXPO_PUBLIC_BACKEND_URL, "EXPO_PUBLIC_BACKEND_URL");
return (
<View style={styles.container}>
<Image
source={require("../assets/images/app-image.png")}
style={styles.image}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0c0c0c",
alignItems: "center",
justifyContent: "center",
},
image: {
width: "100%",
height: "100%",
resizeMode: "contain",
},
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

+8
View File
@@ -0,0 +1,8 @@
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
+225
View File
@@ -0,0 +1,225 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
Modal,
TouchableOpacity,
Image,
Alert,
Share,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import QRCode from 'react-native-qrcode-svg';
import * as Clipboard from 'expo-clipboard';
import Colors from '../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../constants/theme';
interface ReceiveModalProps {
visible: boolean;
onClose: () => void;
token: {
symbol: string;
name: string;
icon: any;
color: string;
};
}
export default function ReceiveModal({ visible, onClose, token }: ReceiveModalProps) {
// Mock wallet address - in production, this would come from user's wallet
const walletAddress = 'pezkuwi1a2b3c4d5e6f7g8h9i0j';
const network = 'Optimism';
const handleCopyAddress = async () => {
await Clipboard.setStringAsync(walletAddress);
Alert.alert('Copied!', 'Wallet address copied to clipboard');
};
const handleShareAddress = async () => {
try {
await Share.share({
message: `My ${token.symbol} wallet address:\n${walletAddress}\n\nNetwork: ${network}`,
title: `${token.symbol} Wallet Address`,
});
} catch (error) {
console.error('Error sharing:', error);
}
};
return (
<Modal
visible={visible}
transparent
animationType="slide"
onRequestClose={onClose}
>
<View style={styles.overlay}>
<View style={styles.modalContainer}>
{/* Close Button */}
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
<Ionicons name="close" size={28} color={Colors.textDark} />
</TouchableOpacity>
{/* Token Icon */}
<Image source={token.icon} style={styles.tokenIcon} />
{/* Title */}
<Text style={styles.title}>Receive {token.symbol}</Text>
{/* QR Code Card */}
<View style={styles.qrCard}>
<QRCode value={walletAddress} size={200} />
</View>
{/* Wallet Address */}
<TouchableOpacity
style={styles.addressContainer}
onPress={handleCopyAddress}
activeOpacity={0.7}
>
<Text style={styles.addressText}>{walletAddress}</Text>
<Ionicons name="copy-outline" size={20} color={Colors.primary} />
</TouchableOpacity>
<Text style={styles.tapToCopy}>Tap to copy</Text>
{/* Network Badge */}
<View style={styles.networkBadge}>
<Ionicons name="globe-outline" size={20} color="#007AFF" />
<Text style={styles.networkText}>{network} Network</Text>
</View>
{/* Share Button */}
<TouchableOpacity style={styles.shareButton} onPress={handleShareAddress}>
<Ionicons name="share-outline" size={20} color={Colors.textDark} />
<Text style={styles.shareButtonText}>Share Address</Text>
</TouchableOpacity>
{/* Warning Box */}
<View style={styles.warningBox}>
<Ionicons name="warning-outline" size={24} color="#F59E0B" />
<Text style={styles.warningText}>
Only send {token.symbol} to this address on {network} network
</Text>
</View>
</View>
</View>
</Modal>
);
}
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
padding: Spacing.lg,
},
modalContainer: {
width: '100%',
maxWidth: 400,
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.xl,
padding: Spacing.xl,
...Shadow.medium,
},
closeButton: {
position: 'absolute',
top: Spacing.md,
right: Spacing.md,
zIndex: 1,
},
tokenIcon: {
width: 60,
height: 60,
alignSelf: 'center',
marginTop: Spacing.md,
},
title: {
fontSize: Typography.sizes.xxl,
fontWeight: Typography.weights.bold,
color: Colors.textDark,
textAlign: 'center',
marginTop: Spacing.sm,
marginBottom: Spacing.lg,
},
qrCard: {
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.lg,
padding: Spacing.lg,
alignItems: 'center',
justifyContent: 'center',
...Shadow.small,
alignSelf: 'center',
},
addressContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.background,
borderRadius: BorderRadius.md,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
marginTop: Spacing.lg,
},
addressText: {
fontSize: Typography.sizes.sm,
fontFamily: 'monospace',
color: Colors.textDark,
marginRight: Spacing.sm,
},
tapToCopy: {
fontSize: Typography.sizes.xs,
color: Colors.textLight,
textAlign: 'center',
marginTop: Spacing.xs,
},
networkBadge: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#E3F2FD',
borderRadius: BorderRadius.md,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
marginTop: Spacing.lg,
alignSelf: 'center',
},
networkText: {
fontSize: Typography.sizes.md,
fontWeight: Typography.weights.semibold,
color: '#007AFF',
marginLeft: Spacing.xs,
},
shareButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.background,
borderRadius: BorderRadius.md,
paddingVertical: Spacing.md,
marginTop: Spacing.lg,
},
shareButtonText: {
fontSize: Typography.sizes.md,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
marginLeft: Spacing.sm,
},
warningBox: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FEF3C7',
borderRadius: BorderRadius.md,
padding: Spacing.md,
marginTop: Spacing.lg,
},
warningText: {
flex: 1,
fontSize: Typography.sizes.sm,
color: '#92400E',
marginLeft: Spacing.sm,
},
});
+300
View File
@@ -0,0 +1,300 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
Modal,
TouchableOpacity,
TextInput,
Image,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../constants/theme';
interface SendModalProps {
visible: boolean;
onClose: () => void;
token: {
symbol: string;
name: string;
balance: string;
icon: any;
color: string;
};
}
export default function SendModal({ visible, onClose, token }: SendModalProps) {
const [recipientAddress, setRecipientAddress] = useState('');
const [amount, setAmount] = useState('');
const [network, setNetwork] = useState('Optimism');
const handleMaxAmount = () => {
setAmount(token.balance);
};
const handleSend = () => {
if (!recipientAddress) {
Alert.alert('Error', 'Please enter recipient address');
return;
}
if (!amount || parseFloat(amount) <= 0) {
Alert.alert('Error', 'Please enter valid amount');
return;
}
if (parseFloat(amount) > parseFloat(token.balance)) {
Alert.alert('Error', 'Insufficient balance');
return;
}
Alert.alert(
'Confirm Transaction',
`Send ${amount} ${token.symbol} to ${recipientAddress.substring(0, 10)}...?`,
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Confirm',
onPress: () => {
// TODO: Integrate with blockchain service
Alert.alert('Success', 'Transaction submitted!');
onClose();
setRecipientAddress('');
setAmount('');
},
},
]
);
};
const handleQRScan = () => {
// TODO: Open QR scanner
Alert.alert('QR Scanner', 'QR scanner will be implemented');
};
const handlePaste = async () => {
// TODO: Paste from clipboard
Alert.alert('Paste', 'Clipboard paste will be implemented');
};
const usdValue = (parseFloat(amount) || 0) * 1.5; // Mock exchange rate
return (
<Modal
visible={visible}
transparent
animationType="slide"
onRequestClose={onClose}
>
<View style={styles.overlay}>
<View style={styles.modalContainer}>
{/* Close Button */}
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
<Ionicons name="close" size={28} color={Colors.textDark} />
</TouchableOpacity>
{/* Token Icon */}
<Image source={token.icon} style={styles.tokenIcon} />
{/* Title */}
<Text style={styles.title}>Send {token.symbol}</Text>
{/* Available Balance */}
<Text style={styles.balance}>
Available Balance: {token.balance} {token.symbol}
</Text>
{/* Recipient Address */}
<Text style={styles.label}>Recipient Address</Text>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
placeholder="Enter wallet address"
placeholderTextColor={Colors.textLight}
value={recipientAddress}
onChangeText={setRecipientAddress}
autoCapitalize="none"
/>
<TouchableOpacity style={styles.iconButton} onPress={handleQRScan}>
<Ionicons name="qr-code-outline" size={24} color={Colors.primary} />
</TouchableOpacity>
<TouchableOpacity style={styles.iconButton} onPress={handlePaste}>
<Ionicons name="clipboard-outline" size={24} color={Colors.primary} />
</TouchableOpacity>
</View>
{/* Amount */}
<Text style={styles.label}>Amount</Text>
<View style={styles.inputContainer}>
<TextInput
style={[styles.input, { flex: 1 }]}
placeholder="0.00"
placeholderTextColor={Colors.textLight}
value={amount}
onChangeText={setAmount}
keyboardType="decimal-pad"
/>
<TouchableOpacity style={styles.maxButton} onPress={handleMaxAmount}>
<Text style={styles.maxButtonText}>MAX</Text>
</TouchableOpacity>
</View>
<Text style={styles.usdValue}>USD Value: ${usdValue.toFixed(2)}</Text>
{/* Network */}
<View style={styles.networkRow}>
<Text style={styles.networkLabel}>Network:</Text>
<TouchableOpacity style={styles.networkSelector}>
<Text style={styles.networkText}>{network}</Text>
<Ionicons name="chevron-down" size={20} color={Colors.textDark} />
</TouchableOpacity>
</View>
{/* Transaction Fee */}
<View style={styles.feeRow}>
<Text style={styles.feeText}>Fee: 0.001 HEZ (~$0.01)</Text>
</View>
{/* Send Button */}
<TouchableOpacity
style={[styles.sendButton, { backgroundColor: token.color }]}
onPress={handleSend}
>
<Text style={styles.sendButtonText}>Send {token.symbol}</Text>
<Ionicons name="arrow-forward" size={20} color="#FFFFFF" />
</TouchableOpacity>
</View>
</View>
</Modal>
);
}
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
padding: Spacing.lg,
},
modalContainer: {
width: '100%',
maxWidth: 400,
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.xl,
padding: Spacing.xl,
...Shadow.medium,
},
closeButton: {
position: 'absolute',
top: Spacing.md,
left: Spacing.md,
zIndex: 1,
},
tokenIcon: {
width: 60,
height: 60,
alignSelf: 'center',
marginTop: Spacing.md,
},
title: {
fontSize: Typography.sizes.xxl,
fontWeight: Typography.weights.bold,
color: Colors.textDark,
textAlign: 'center',
marginTop: Spacing.sm,
},
balance: {
fontSize: Typography.sizes.md,
color: Colors.textLight,
textAlign: 'center',
marginTop: Spacing.xs,
marginBottom: Spacing.lg,
},
label: {
fontSize: Typography.sizes.md,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
marginBottom: Spacing.xs,
marginTop: Spacing.md,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: Colors.background,
borderRadius: BorderRadius.md,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
},
input: {
flex: 1,
fontSize: Typography.sizes.md,
color: Colors.textDark,
paddingVertical: Spacing.xs,
},
iconButton: {
marginLeft: Spacing.sm,
},
maxButton: {
backgroundColor: Colors.primary,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.xs,
borderRadius: BorderRadius.sm,
},
maxButtonText: {
fontSize: Typography.sizes.sm,
fontWeight: Typography.weights.bold,
color: '#FFFFFF',
},
usdValue: {
fontSize: Typography.sizes.sm,
color: Colors.textLight,
marginTop: Spacing.xs,
},
networkRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: Spacing.lg,
},
networkLabel: {
fontSize: Typography.sizes.md,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
},
networkSelector: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: Colors.background,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
borderRadius: BorderRadius.sm,
},
networkText: {
fontSize: Typography.sizes.md,
color: Colors.textDark,
marginRight: Spacing.xs,
},
feeRow: {
marginTop: Spacing.md,
},
feeText: {
fontSize: Typography.sizes.sm,
color: Colors.textLight,
},
sendButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: Spacing.md,
borderRadius: BorderRadius.lg,
marginTop: Spacing.xl,
...Shadow.small,
},
sendButtonText: {
fontSize: Typography.sizes.lg,
fontWeight: Typography.weights.bold,
color: '#FFFFFF',
marginRight: Spacing.sm,
},
});
+92
View File
@@ -0,0 +1,92 @@
/**
* PezkuwiChain Blockchain Configuration
*/
import { ChainConfig } from '../types';
export const CHAIN_CONFIGS: Record<'testnet' | 'mainnet', ChainConfig> = {
testnet: {
name: 'PezkuwiChain Testnet',
rpcUrl: 'wss://testnet-rpc.pezkuwichain.io',
chainId: 'pezkuwi-testnet',
genesisHash: '0x0000000000000000000000000000000000000000000000000000000000000000', // Will be updated
decimals: {
hez: 12,
pez: 12,
},
},
mainnet: {
name: 'PezkuwiChain Mainnet',
rpcUrl: 'wss://mainnet-rpc.pezkuwichain.io',
chainId: 'pezkuwi-mainnet',
genesisHash: '0x0000000000000000000000000000000000000000000000000000000000000000', // Will be updated
decimals: {
hez: 12,
pez: 12,
},
},
};
// Default to testnet for development
export const DEFAULT_CHAIN: 'testnet' | 'mainnet' = 'testnet';
export const CURRENT_CHAIN_CONFIG = CHAIN_CONFIGS[DEFAULT_CHAIN];
// Pallet names
export const PALLETS = {
BALANCES: 'balances',
ASSETS: 'assets',
STAKING: 'staking',
WELATI: 'welati',
TRUST: 'trust',
PEZ_REWARDS: 'pezRewards',
PEZ_TREASURY: 'pezTreasury',
IDENTITY_KYC: 'identityKyc',
PERWERDE: 'perwerde',
REFERRAL: 'referral',
VALIDATOR_POOL: 'validatorPool',
STAKING_SCORE: 'stakingScore',
};
// Asset IDs
export const ASSET_IDS = {
PEZ: 1, // PEZ token asset ID
};
// Constants from blockchain
export const BLOCKCHAIN_CONSTANTS = {
// PEZ Token
PEZ_TOTAL_SUPPLY: '5000000000000000000000', // 5 billion with 12 decimals
PEZ_HALVING_PERIOD: 48, // months
PEZ_EPOCH_BLOCKS: 432000, // ~30 days
PEZ_CLAIM_PERIOD: 100800, // ~1 week
// Parliamentary NFT
PARLIAMENTARY_NFT_COUNT: 201,
PARLIAMENTARY_NFT_COLLECTION_ID: 100,
PARLIAMENTARY_NFT_INCENTIVE_PERCENT: 10, // 10% of rewards pool
// Trust Score
TRUST_SCORE_MIN: 0,
TRUST_SCORE_MAX: 1000,
// Staking
MIN_STAKE_AMOUNT: '1000000000000', // 1 HEZ with 12 decimals
UNBONDING_PERIOD: 28, // days
// Governance
PROPOSAL_DEPOSIT: '100000000000000', // 100 PEZ with 12 decimals
VOTING_PERIOD: 7, // days
};
// Block time (approximate)
export const BLOCK_TIME = 6; // seconds
// Transaction fees (approximate)
export const TX_FEES = {
TRANSFER: '0.01', // HEZ
STAKE: '0.02',
VOTE: '0.01',
PROPOSAL: '0.05',
};
+57
View File
@@ -0,0 +1,57 @@
/**
* PezkuwiChain Mobile App - Color Palette
* Elegant, soft Kurdish-inspired colors
*/
export const Colors = {
// Primary Kurdish Colors (Soft)
coral: '#F08080', // Soft red - Send, Home
mint: '#98D8C8', // Soft green - Governance, Vote
gold: '#E8C896', // Soft gold - Trust score, Parliamentary
peach: '#F5B895', // Soft orange - Update, Create buttons
teal: '#7DD3C0', // Soft teal - Identity, Receive
lavender: '#C8B6D6', // Soft purple - Education, Stake
navy: '#5D7A8C', // Soft navy - Business
// Supporting Colors
blue: '#89CFF0', // Soft blue - Wallet, Receive
cyan: '#7DD3C0', // Soft cyan - Exchange
emerald: '#7FD8BE', // Soft emerald - Trust
violet: '#B19CD9', // Soft violet - Rewards
pink: '#FFB6C1', // Soft pink - Certificates
// Backgrounds
background: '#FAFAFA', // Off-white main background
card: '#FFFFFF', // White cards
header: '#F5F5F5', // Light gray header
// Text Colors
textDark: '#2C3E50', // Primary text
textGray: '#7F8C8D', // Secondary text
textLight: '#BDC3C7', // Tertiary text
// Functional Colors
success: '#2ECC71', // Green for success
warning: '#F39C12', // Orange for warnings
error: '#E74C3C', // Red for errors
info: '#3498DB', // Blue for info
// Gradients (start and end colors)
gradients: {
header: ['#F08080', '#F5B895'], // Coral to peach
hezCard: ['#F08080', '#F5B895'], // Red to orange
pezCard: ['#98D8C8', '#7DD3C0'], // Green to teal
governance: ['#98D8C8', '#7DD3C0'], // Mint gradient
education: ['#C8B6D6', '#B19CD9'], // Lavender gradient
business: ['#5D7A8C', '#7F8C8D'], // Navy gradient
exchange: ['#7DD3C0', '#89CFF0'], // Cyan gradient
referral: ['#F5B895', '#E8C896'], // Peach to gold
identity: ['#7DD3C0', '#98D8C8'], // Teal to mint
},
// Shadow
shadow: 'rgba(0, 0, 0, 0.08)',
};
export default Colors;
+87
View File
@@ -0,0 +1,87 @@
/**
* PezkuwiChain Mobile App - Theme Constants
*/
export const Typography = {
// Font Sizes
sizes: {
tiny: 10,
small: 12,
body: 14,
medium: 16,
large: 18,
title: 20,
heading: 24,
display: 32,
hero: 40,
},
// Font Weights
weights: {
regular: '400' as const,
medium: '500' as const,
semibold: '600' as const,
bold: '700' as const,
},
// Line Heights
lineHeights: {
tight: 1.2,
normal: 1.5,
relaxed: 1.8,
},
};
export const Spacing = {
xs: 4,
sm: 8,
md: 12,
lg: 16,
xl: 20,
xxl: 24,
xxxl: 32,
};
export const BorderRadius = {
small: 8,
medium: 12,
large: 16,
xlarge: 20,
xxlarge: 24,
round: 9999,
};
export const Shadow = {
soft: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 8,
elevation: 3,
},
medium: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 12,
elevation: 5,
},
};
export const IconSizes = {
tiny: 16,
small: 20,
medium: 24,
large: 32,
xlarge: 40,
xxlarge: 60,
};
export default {
Typography,
Spacing,
BorderRadius,
Shadow,
IconSizes,
};
@@ -0,0 +1,63 @@
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../constants/colors';
// Screens
import HomeScreen from '../screens/Home/HomeScreen';
import WalletScreen from '../screens/Wallet/WalletScreen';
import GovernanceScreen from '../screens/Governance/GovernanceScreen';
import ReferralScreen from '../screens/Referral/ReferralScreen';
import ProfileScreen from '../screens/Profile/ProfileScreen';
const Tab = createBottomTabNavigator();
export default function BottomTabNavigator() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName: keyof typeof Ionicons.glyphMap;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Wallet') {
iconName = focused ? 'wallet' : 'wallet-outline';
} else if (route.name === 'Governance') {
iconName = focused ? 'business' : 'business-outline';
} else if (route.name === 'Referral') {
iconName = focused ? 'gift' : 'gift-outline';
} else if (route.name === 'Profile') {
iconName = focused ? 'person' : 'person-outline';
} else {
iconName = 'help-outline';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: Colors.coral,
tabBarInactiveTintColor: Colors.textGray,
tabBarStyle: {
backgroundColor: '#FFFFFF',
borderTopWidth: 1,
borderTopColor: '#F0F0F0',
paddingBottom: 8,
paddingTop: 8,
height: 60,
},
tabBarLabelStyle: {
fontSize: 12,
fontWeight: '500',
},
headerShown: false,
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Wallet" component={WalletScreen} />
<Tab.Screen name="Governance" component={GovernanceScreen} />
<Tab.Screen name="Referral" component={ReferralScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
+54
View File
@@ -0,0 +1,54 @@
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
// Auth Screens
import LanguageSelectionScreen from '../screens/Auth/LanguageSelectionScreen';
import SignUpScreen from '../screens/Auth/SignUpScreen';
// Main App
import BottomTabNavigator from './BottomTabNavigator';
// Identity & KYC Screens
import IdentityKYCFormScreen from '../screens/Identity/IdentityKYCFormScreen';
import CitizenCardScreen from '../screens/Identity/CitizenCardScreen';
// Additional Screens
import EducationScreen from '../screens/Education/EducationScreen';
import BusinessScreen from '../screens/Business/BusinessScreen';
import ExchangeScreen from '../screens/Exchange/ExchangeScreen';
import ReferralScreen from '../screens/Referral/ReferralScreen';
const Stack = createNativeStackNavigator();
export default function RootNavigator() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="LanguageSelection"
screenOptions={{
headerShown: false,
}}
>
{/* Auth Flow */}
<Stack.Screen name="LanguageSelection" component={LanguageSelectionScreen} />
<Stack.Screen name="SignUp" component={SignUpScreen} />
{/* Main App */}
<Stack.Screen name="MainTabs" component={BottomTabNavigator} />
{/* Identity & KYC */}
<Stack.Screen name="IdentityKYCForm" component={IdentityKYCFormScreen} />
<Stack.Screen name="CitizenCard" component={CitizenCardScreen} />
{/* Additional Screens */}
<Stack.Screen name="Education" component={EducationScreen} />
<Stack.Screen name="Business" component={BusinessScreen} />
<Stack.Screen name="Exchange" component={ExchangeScreen} />
<Stack.Screen name="Referral" component={ReferralScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// Add Ministries screen to imports and stack
@@ -0,0 +1,199 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
StatusBar,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius } from '../../constants/theme';
interface Language {
code: string;
name: string;
nativeName: string;
}
const LANGUAGES: Language[] = [
{ code: 'en', name: 'English', nativeName: 'English' },
{ code: 'ku', name: 'Kurdish (Kurmanji)', nativeName: 'Kurdî (Kurmancî)' },
{ code: 'ckb', name: 'Kurdish (Sorani)', nativeName: 'کوردی (سۆرانی)' },
{ code: 'tr', name: 'Turkish', nativeName: 'Türkçe' },
{ code: 'ar', name: 'Arabic', nativeName: 'العربية' },
{ code: 'fa', name: 'Persian', nativeName: 'فارسی' },
];
export default function LanguageSelectionScreen({ navigation }: any) {
const [selectedLanguage, setSelectedLanguage] = useState<string>('en');
const handleContinue = () => {
// Save language preference
// TODO: Implement i18n language switching
navigation.navigate('SignUp');
};
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="light-content" />
<LinearGradient
colors={['#F08080', '#F5B895', '#E8C896']}
style={styles.gradient}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
>
<View style={styles.content}>
{/* Header */}
<View style={styles.header}>
<Text style={styles.title}>Welcome to</Text>
<Text style={styles.brandName}>PezkuwiChain</Text>
<Text style={styles.subtitle}>Your Digital Citizenship Platform</Text>
<Text style={styles.description}>
Building the future of Kurdish digital infrastructure
</Text>
</View>
{/* Language Selection */}
<View style={styles.languageSection}>
<Text style={styles.sectionTitle}>Select Your Language</Text>
<View style={styles.languageGrid}>
{LANGUAGES.map((language) => (
<TouchableOpacity
key={language.code}
style={[
styles.languageButton,
selectedLanguage === language.code && styles.languageButtonSelected,
]}
onPress={() => setSelectedLanguage(language.code)}
activeOpacity={0.7}
>
<Text
style={[
styles.languageText,
selectedLanguage === language.code && styles.languageTextSelected,
]}
>
{language.nativeName}
</Text>
</TouchableOpacity>
))}
</View>
</View>
{/* Continue Button */}
<TouchableOpacity
style={styles.continueButton}
onPress={handleContinue}
activeOpacity={0.8}
>
<Text style={styles.continueButtonText}>Get Started </Text>
</TouchableOpacity>
</View>
</LinearGradient>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.coral,
},
gradient: {
flex: 1,
},
content: {
flex: 1,
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.xxxl,
justifyContent: 'space-between',
},
header: {
alignItems: 'center',
marginTop: Spacing.xxxl,
},
title: {
fontSize: Typography.sizes.heading,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
marginBottom: Spacing.sm,
},
brandName: {
fontSize: Typography.sizes.display,
fontWeight: Typography.weights.bold,
color: '#FFFFFF',
marginBottom: Spacing.md,
},
subtitle: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.medium,
color: '#FFFFFF',
marginBottom: Spacing.sm,
},
description: {
fontSize: Typography.sizes.body,
color: 'rgba(255, 255, 255, 0.9)',
textAlign: 'center',
},
languageSection: {
flex: 1,
justifyContent: 'center',
},
sectionTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
textAlign: 'center',
marginBottom: Spacing.xl,
},
languageGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
gap: Spacing.md,
},
languageButton: {
width: '48%',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
borderRadius: BorderRadius.large,
paddingVertical: Spacing.lg,
paddingHorizontal: Spacing.md,
borderWidth: 2,
borderColor: 'transparent',
},
languageButtonSelected: {
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#FFFFFF',
},
languageText: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.medium,
color: '#FFFFFF',
textAlign: 'center',
},
languageTextSelected: {
color: Colors.coral,
fontWeight: Typography.weights.semibold,
},
continueButton: {
backgroundColor: 'rgba(212, 160, 23, 0.9)',
borderRadius: BorderRadius.xxlarge,
paddingVertical: Spacing.lg,
paddingHorizontal: Spacing.xxxl,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.2,
shadowRadius: 8,
elevation: 5,
},
continueButtonText: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
},
});
+326
View File
@@ -0,0 +1,326 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
SafeAreaView,
KeyboardAvoidingView,
Platform,
ScrollView,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
export default function SignUpScreen({ navigation }: any) {
const [isSignUp, setIsSignUp] = useState(true);
const [formData, setFormData] = useState({
fullName: '',
email: '',
password: '',
confirmPassword: '',
referralCode: '',
});
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const handleSubmit = () => {
// TODO: Implement authentication logic
navigation.navigate('MainTabs');
};
const toggleMode = () => {
setIsSignUp(!isSignUp);
};
return (
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.keyboardView}
>
<ScrollView
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* Header */}
<View style={styles.header}>
<Text style={styles.logo}>PezkuwiChain</Text>
<Text style={styles.subtitle}>Access your governance account</Text>
</View>
{/* Toggle Buttons */}
<View style={styles.toggleContainer}>
<TouchableOpacity
style={[styles.toggleButton, !isSignUp && styles.toggleButtonActive]}
onPress={() => setIsSignUp(false)}
>
<Text style={[styles.toggleText, !isSignUp && styles.toggleTextActive]}>
Sign In
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.toggleButton, isSignUp && styles.toggleButtonActive]}
onPress={() => setIsSignUp(true)}
>
<Text style={[styles.toggleText, isSignUp && styles.toggleTextActive]}>
Sign Up
</Text>
</TouchableOpacity>
</View>
{/* Form */}
<View style={styles.form}>
{isSignUp && (
<View style={styles.inputContainer}>
<Text style={styles.label}>Full Name</Text>
<View style={styles.inputWrapper}>
<Ionicons name="person-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="John Doe"
placeholderTextColor={Colors.textLight}
value={formData.fullName}
onChangeText={(text) => setFormData({ ...formData, fullName: text })}
/>
</View>
</View>
)}
<View style={styles.inputContainer}>
<Text style={styles.label}>Email</Text>
<View style={styles.inputWrapper}>
<Ionicons name="mail-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="name@example.com"
placeholderTextColor={Colors.textLight}
keyboardType="email-address"
autoCapitalize="none"
value={formData.email}
onChangeText={(text) => setFormData({ ...formData, email: text })}
/>
</View>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Password</Text>
<View style={styles.inputWrapper}>
<Ionicons name="lock-closed-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="••••••••"
placeholderTextColor={Colors.textLight}
secureTextEntry={!showPassword}
value={formData.password}
onChangeText={(text) => setFormData({ ...formData, password: text })}
/>
<TouchableOpacity onPress={() => setShowPassword(!showPassword)}>
<Ionicons
name={showPassword ? 'eye-outline' : 'eye-off-outline'}
size={20}
color={Colors.textGray}
/>
</TouchableOpacity>
</View>
</View>
{isSignUp && (
<>
<View style={styles.inputContainer}>
<Text style={styles.label}>Confirm Password</Text>
<View style={styles.inputWrapper}>
<Ionicons name="lock-closed-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="••••••••"
placeholderTextColor={Colors.textLight}
secureTextEntry={!showConfirmPassword}
value={formData.confirmPassword}
onChangeText={(text) =>
setFormData({ ...formData, confirmPassword: text })
}
/>
<TouchableOpacity
onPress={() => setShowConfirmPassword(!showConfirmPassword)}
>
<Ionicons
name={showConfirmPassword ? 'eye-outline' : 'eye-off-outline'}
size={20}
color={Colors.textGray}
/>
</TouchableOpacity>
</View>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Referral Code (Optional)</Text>
<View style={styles.inputWrapper}>
<Ionicons name="gift-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="Enter referral code"
placeholderTextColor={Colors.textLight}
autoCapitalize="characters"
value={formData.referralCode}
onChangeText={(text) =>
setFormData({ ...formData, referralCode: text })
}
/>
</View>
</View>
</>
)}
{!isSignUp && (
<TouchableOpacity style={styles.forgotPassword}>
<Text style={styles.forgotPasswordText}>Forgot Password?</Text>
</TouchableOpacity>
)}
{/* Submit Button */}
<TouchableOpacity style={styles.submitButton} onPress={handleSubmit}>
<Text style={styles.submitButtonText}>
{isSignUp ? 'Create Account' : 'Sign In'}
</Text>
</TouchableOpacity>
{/* Footer */}
<View style={styles.footer}>
<Text style={styles.footerText}>
{isSignUp ? 'Already have an account?' : "Don't have an account?"}
</Text>
<TouchableOpacity onPress={toggleMode}>
<Text style={styles.footerLink}>
{isSignUp ? 'Sign In' : 'Sign Up'}
</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.navy,
},
keyboardView: {
flex: 1,
},
scrollContent: {
flexGrow: 1,
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.xxxl,
},
header: {
alignItems: 'center',
marginBottom: Spacing.xxxl,
},
logo: {
fontSize: Typography.sizes.display,
fontWeight: Typography.weights.bold,
color: Colors.mint,
marginBottom: Spacing.sm,
},
subtitle: {
fontSize: Typography.sizes.medium,
color: Colors.textLight,
},
toggleContainer: {
flexDirection: 'row',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: BorderRadius.large,
padding: Spacing.xs,
marginBottom: Spacing.xl,
},
toggleButton: {
flex: 1,
paddingVertical: Spacing.md,
alignItems: 'center',
borderRadius: BorderRadius.medium,
},
toggleButtonActive: {
backgroundColor: Colors.mint,
},
toggleText: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.medium,
color: Colors.textLight,
},
toggleTextActive: {
color: Colors.navy,
fontWeight: Typography.weights.semibold,
},
form: {
flex: 1,
},
inputContainer: {
marginBottom: Spacing.lg,
},
label: {
fontSize: Typography.sizes.body,
fontWeight: Typography.weights.medium,
color: '#FFFFFF',
marginBottom: Spacing.sm,
},
inputWrapper: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: BorderRadius.medium,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.2)',
},
input: {
flex: 1,
fontSize: Typography.sizes.medium,
color: '#FFFFFF',
marginLeft: Spacing.sm,
},
forgotPassword: {
alignSelf: 'flex-end',
marginBottom: Spacing.lg,
},
forgotPasswordText: {
fontSize: Typography.sizes.small,
color: Colors.mint,
},
submitButton: {
backgroundColor: Colors.peach,
borderRadius: BorderRadius.xxlarge,
paddingVertical: Spacing.lg,
alignItems: 'center',
marginTop: Spacing.lg,
...Shadow.soft,
},
submitButtonText: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
},
footer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginTop: Spacing.xl,
gap: Spacing.xs,
},
footerText: {
fontSize: Typography.sizes.body,
color: Colors.textLight,
},
footerLink: {
fontSize: Typography.sizes.body,
fontWeight: Typography.weights.semibold,
color: Colors.mint,
},
});
@@ -0,0 +1,794 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
SafeAreaView,
ScrollView,
TouchableOpacity,
TextInput,
Modal,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import QRCode from 'react-native-qrcode-svg';
import * as Clipboard from 'expo-clipboard';
import Colors from '../../constants/colors';
interface BusinessStats {
totalSales: number;
todaySales: number;
pendingPayments: number;
totalCustomers: number;
}
interface Transaction {
id: string;
customer: string;
amount: number;
token: 'HEZ' | 'PEZ';
date: string;
status: 'completed' | 'pending' | 'failed';
invoiceId: string;
}
export default function BusinessScreen({ navigation }: any) {
const [merchantAddress] = useState('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
const [showPaymentModal, setShowPaymentModal] = useState(false);
const [paymentAmount, setPaymentAmount] = useState('');
const [selectedToken, setSelectedToken] = useState<'HEZ' | 'PEZ'>('PEZ');
const [paymentNote, setPaymentNote] = useState('');
const [stats] = useState<BusinessStats>({
totalSales: 125450,
todaySales: 3250,
pendingPayments: 850,
totalCustomers: 342,
});
const [transactions] = useState<Transaction[]>([
{
id: '1',
customer: 'Ahmed Karwan',
amount: 500,
token: 'PEZ',
date: '2024-01-23 14:30',
status: 'completed',
invoiceId: 'INV-2024-001',
},
{
id: '2',
customer: 'Layla Sherzad',
amount: 1250,
token: 'HEZ',
date: '2024-01-23 12:15',
status: 'completed',
invoiceId: 'INV-2024-002',
},
{
id: '3',
customer: 'Saman Aziz',
amount: 750,
token: 'PEZ',
date: '2024-01-23 10:45',
status: 'pending',
invoiceId: 'INV-2024-003',
},
{
id: '4',
customer: 'Hana Dilshad',
amount: 350,
token: 'PEZ',
date: '2024-01-22 16:20',
status: 'completed',
invoiceId: 'INV-2024-004',
},
]);
const generatePaymentQR = () => {
if (!paymentAmount || parseFloat(paymentAmount) <= 0) {
Alert.alert('Invalid Amount', 'Please enter a valid payment amount');
return;
}
setShowPaymentModal(true);
};
const copyMerchantAddress = async () => {
await Clipboard.setStringAsync(merchantAddress);
Alert.alert('✓ Copied!', 'Merchant address copied to clipboard');
};
const getStatusColor = (status: string) => {
switch (status) {
case 'completed':
return Colors.success;
case 'pending':
return Colors.warning;
case 'failed':
return Colors.error;
default:
return Colors.textGray;
}
};
const paymentData = JSON.stringify({
merchant: merchantAddress,
amount: paymentAmount,
token: selectedToken,
note: paymentNote,
timestamp: Date.now(),
});
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* Header */}
<LinearGradient
colors={[Colors.kurdishGold, Colors.coral]}
style={styles.header}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.headerContent}>
<View>
<Text style={styles.headerTitle}>Business Hub</Text>
<Text style={styles.headerSubtitle}>Merchant Payment Center</Text>
</View>
<TouchableOpacity
style={styles.settingsButton}
onPress={() => Alert.alert('Settings', 'Business settings coming soon')}
>
<Ionicons name="settings-outline" size={24} color="white" />
</TouchableOpacity>
</View>
</LinearGradient>
{/* Statistics Grid */}
<View style={styles.statsContainer}>
<View style={styles.statsGrid}>
<View style={styles.statCard}>
<Ionicons name="cash" size={28} color={Colors.success} />
<Text style={styles.statValue}>{stats.totalSales.toLocaleString()}</Text>
<Text style={styles.statLabel}>Total Sales (PEZ)</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="trending-up" size={28} color={Colors.primary} />
<Text style={styles.statValue}>{stats.todaySales.toLocaleString()}</Text>
<Text style={styles.statLabel}>Today's Sales</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="time" size={28} color={Colors.warning} />
<Text style={styles.statValue}>{stats.pendingPayments.toLocaleString()}</Text>
<Text style={styles.statLabel}>Pending</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="people" size={28} color={Colors.kurdishGold} />
<Text style={styles.statValue}>{stats.totalCustomers}</Text>
<Text style={styles.statLabel}>Customers</Text>
</View>
</View>
</View>
{/* Quick Actions */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Quick Actions</Text>
<View style={styles.actionsGrid}>
<TouchableOpacity style={styles.actionCard} activeOpacity={0.7}>
<View style={[styles.actionIcon, { backgroundColor: Colors.primary + '20' }]}>
<Ionicons name="qr-code" size={32} color={Colors.primary} />
</View>
<Text style={styles.actionLabel}>Generate QR</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionCard} activeOpacity={0.7}>
<View style={[styles.actionIcon, { backgroundColor: Colors.success + '20' }]}>
<Ionicons name="document-text" size={32} color={Colors.success} />
</View>
<Text style={styles.actionLabel}>Create Invoice</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionCard} activeOpacity={0.7}>
<View style={[styles.actionIcon, { backgroundColor: Colors.kurdishGold + '20' }]}>
<Ionicons name="stats-chart" size={32} color={Colors.kurdishGold} />
</View>
<Text style={styles.actionLabel}>Analytics</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionCard} activeOpacity={0.7}>
<View style={[styles.actionIcon, { backgroundColor: Colors.coral + '20' }]}>
<Ionicons name="card" size={32} color={Colors.coral} />
</View>
<Text style={styles.actionLabel}>Payment Link</Text>
</TouchableOpacity>
</View>
</View>
{/* Payment Request Form */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Create Payment Request</Text>
<View style={styles.formCard}>
<View style={styles.inputGroup}>
<Text style={styles.inputLabel}>Amount</Text>
<TextInput
style={styles.input}
placeholder="Enter amount"
keyboardType="decimal-pad"
value={paymentAmount}
onChangeText={setPaymentAmount}
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.inputLabel}>Token</Text>
<View style={styles.tokenSelector}>
<TouchableOpacity
style={[
styles.tokenOption,
selectedToken === 'PEZ' && styles.tokenOptionActive,
]}
onPress={() => setSelectedToken('PEZ')}
>
<Text
style={[
styles.tokenOptionText,
selectedToken === 'PEZ' && styles.tokenOptionTextActive,
]}
>
PEZ
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.tokenOption,
selectedToken === 'HEZ' && styles.tokenOptionActive,
]}
onPress={() => setSelectedToken('HEZ')}
>
<Text
style={[
styles.tokenOptionText,
selectedToken === 'HEZ' && styles.tokenOptionTextActive,
]}
>
HEZ
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.inputLabel}>Note (Optional)</Text>
<TextInput
style={[styles.input, styles.textArea]}
placeholder="Payment description"
multiline
numberOfLines={3}
value={paymentNote}
onChangeText={setPaymentNote}
/>
</View>
<TouchableOpacity
style={styles.generateButton}
onPress={generatePaymentQR}
activeOpacity={0.8}
>
<LinearGradient
colors={[Colors.primary, Colors.success]}
style={styles.generateButtonGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<Ionicons name="qr-code" size={20} color="white" />
<Text style={styles.generateButtonText}>Generate Payment QR</Text>
</LinearGradient>
</TouchableOpacity>
</View>
</View>
{/* Recent Transactions */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Recent Transactions</Text>
<TouchableOpacity>
<Text style={styles.viewAllText}>View All</Text>
</TouchableOpacity>
</View>
{transactions.map((transaction) => (
<View key={transaction.id} style={styles.transactionCard}>
<View style={styles.transactionHeader}>
<View style={styles.transactionIcon}>
<Ionicons
name={
transaction.status === 'completed'
? 'checkmark-circle'
: transaction.status === 'pending'
? 'time'
: 'close-circle'
}
size={24}
color={getStatusColor(transaction.status)}
/>
</View>
<View style={styles.transactionInfo}>
<Text style={styles.transactionCustomer}>
{transaction.customer}
</Text>
<Text style={styles.transactionDate}>{transaction.date}</Text>
<Text style={styles.transactionInvoice}>
{transaction.invoiceId}
</Text>
</View>
<View style={styles.transactionAmount}>
<Text style={styles.amountValue}>
{transaction.amount.toLocaleString()}
</Text>
<Text style={styles.amountToken}>{transaction.token}</Text>
</View>
</View>
<View
style={[
styles.statusBadge,
{ backgroundColor: getStatusColor(transaction.status) + '20' },
]}
>
<Text
style={[
styles.statusText,
{ color: getStatusColor(transaction.status) },
]}
>
{transaction.status.charAt(0).toUpperCase() +
transaction.status.slice(1)}
</Text>
</View>
</View>
))}
</View>
{/* Merchant Info */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Merchant Information</Text>
<View style={styles.merchantCard}>
<View style={styles.merchantHeader}>
<Ionicons name="storefront" size={24} color={Colors.primary} />
<Text style={styles.merchantTitle}>Your Merchant Address</Text>
</View>
<TouchableOpacity
style={styles.addressBox}
onPress={copyMerchantAddress}
activeOpacity={0.7}
>
<Text style={styles.addressText} numberOfLines={1}>
{merchantAddress}
</Text>
<Ionicons name="copy-outline" size={20} color={Colors.primary} />
</TouchableOpacity>
<Text style={styles.merchantNote}>
Share this address with customers to receive payments
</Text>
</View>
</View>
<View style={{ height: 40 }} />
</ScrollView>
{/* Payment QR Modal */}
<Modal
visible={showPaymentModal}
transparent
animationType="fade"
onRequestClose={() => setShowPaymentModal(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<Text style={styles.modalTitle}>Payment QR Code</Text>
<TouchableOpacity onPress={() => setShowPaymentModal(false)}>
<Ionicons name="close" size={28} color={Colors.textDark} />
</TouchableOpacity>
</View>
<View style={styles.qrContainer}>
<QRCode value={paymentData} size={200} />
</View>
<View style={styles.paymentDetails}>
<View style={styles.paymentDetailRow}>
<Text style={styles.paymentDetailLabel}>Amount:</Text>
<Text style={styles.paymentDetailValue}>
{paymentAmount} {selectedToken}
</Text>
</View>
{paymentNote ? (
<View style={styles.paymentDetailRow}>
<Text style={styles.paymentDetailLabel}>Note:</Text>
<Text style={styles.paymentDetailValue}>{paymentNote}</Text>
</View>
) : null}
</View>
<Text style={styles.qrInstruction}>
Ask customer to scan this QR code with their PezkuwiChain app
</Text>
<TouchableOpacity
style={styles.doneButton}
onPress={() => setShowPaymentModal(false)}
>
<Text style={styles.doneButtonText}>Done</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
paddingHorizontal: 20,
paddingTop: 20,
paddingBottom: 24,
borderBottomLeftRadius: 24,
borderBottomRightRadius: 24,
},
headerContent: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
headerTitle: {
fontSize: 32,
fontWeight: '700',
color: 'white',
marginBottom: 4,
},
headerSubtitle: {
fontSize: 16,
color: 'rgba(255, 255, 255, 0.9)',
},
settingsButton: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: 'rgba(255, 255, 255, 0.2)',
alignItems: 'center',
justifyContent: 'center',
},
statsContainer: {
paddingHorizontal: 20,
marginTop: -30,
marginBottom: 20,
},
statsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
},
statCard: {
flex: 1,
minWidth: '47%',
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
},
statValue: {
fontSize: 24,
fontWeight: '700',
color: Colors.textDark,
marginTop: 8,
marginBottom: 4,
},
statLabel: {
fontSize: 13,
color: Colors.textGray,
textAlign: 'center',
},
section: {
paddingHorizontal: 20,
marginBottom: 24,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
sectionTitle: {
fontSize: 20,
fontWeight: '700',
color: Colors.textDark,
marginBottom: 12,
},
viewAllText: {
fontSize: 14,
fontWeight: '600',
color: Colors.primary,
},
actionsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
},
actionCard: {
flex: 1,
minWidth: '47%',
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
actionIcon: {
width: 64,
height: 64,
borderRadius: 16,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 12,
},
actionLabel: {
fontSize: 14,
fontWeight: '600',
color: Colors.textDark,
textAlign: 'center',
},
formCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 20,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
inputGroup: {
marginBottom: 16,
},
inputLabel: {
fontSize: 14,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 8,
},
input: {
backgroundColor: Colors.background,
borderRadius: 10,
paddingHorizontal: 16,
paddingVertical: 12,
fontSize: 15,
color: Colors.textDark,
},
textArea: {
height: 80,
textAlignVertical: 'top',
},
tokenSelector: {
flexDirection: 'row',
gap: 12,
},
tokenOption: {
flex: 1,
paddingVertical: 12,
borderRadius: 10,
backgroundColor: Colors.background,
alignItems: 'center',
},
tokenOptionActive: {
backgroundColor: Colors.primary,
},
tokenOptionText: {
fontSize: 15,
fontWeight: '600',
color: Colors.textGray,
},
tokenOptionTextActive: {
color: 'white',
},
generateButton: {
borderRadius: 10,
overflow: 'hidden',
marginTop: 8,
},
generateButtonGradient: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 14,
gap: 8,
},
generateButtonText: {
fontSize: 16,
fontWeight: '600',
color: 'white',
},
transactionCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
transactionHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
transactionIcon: {
marginRight: 12,
},
transactionInfo: {
flex: 1,
},
transactionCustomer: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 2,
},
transactionDate: {
fontSize: 12,
color: Colors.textGray,
marginBottom: 2,
},
transactionInvoice: {
fontSize: 11,
color: Colors.textGray,
fontFamily: 'monospace',
},
transactionAmount: {
alignItems: 'flex-end',
},
amountValue: {
fontSize: 18,
fontWeight: '700',
color: Colors.textDark,
},
amountToken: {
fontSize: 12,
color: Colors.textGray,
marginTop: 2,
},
statusBadge: {
alignSelf: 'flex-start',
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 8,
},
statusText: {
fontSize: 12,
fontWeight: '600',
},
merchantCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 20,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
merchantHeader: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
marginBottom: 12,
},
merchantTitle: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
},
addressBox: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: Colors.background,
borderRadius: 10,
padding: 12,
marginBottom: 8,
},
addressText: {
flex: 1,
fontSize: 13,
color: Colors.textDark,
fontFamily: 'monospace',
marginRight: 8,
},
merchantNote: {
fontSize: 12,
color: Colors.textGray,
lineHeight: 18,
},
modalOverlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
modalContent: {
backgroundColor: 'white',
borderRadius: 20,
padding: 24,
width: '100%',
maxWidth: 400,
},
modalHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 24,
},
modalTitle: {
fontSize: 22,
fontWeight: '700',
color: Colors.textDark,
},
qrContainer: {
alignItems: 'center',
padding: 20,
backgroundColor: Colors.background,
borderRadius: 16,
marginBottom: 20,
},
paymentDetails: {
backgroundColor: Colors.background,
borderRadius: 12,
padding: 16,
marginBottom: 16,
gap: 8,
},
paymentDetailRow: {
flexDirection: 'row',
justifyContent: 'space-between',
},
paymentDetailLabel: {
fontSize: 14,
color: Colors.textGray,
},
paymentDetailValue: {
fontSize: 14,
fontWeight: '600',
color: Colors.textDark,
},
qrInstruction: {
fontSize: 13,
color: Colors.textGray,
textAlign: 'center',
marginBottom: 20,
lineHeight: 20,
},
doneButton: {
backgroundColor: Colors.primary,
borderRadius: 10,
paddingVertical: 14,
alignItems: 'center',
},
doneButtonText: {
fontSize: 16,
fontWeight: '600',
color: 'white',
},
});
@@ -0,0 +1,626 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
SafeAreaView,
ScrollView,
TouchableOpacity,
Share,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import Colors from '../../constants/colors';
interface Certificate {
id: string;
title: string;
institution: string;
issueDate: string;
certificateId: string;
type: 'degree' | 'course' | 'skill' | 'achievement';
verified: boolean;
ipfsHash?: string;
}
interface EducationStats {
totalCertificates: number;
verifiedCertificates: number;
skillsAcquired: number;
learningHours: number;
}
export default function EducationScreen({ navigation }: any) {
const [stats] = useState<EducationStats>({
totalCertificates: 8,
verifiedCertificates: 6,
skillsAcquired: 15,
learningHours: 240,
});
const [certificates] = useState<Certificate[]>([
{
id: '1',
title: 'Bachelor of Computer Science',
institution: 'University of Kurdistan',
issueDate: '2022-06-15',
certificateId: 'UOK-CS-2022-1234',
type: 'degree',
verified: true,
ipfsHash: 'QmX7Y8Z9...',
},
{
id: '2',
title: 'Blockchain Development Certificate',
institution: 'Digital Kurdistan Academy',
issueDate: '2023-03-20',
certificateId: 'DKA-BC-2023-5678',
type: 'course',
verified: true,
ipfsHash: 'QmA1B2C3...',
},
{
id: '3',
title: 'Kurdish Language Proficiency',
institution: 'Kurdistan Cultural Institute',
issueDate: '2023-08-10',
certificateId: 'KCI-KL-2023-9012',
type: 'skill',
verified: true,
ipfsHash: 'QmD4E5F6...',
},
{
id: '4',
title: 'Smart Contract Security',
institution: 'PezkuwiChain Foundation',
issueDate: '2024-01-15',
certificateId: 'PKW-SCS-2024-3456',
type: 'course',
verified: false,
},
]);
const getCertificateIcon = (type: string) => {
switch (type) {
case 'degree':
return 'school';
case 'course':
return 'book';
case 'skill':
return 'ribbon';
case 'achievement':
return 'trophy';
default:
return 'document';
}
};
const getCertificateColor = (type: string) => {
switch (type) {
case 'degree':
return Colors.primary;
case 'course':
return Colors.success;
case 'skill':
return Colors.kurdishGold;
case 'achievement':
return Colors.coral;
default:
return Colors.textGray;
}
};
const shareCertificate = async (certificate: Certificate) => {
try {
await Share.share({
message: `🎓 ${certificate.title}\n\nIssued by: ${certificate.institution}\nDate: ${certificate.issueDate}\nCertificate ID: ${certificate.certificateId}\n\nVerified on PezkuwiChain - Digital Kurdistan's Blockchain Platform`,
});
} catch (error) {
console.error('Error sharing:', error);
}
};
const verifyCertificate = (certificate: Certificate) => {
if (certificate.verified) {
Alert.alert(
'✓ Verified Certificate',
`This certificate is verified on PezkuwiChain blockchain.\n\nIPFS Hash: ${certificate.ipfsHash}\nCertificate ID: ${certificate.certificateId}`,
[{ text: 'OK' }]
);
} else {
Alert.alert(
'Pending Verification',
'This certificate is pending blockchain verification. Please check back later.',
[{ text: 'OK' }]
);
}
};
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* Header */}
<LinearGradient
colors={[Colors.primary, Colors.success]}
style={styles.header}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.headerContent}>
<View>
<Text style={styles.headerTitle}>Perwerde</Text>
<Text style={styles.headerSubtitle}>Education & Certificates</Text>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={() =>
Alert.alert(
'Add Certificate',
'Request your institution to issue a certificate on PezkuwiChain',
[{ text: 'OK' }]
)
}
>
<Ionicons name="add" size={24} color="white" />
</TouchableOpacity>
</View>
</LinearGradient>
{/* Statistics Grid */}
<View style={styles.statsContainer}>
<View style={styles.statsGrid}>
<View style={styles.statCard}>
<Ionicons
name="document-text"
size={28}
color={Colors.primary}
/>
<Text style={styles.statValue}>{stats.totalCertificates}</Text>
<Text style={styles.statLabel}>Certificates</Text>
</View>
<View style={styles.statCard}>
<Ionicons
name="checkmark-circle"
size={28}
color={Colors.success}
/>
<Text style={styles.statValue}>{stats.verifiedCertificates}</Text>
<Text style={styles.statLabel}>Verified</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="bulb" size={28} color={Colors.kurdishGold} />
<Text style={styles.statValue}>{stats.skillsAcquired}</Text>
<Text style={styles.statLabel}>Skills</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="time" size={28} color={Colors.coral} />
<Text style={styles.statValue}>{stats.learningHours}</Text>
<Text style={styles.statLabel}>Hours</Text>
</View>
</View>
</View>
{/* Certificates Section */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>My Certificates</Text>
<TouchableOpacity>
<Text style={styles.viewAllText}>View All</Text>
</TouchableOpacity>
</View>
{certificates.map((certificate) => (
<TouchableOpacity
key={certificate.id}
style={styles.certificateCard}
onPress={() => verifyCertificate(certificate)}
activeOpacity={0.7}
>
<View style={styles.certificateHeader}>
<View
style={[
styles.certificateIconContainer,
{
backgroundColor:
getCertificateColor(certificate.type) + '20',
},
]}
>
<Ionicons
name={getCertificateIcon(certificate.type)}
size={28}
color={getCertificateColor(certificate.type)}
/>
</View>
<View style={styles.certificateInfo}>
<Text style={styles.certificateTitle}>
{certificate.title}
</Text>
<Text style={styles.certificateInstitution}>
{certificate.institution}
</Text>
<Text style={styles.certificateDate}>
Issued: {certificate.issueDate}
</Text>
</View>
</View>
<View style={styles.certificateFooter}>
<View style={styles.certificateId}>
<Ionicons
name="finger-print"
size={14}
color={Colors.textGray}
/>
<Text style={styles.certificateIdText}>
{certificate.certificateId}
</Text>
</View>
<View style={styles.certificateActions}>
{certificate.verified && (
<View style={styles.verifiedBadge}>
<Ionicons
name="shield-checkmark"
size={14}
color={Colors.success}
/>
<Text style={styles.verifiedText}>Verified</Text>
</View>
)}
<TouchableOpacity
onPress={() => shareCertificate(certificate)}
style={styles.shareButton}
>
<Ionicons
name="share-social-outline"
size={20}
color={Colors.primary}
/>
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
))}
</View>
{/* Learning Paths */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Recommended Learning Paths</Text>
<TouchableOpacity style={styles.pathCard} activeOpacity={0.7}>
<LinearGradient
colors={[Colors.primary + '20', Colors.success + '20']}
style={styles.pathGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.pathContent}>
<View style={styles.pathIcon}>
<Ionicons name="code-slash" size={32} color={Colors.primary} />
</View>
<View style={styles.pathInfo}>
<Text style={styles.pathTitle}>
Blockchain Developer Path
</Text>
<Text style={styles.pathDescription}>
Master Substrate & Polkadot development
</Text>
<View style={styles.pathMeta}>
<View style={styles.pathMetaItem}>
<Ionicons name="book" size={14} color={Colors.textGray} />
<Text style={styles.pathMetaText}>8 Courses</Text>
</View>
<View style={styles.pathMetaItem}>
<Ionicons name="time" size={14} color={Colors.textGray} />
<Text style={styles.pathMetaText}>120 Hours</Text>
</View>
</View>
</View>
<Ionicons
name="chevron-forward"
size={24}
color={Colors.textGray}
/>
</View>
</LinearGradient>
</TouchableOpacity>
<TouchableOpacity style={styles.pathCard} activeOpacity={0.7}>
<LinearGradient
colors={[Colors.kurdishGold + '20', Colors.coral + '20']}
style={styles.pathGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.pathContent}>
<View style={styles.pathIcon}>
<Ionicons name="language" size={32} color={Colors.kurdishGold} />
</View>
<View style={styles.pathInfo}>
<Text style={styles.pathTitle}>Kurdish Culture & Heritage</Text>
<Text style={styles.pathDescription}>
Learn Kurdish language and history
</Text>
<View style={styles.pathMeta}>
<View style={styles.pathMetaItem}>
<Ionicons name="book" size={14} color={Colors.textGray} />
<Text style={styles.pathMetaText}>5 Courses</Text>
</View>
<View style={styles.pathMetaItem}>
<Ionicons name="time" size={14} color={Colors.textGray} />
<Text style={styles.pathMetaText}>60 Hours</Text>
</View>
</View>
</View>
<Ionicons
name="chevron-forward"
size={24}
color={Colors.textGray}
/>
</View>
</LinearGradient>
</TouchableOpacity>
</View>
{/* Info Section */}
<View style={styles.infoSection}>
<View style={styles.infoCard}>
<Ionicons name="information-circle" size={24} color={Colors.primary} />
<Text style={styles.infoText}>
All certificates are stored on PezkuwiChain blockchain and IPFS,
ensuring permanent verification and authenticity.
</Text>
</View>
</View>
<View style={{ height: 40 }} />
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
paddingHorizontal: 20,
paddingTop: 20,
paddingBottom: 24,
borderBottomLeftRadius: 24,
borderBottomRightRadius: 24,
},
headerContent: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
headerTitle: {
fontSize: 32,
fontWeight: '700',
color: 'white',
marginBottom: 4,
},
headerSubtitle: {
fontSize: 16,
color: 'rgba(255, 255, 255, 0.9)',
},
addButton: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: 'rgba(255, 255, 255, 0.2)',
alignItems: 'center',
justifyContent: 'center',
},
statsContainer: {
paddingHorizontal: 20,
marginTop: -30,
marginBottom: 20,
},
statsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
},
statCard: {
flex: 1,
minWidth: '47%',
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
},
statValue: {
fontSize: 24,
fontWeight: '700',
color: Colors.textDark,
marginTop: 8,
marginBottom: 4,
},
statLabel: {
fontSize: 13,
color: Colors.textGray,
textAlign: 'center',
},
section: {
paddingHorizontal: 20,
marginBottom: 24,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
sectionTitle: {
fontSize: 20,
fontWeight: '700',
color: Colors.textDark,
},
viewAllText: {
fontSize: 14,
fontWeight: '600',
color: Colors.primary,
},
certificateCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
certificateHeader: {
flexDirection: 'row',
marginBottom: 12,
},
certificateIconContainer: {
width: 56,
height: 56,
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
certificateInfo: {
flex: 1,
},
certificateTitle: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 4,
},
certificateInstitution: {
fontSize: 14,
color: Colors.textGray,
marginBottom: 2,
},
certificateDate: {
fontSize: 12,
color: Colors.textGray,
},
certificateFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: 12,
borderTopWidth: 1,
borderTopColor: Colors.background,
},
certificateId: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
flex: 1,
},
certificateIdText: {
fontSize: 12,
color: Colors.textGray,
fontFamily: 'monospace',
},
certificateActions: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
verifiedBadge: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
backgroundColor: Colors.success + '20',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 8,
},
verifiedText: {
fontSize: 12,
fontWeight: '600',
color: Colors.success,
},
shareButton: {
padding: 4,
},
pathCard: {
marginBottom: 12,
borderRadius: 14,
overflow: 'hidden',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
pathGradient: {
padding: 16,
},
pathContent: {
flexDirection: 'row',
alignItems: 'center',
},
pathIcon: {
width: 56,
height: 56,
borderRadius: 12,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
pathInfo: {
flex: 1,
},
pathTitle: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 4,
},
pathDescription: {
fontSize: 13,
color: Colors.textGray,
marginBottom: 8,
},
pathMeta: {
flexDirection: 'row',
gap: 16,
},
pathMetaItem: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
},
pathMetaText: {
fontSize: 12,
color: Colors.textGray,
},
infoSection: {
paddingHorizontal: 20,
marginBottom: 24,
},
infoCard: {
flexDirection: 'row',
backgroundColor: Colors.primary + '10',
borderRadius: 12,
padding: 16,
gap: 12,
},
infoText: {
flex: 1,
fontSize: 13,
color: Colors.textDark,
lineHeight: 20,
},
});
@@ -0,0 +1,792 @@
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
SafeAreaView,
ScrollView,
TouchableOpacity,
TextInput,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import Colors from '../../constants/colors';
interface ExchangeRate {
hezToPez: number;
pezToHez: number;
lastUpdated: string;
}
interface SwapHistory {
id: string;
fromToken: 'HEZ' | 'PEZ';
toToken: 'HEZ' | 'PEZ';
fromAmount: number;
toAmount: number;
rate: number;
date: string;
txHash: string;
}
export default function ExchangeScreen({ navigation }: any) {
const [fromToken, setFromToken] = useState<'HEZ' | 'PEZ'>('HEZ');
const [toToken, setToToken] = useState<'HEZ' | 'PEZ'>('PEZ');
const [fromAmount, setFromAmount] = useState('');
const [toAmount, setToAmount] = useState('');
const [slippage, setSlippage] = useState('0.5');
const [balances] = useState({
hez: 45750.5,
pez: 1234567,
});
const [exchangeRate] = useState<ExchangeRate>({
hezToPez: 27.5,
pezToHez: 0.0364,
lastUpdated: new Date().toLocaleTimeString(),
});
const [swapHistory] = useState<SwapHistory[]>([
{
id: '1',
fromToken: 'HEZ',
toToken: 'PEZ',
fromAmount: 100,
toAmount: 2750,
rate: 27.5,
date: '2024-01-23 14:30',
txHash: '0x1a2b3c...',
},
{
id: '2',
fromToken: 'PEZ',
toToken: 'HEZ',
fromAmount: 5000,
toAmount: 182,
rate: 0.0364,
date: '2024-01-22 10:15',
txHash: '0x4d5e6f...',
},
]);
useEffect(() => {
calculateToAmount();
}, [fromAmount, fromToken, toToken]);
const calculateToAmount = () => {
if (!fromAmount || parseFloat(fromAmount) <= 0) {
setToAmount('');
return;
}
const amount = parseFloat(fromAmount);
let result = 0;
if (fromToken === 'HEZ' && toToken === 'PEZ') {
result = amount * exchangeRate.hezToPez;
} else if (fromToken === 'PEZ' && toToken === 'HEZ') {
result = amount * exchangeRate.pezToHez;
}
// Apply slippage
const slippagePercent = parseFloat(slippage) / 100;
result = result * (1 - slippagePercent);
setToAmount(result.toFixed(4));
};
const swapTokens = () => {
const newFromToken = toToken;
const newToToken = fromToken;
setFromToken(newFromToken);
setToToken(newToToken);
setFromAmount(toAmount);
};
const executeSwap = () => {
if (!fromAmount || parseFloat(fromAmount) <= 0) {
Alert.alert('Invalid Amount', 'Please enter a valid amount to swap');
return;
}
const amount = parseFloat(fromAmount);
const currentBalance = fromToken === 'HEZ' ? balances.hez : balances.pez;
if (amount > currentBalance) {
Alert.alert('Insufficient Balance', `You don't have enough ${fromToken}`);
return;
}
Alert.alert(
'Confirm Swap',
`Swap ${fromAmount} ${fromToken} for ${toAmount} ${toToken}?\n\nRate: 1 ${fromToken} = ${
fromToken === 'HEZ' ? exchangeRate.hezToPez : exchangeRate.pezToHez
} ${toToken}\nSlippage: ${slippage}%`,
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Confirm',
onPress: () => {
Alert.alert(
'Success!',
`Swap executed successfully!\n\nTransaction hash: 0x${Math.random()
.toString(16)
.substr(2, 8)}...`
);
setFromAmount('');
setToAmount('');
},
},
]
);
};
const setMaxAmount = () => {
const maxBalance = fromToken === 'HEZ' ? balances.hez : balances.pez;
setFromAmount(maxBalance.toString());
};
const getCurrentRate = () => {
return fromToken === 'HEZ' ? exchangeRate.hezToPez : exchangeRate.pezToHez;
};
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* Header */}
<LinearGradient
colors={[Colors.primary, Colors.kurdishGold]}
style={styles.header}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.headerContent}>
<View>
<Text style={styles.headerTitle}>Exchange</Text>
<Text style={styles.headerSubtitle}>Swap HEZ PEZ Tokens</Text>
</View>
<TouchableOpacity
style={styles.historyButton}
onPress={() => Alert.alert('History', 'Full swap history coming soon')}
>
<Ionicons name="time-outline" size={24} color="white" />
</TouchableOpacity>
</View>
</LinearGradient>
{/* Exchange Rate Card */}
<View style={styles.rateContainer}>
<View style={styles.rateCard}>
<View style={styles.rateHeader}>
<Text style={styles.rateTitle}>Current Exchange Rate</Text>
<View style={styles.updateBadge}>
<Ionicons name="sync" size={12} color={Colors.success} />
<Text style={styles.updateText}>Live</Text>
</View>
</View>
<View style={styles.rateContent}>
<View style={styles.rateItem}>
<Text style={styles.rateLabel}>1 HEZ =</Text>
<Text style={styles.rateValue}>{exchangeRate.hezToPez} PEZ</Text>
</View>
<View style={styles.rateDivider} />
<View style={styles.rateItem}>
<Text style={styles.rateLabel}>1 PEZ =</Text>
<Text style={styles.rateValue}>{exchangeRate.pezToHez} HEZ</Text>
</View>
</View>
<Text style={styles.rateUpdate}>
Last updated: {exchangeRate.lastUpdated}
</Text>
</View>
</View>
{/* Swap Interface */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Swap Tokens</Text>
{/* From Token */}
<View style={styles.swapCard}>
<View style={styles.swapHeader}>
<Text style={styles.swapLabel}>From</Text>
<Text style={styles.balanceText}>
Balance: {fromToken === 'HEZ' ? balances.hez.toLocaleString() : balances.pez.toLocaleString()} {fromToken}
</Text>
</View>
<View style={styles.inputContainer}>
<TextInput
style={styles.amountInput}
placeholder="0.00"
keyboardType="decimal-pad"
value={fromAmount}
onChangeText={setFromAmount}
/>
<View style={styles.tokenSelector}>
<View style={styles.tokenBadge}>
<Text style={styles.tokenText}>{fromToken}</Text>
</View>
<TouchableOpacity onPress={setMaxAmount} style={styles.maxButton}>
<Text style={styles.maxButtonText}>MAX</Text>
</TouchableOpacity>
</View>
</View>
</View>
{/* Swap Button */}
<View style={styles.swapButtonContainer}>
<TouchableOpacity
style={styles.swapIconButton}
onPress={swapTokens}
activeOpacity={0.7}
>
<Ionicons name="swap-vertical" size={24} color={Colors.primary} />
</TouchableOpacity>
</View>
{/* To Token */}
<View style={styles.swapCard}>
<View style={styles.swapHeader}>
<Text style={styles.swapLabel}>To</Text>
<Text style={styles.balanceText}>
Balance: {toToken === 'HEZ' ? balances.hez.toLocaleString() : balances.pez.toLocaleString()} {toToken}
</Text>
</View>
<View style={styles.inputContainer}>
<TextInput
style={[styles.amountInput, styles.amountInputDisabled]}
placeholder="0.00"
value={toAmount}
editable={false}
/>
<View style={styles.tokenSelector}>
<View style={styles.tokenBadge}>
<Text style={styles.tokenText}>{toToken}</Text>
</View>
</View>
</View>
</View>
{/* Swap Details */}
{fromAmount && toAmount && (
<View style={styles.detailsCard}>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>Rate</Text>
<Text style={styles.detailValue}>
1 {fromToken} = {getCurrentRate()} {toToken}
</Text>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>Slippage Tolerance</Text>
<View style={styles.slippageSelector}>
<TouchableOpacity
style={[
styles.slippageOption,
slippage === '0.5' && styles.slippageOptionActive,
]}
onPress={() => setSlippage('0.5')}
>
<Text
style={[
styles.slippageText,
slippage === '0.5' && styles.slippageTextActive,
]}
>
0.5%
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.slippageOption,
slippage === '1.0' && styles.slippageOptionActive,
]}
onPress={() => setSlippage('1.0')}
>
<Text
style={[
styles.slippageText,
slippage === '1.0' && styles.slippageTextActive,
]}
>
1.0%
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.slippageOption,
slippage === '2.0' && styles.slippageOptionActive,
]}
onPress={() => setSlippage('2.0')}
>
<Text
style={[
styles.slippageText,
slippage === '2.0' && styles.slippageTextActive,
]}
>
2.0%
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.detailRow}>
<Text style={styles.detailLabel}>Network Fee</Text>
<Text style={styles.detailValue}>~0.01 HEZ</Text>
</View>
</View>
)}
{/* Execute Swap Button */}
<TouchableOpacity
style={styles.executeButton}
onPress={executeSwap}
activeOpacity={0.8}
>
<LinearGradient
colors={[Colors.primary, Colors.success]}
style={styles.executeButtonGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<Ionicons name="swap-horizontal" size={20} color="white" />
<Text style={styles.executeButtonText}>Swap Tokens</Text>
</LinearGradient>
</TouchableOpacity>
</View>
{/* Recent Swaps */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Recent Swaps</Text>
<TouchableOpacity>
<Text style={styles.viewAllText}>View All</Text>
</TouchableOpacity>
</View>
{swapHistory.map((swap) => (
<View key={swap.id} style={styles.historyCard}>
<View style={styles.historyHeader}>
<View style={styles.historyIcon}>
<Ionicons
name="swap-horizontal"
size={24}
color={Colors.primary}
/>
</View>
<View style={styles.historyInfo}>
<Text style={styles.historyTitle}>
{swap.fromToken} {swap.toToken}
</Text>
<Text style={styles.historyDate}>{swap.date}</Text>
<Text style={styles.historyHash}>Tx: {swap.txHash}</Text>
</View>
<View style={styles.historyAmount}>
<Text style={styles.historyFromAmount}>
-{swap.fromAmount} {swap.fromToken}
</Text>
<Text style={styles.historyToAmount}>
+{swap.toAmount} {swap.toToken}
</Text>
</View>
</View>
<View style={styles.historyFooter}>
<Text style={styles.historyRate}>
Rate: 1 {swap.fromToken} = {swap.rate} {swap.toToken}
</Text>
<TouchableOpacity>
<Ionicons
name="open-outline"
size={16}
color={Colors.primary}
/>
</TouchableOpacity>
</View>
</View>
))}
</View>
{/* Info Section */}
<View style={styles.infoSection}>
<View style={styles.infoCard}>
<Ionicons name="information-circle" size={24} color={Colors.primary} />
<View style={styles.infoContent}>
<Text style={styles.infoTitle}>About Token Swap</Text>
<Text style={styles.infoText}>
Exchange rates are determined by the on-chain liquidity pool. HEZ is the security layer token, while PEZ is the governance token. Swap fees support network validators and liquidity providers.
</Text>
</View>
</View>
</View>
<View style={{ height: 40 }} />
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
paddingHorizontal: 20,
paddingTop: 20,
paddingBottom: 24,
borderBottomLeftRadius: 24,
borderBottomRightRadius: 24,
},
headerContent: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
headerTitle: {
fontSize: 32,
fontWeight: '700',
color: 'white',
marginBottom: 4,
},
headerSubtitle: {
fontSize: 16,
color: 'rgba(255, 255, 255, 0.9)',
},
historyButton: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: 'rgba(255, 255, 255, 0.2)',
alignItems: 'center',
justifyContent: 'center',
},
rateContainer: {
paddingHorizontal: 20,
marginTop: -30,
marginBottom: 20,
},
rateCard: {
backgroundColor: 'white',
borderRadius: 16,
padding: 20,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
},
rateHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
rateTitle: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
},
updateBadge: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
backgroundColor: Colors.success + '20',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 8,
},
updateText: {
fontSize: 12,
fontWeight: '600',
color: Colors.success,
},
rateContent: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
rateItem: {
flex: 1,
alignItems: 'center',
},
rateLabel: {
fontSize: 14,
color: Colors.textGray,
marginBottom: 4,
},
rateValue: {
fontSize: 18,
fontWeight: '700',
color: Colors.primary,
},
rateDivider: {
width: 1,
height: 40,
backgroundColor: Colors.background,
},
rateUpdate: {
fontSize: 12,
color: Colors.textGray,
textAlign: 'center',
},
section: {
paddingHorizontal: 20,
marginBottom: 24,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
sectionTitle: {
fontSize: 20,
fontWeight: '700',
color: Colors.textDark,
marginBottom: 12,
},
viewAllText: {
fontSize: 14,
fontWeight: '600',
color: Colors.primary,
},
swapCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
swapHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
swapLabel: {
fontSize: 14,
fontWeight: '600',
color: Colors.textGray,
},
balanceText: {
fontSize: 12,
color: Colors.textGray,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
amountInput: {
flex: 1,
fontSize: 24,
fontWeight: '600',
color: Colors.textDark,
},
amountInputDisabled: {
color: Colors.textGray,
},
tokenSelector: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
tokenBadge: {
backgroundColor: Colors.primary + '20',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 10,
},
tokenText: {
fontSize: 16,
fontWeight: '700',
color: Colors.primary,
},
maxButton: {
backgroundColor: Colors.background,
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 8,
},
maxButtonText: {
fontSize: 12,
fontWeight: '700',
color: Colors.primary,
},
swapButtonContainer: {
alignItems: 'center',
marginVertical: -12,
zIndex: 10,
},
swapIconButton: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 3,
},
detailsCard: {
backgroundColor: Colors.background,
borderRadius: 12,
padding: 16,
marginTop: 16,
gap: 12,
},
detailRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
detailLabel: {
fontSize: 14,
color: Colors.textGray,
},
detailValue: {
fontSize: 14,
fontWeight: '600',
color: Colors.textDark,
},
slippageSelector: {
flexDirection: 'row',
gap: 8,
},
slippageOption: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 8,
backgroundColor: 'white',
},
slippageOptionActive: {
backgroundColor: Colors.primary,
},
slippageText: {
fontSize: 12,
fontWeight: '600',
color: Colors.textGray,
},
slippageTextActive: {
color: 'white',
},
executeButton: {
borderRadius: 12,
overflow: 'hidden',
marginTop: 20,
},
executeButtonGradient: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 16,
gap: 8,
},
executeButtonText: {
fontSize: 16,
fontWeight: '700',
color: 'white',
},
historyCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
historyHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
historyIcon: {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: Colors.primary + '20',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
historyInfo: {
flex: 1,
},
historyTitle: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 2,
},
historyDate: {
fontSize: 12,
color: Colors.textGray,
marginBottom: 2,
},
historyHash: {
fontSize: 11,
color: Colors.textGray,
fontFamily: 'monospace',
},
historyAmount: {
alignItems: 'flex-end',
},
historyFromAmount: {
fontSize: 14,
fontWeight: '600',
color: Colors.error,
marginBottom: 2,
},
historyToAmount: {
fontSize: 14,
fontWeight: '600',
color: Colors.success,
},
historyFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: 12,
borderTopWidth: 1,
borderTopColor: Colors.background,
},
historyRate: {
fontSize: 12,
color: Colors.textGray,
},
infoSection: {
paddingHorizontal: 20,
marginBottom: 24,
},
infoCard: {
flexDirection: 'row',
backgroundColor: Colors.primary + '10',
borderRadius: 14,
padding: 16,
gap: 12,
},
infoContent: {
flex: 1,
},
infoTitle: {
fontSize: 14,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 4,
},
infoText: {
fontSize: 13,
color: Colors.textDark,
lineHeight: 20,
},
});
@@ -0,0 +1,416 @@
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
ScrollView,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import AsyncStorage from '@react-native-async-storage/async-storage';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
interface GovernanceService {
id: string;
name: string;
nameKu: string;
icon: keyof typeof Ionicons.glyphMap;
color: string;
description: string;
route?: string;
}
export default function GovernanceScreen({ navigation }: any) {
const [isHemwelati, setIsHemwelati] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
checkKYCStatus();
}, []);
const checkKYCStatus = async () => {
try {
const kycData = await AsyncStorage.getItem('kyc_data');
const kycStatus = await AsyncStorage.getItem('kyc_status');
if (kycData && kycStatus === 'approved') {
setIsHemwelati(true);
} else {
setIsHemwelati(false);
}
} catch (error) {
console.error('Error checking KYC status:', error);
setIsHemwelati(false);
} finally {
setLoading(false);
}
};
// Main governance services (not ministries)
const services: GovernanceService[] = [
{
id: 'presidency',
name: 'Presidency',
nameKu: 'Serokayetî',
icon: 'business',
color: '#E74C3C',
description: 'Office of the President',
},
{
id: 'parliament',
name: 'Parliament',
nameKu: 'Meclîs',
icon: 'people',
color: '#3498DB',
description: 'Legislative Assembly - 201 MPs',
},
{
id: 'ministries',
name: 'Ministries',
nameKu: 'Wezaretî',
icon: 'briefcase',
color: '#9B59B6',
description: 'All Cabinet Ministries',
route: 'Ministries',
},
{
id: 'voting',
name: 'Voting',
nameKu: 'Dengdan',
icon: 'checkmark-circle',
color: '#1ABC9C',
description: 'Proposals & Democratic Voting',
},
{
id: 'p2p',
name: 'P2P Services',
nameKu: 'Karûbarên P2P',
icon: 'people-circle',
color: '#26A69A',
description: 'Peer-to-Peer Transactions',
},
{
id: 'b2b',
name: 'B2B Services',
nameKu: 'Karûbarên B2B',
icon: 'business-outline',
color: '#FF9800',
description: 'Business-to-Business Platform',
},
{
id: 'digital',
name: 'Digital Infrastructure',
nameKu: 'Binyata Dîjîtal',
icon: 'server',
color: '#26C6DA',
description: 'Technology & Innovation Hub',
},
{
id: 'citizenship',
name: 'Citizenship Affairs',
nameKu: 'Karûbarên Hemwelatî',
icon: 'card',
color: '#66BB6A',
description: 'Identity & Registration Services',
},
{
id: 'treasury',
name: 'Public Treasury',
nameKu: 'Xizêneya Giştî',
icon: 'wallet',
color: '#F39C12',
description: 'National Budget & Spending',
},
{
id: 'judiciary',
name: 'Judiciary',
nameKu: 'Dadwerî',
icon: 'scale',
color: '#34495E',
description: 'Court System & Justice',
},
{
id: 'elections',
name: 'Elections',
nameKu: 'Hilbijartinî',
icon: 'ballot',
color: '#5C6BC0',
description: 'Electoral Commission',
},
{
id: 'ombudsman',
name: 'Ombudsman',
nameKu: 'Parêzerê Mafan',
icon: 'shield-checkmark',
color: '#EC407A',
description: 'Citizens Rights Protection',
},
];
const handleServicePress = (service: GovernanceService) => {
if (!isHemwelati) {
Alert.alert(
'Access Restricted',
'You must complete Identity KYC verification to access Governance features.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Complete KYC',
onPress: () => navigation.navigate('Identity'),
},
]
);
return;
}
// Navigate to specific route if available
if (service.route) {
navigation.navigate(service.route);
return;
}
// Show placeholder for other services
Alert.alert(
service.name,
`${service.nameKu}\n\n${service.description}\n\nThis feature is under development.`,
[{ text: 'OK' }]
);
};
if (loading) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.loadingContainer}>
<Text style={styles.loadingText}>Loading...</Text>
</View>
</SafeAreaView>
);
}
if (!isHemwelati) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.restrictedContainer}>
<Ionicons name="lock-closed" size={80} color={Colors.textLight} />
<Text style={styles.restrictedTitle}>Access Restricted</Text>
<Text style={styles.restrictedText}>
Governance features are only available to verified Hemwelatî (Digital Citizens).
</Text>
<Text style={styles.restrictedText}>
Complete your Identity KYC verification to access:
</Text>
<View style={styles.featureList}>
<Text style={styles.featureItem}> Vote on proposals</Text>
<Text style={styles.featureItem}> Participate in governance</Text>
<Text style={styles.featureItem}> Access government services</Text>
<Text style={styles.featureItem}> View parliamentary sessions</Text>
</View>
<TouchableOpacity
style={styles.kycButton}
onPress={() => navigation.navigate('Identity')}
>
<Ionicons name="checkmark-circle" size={20} color="#FFFFFF" />
<Text style={styles.kycButtonText}>Complete Identity KYC</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
return (
<SafeAreaView style={styles.container}>
{/* Header */}
<View style={styles.header}>
<View style={styles.headerContent}>
<Text style={styles.headerTitle}>Welati Governance</Text>
<Text style={styles.headerSubtitle}>Komara Kurdistanê</Text>
</View>
<View style={styles.verifiedBadge}>
<Ionicons name="checkmark-circle" size={20} color="#27AE60" />
<Text style={styles.verifiedText}>Verified Citizen</Text>
</View>
</View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
{/* Services Grid */}
<View style={styles.grid}>
{services.map((service) => (
<TouchableOpacity
key={service.id}
style={styles.serviceCard}
onPress={() => handleServicePress(service)}
activeOpacity={0.7}
>
<View style={[styles.iconContainer, { backgroundColor: service.color }]}>
<Ionicons name={service.icon} size={32} color="#FFFFFF" />
</View>
<Text style={styles.serviceName}>{service.name}</Text>
<Text style={styles.serviceNameKu}>{service.nameKu}</Text>
</TouchableOpacity>
))}
</View>
{/* Info Footer */}
<View style={styles.infoFooter}>
<Ionicons name="information-circle" size={20} color={Colors.primary} />
<Text style={styles.infoText}>
As a verified Hemwelatî, you have full access to all governance features.
</Text>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
loadingText: {
fontSize: Typography.sizes.lg,
color: Colors.textLight,
},
restrictedContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: Spacing.xl,
},
restrictedTitle: {
fontSize: Typography.sizes.xxl,
fontWeight: Typography.weights.bold,
color: Colors.textDark,
marginTop: Spacing.lg,
marginBottom: Spacing.md,
},
restrictedText: {
fontSize: Typography.sizes.md,
color: Colors.textLight,
textAlign: 'center',
marginBottom: Spacing.md,
lineHeight: 22,
},
featureList: {
marginTop: Spacing.md,
marginBottom: Spacing.xl,
},
featureItem: {
fontSize: Typography.sizes.md,
color: Colors.textDark,
marginBottom: Spacing.xs,
},
kycButton: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: Colors.primary,
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.md,
borderRadius: BorderRadius.lg,
...Shadow.medium,
},
kycButtonText: {
fontSize: Typography.sizes.lg,
fontWeight: Typography.weights.bold,
color: '#FFFFFF',
marginLeft: Spacing.sm,
},
header: {
backgroundColor: '#FFFFFF',
paddingHorizontal: Spacing.lg,
paddingVertical: Spacing.md,
borderBottomWidth: 1,
borderBottomColor: '#E0E0E0',
...Shadow.small,
},
headerContent: {
marginBottom: Spacing.sm,
},
headerTitle: {
fontSize: Typography.sizes.xxl,
fontWeight: Typography.weights.bold,
color: Colors.textDark,
},
headerSubtitle: {
fontSize: Typography.sizes.md,
color: Colors.textLight,
marginTop: Spacing.xs,
},
verifiedBadge: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#E8F5E9',
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.xs,
borderRadius: BorderRadius.md,
alignSelf: 'flex-start',
},
verifiedText: {
fontSize: Typography.sizes.sm,
fontWeight: Typography.weights.semibold,
color: '#27AE60',
marginLeft: Spacing.xs,
},
content: {
flex: 1,
},
grid: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: Spacing.md,
},
serviceCard: {
width: '31%',
marginHorizontal: '1%',
marginBottom: Spacing.lg,
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.lg,
padding: Spacing.md,
alignItems: 'center',
...Shadow.small,
},
iconContainer: {
width: 64,
height: 64,
borderRadius: BorderRadius.lg,
justifyContent: 'center',
alignItems: 'center',
marginBottom: Spacing.sm,
},
serviceName: {
fontSize: Typography.sizes.xs,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
textAlign: 'center',
marginBottom: Spacing.xs,
},
serviceNameKu: {
fontSize: Typography.sizes.xs,
color: Colors.textLight,
textAlign: 'center',
},
infoFooter: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#E3F2FD',
margin: Spacing.lg,
padding: Spacing.md,
borderRadius: BorderRadius.md,
},
infoText: {
flex: 1,
fontSize: Typography.sizes.sm,
color: Colors.primary,
marginLeft: Spacing.sm,
lineHeight: 18,
},
});
@@ -0,0 +1,117 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, SafeAreaView, ActivityIndicator } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
import { kycService } from '../../services/kycService';
export default function GovernanceScreen({ navigation }: any) {
const [loading, setLoading] = useState(true);
const [hasAccess, setHasAccess] = useState(false);
useEffect(() => {
checkAccess();
}, []);
const checkAccess = async () => {
try {
const access = await kycService.hasGovernanceAccess();
setHasAccess(access);
} catch (error) {
console.error('Failed to check governance access:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<SafeAreaView style={styles.container}>
<ActivityIndicator size="large" color={Colors.mint} />
</SafeAreaView>
);
}
// If user doesn't have access (KYC not approved)
if (!hasAccess) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Governance (Welati)</Text>
</View>
<View style={styles.content}>
<View style={styles.lockedCard}>
<Ionicons name="lock-closed" size={60} color={Colors.textGray} />
<Text style={styles.lockedTitle}>Access Restricted</Text>
<Text style={styles.lockedSubtitle}>
You need to complete Identity KYC verification to access Governance features.
</Text>
</View>
<View style={styles.requirementsCard}>
<Text style={styles.requirementsTitle}>Requirements:</Text>
<View style={styles.requirementItem}>
<Ionicons name="person-outline" size={20} color={Colors.teal} />
<Text style={styles.requirementText}>Complete Identity KYC form</Text>
</View>
<View style={styles.requirementItem}>
<Ionicons name="checkmark-circle-outline" size={20} color={Colors.teal} />
<Text style={styles.requirementText}>Get KYC approval on blockchain</Text>
</View>
<View style={styles.requirementItem}>
<Ionicons name="card-outline" size={20} color={Colors.teal} />
<Text style={styles.requirementText}>Receive Kurdistan Digital Citizen card</Text>
</View>
</View>
<TouchableOpacity
style={styles.completeButton}
onPress={() => navigation.navigate('Identity')}
>
<Text style={styles.completeButtonText}>Complete Identity KYC</Text>
<Ionicons name="arrow-forward" size={20} color="#FFFFFF" />
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
// If user has access, show governance features
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Governance (Welati)</Text>
<View style={styles.citizenBadge}>
<Ionicons name="shield-checkmark" size={16} color={Colors.mint} />
<Text style={styles.citizenBadgeText}>Verified Citizen</Text>
</View>
</View>
<View style={styles.content}>
<Text style={styles.sectionTitle}>Active Proposals</Text>
<Text style={styles.comingSoon}>Coming soon...</Text>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: Colors.background },
header: { padding: Spacing.xl, borderBottomWidth: 1, borderBottomColor: '#F0F0F0' },
title: { fontSize: 28, fontWeight: '700', color: Colors.textDark },
content: { flex: 1, padding: Spacing.xl },
lockedCard: { backgroundColor: Colors.card, padding: Spacing.xxxl, borderRadius: BorderRadius.xlarge, alignItems: 'center', ...Shadow.soft },
lockedTitle: { fontSize: 24, fontWeight: '700', color: Colors.textDark, marginTop: Spacing.lg },
lockedSubtitle: { fontSize: 16, color: Colors.textGray, marginTop: Spacing.sm, textAlign: 'center', lineHeight: 24 },
requirementsCard: { backgroundColor: Colors.teal + '20', padding: Spacing.xl, borderRadius: BorderRadius.large, marginTop: Spacing.xl },
requirementsTitle: { fontSize: 18, fontWeight: '600', color: Colors.textDark, marginBottom: Spacing.md },
requirementItem: { flexDirection: 'row', alignItems: 'center', marginTop: Spacing.sm, gap: Spacing.sm },
requirementText: { fontSize: 16, color: Colors.textDark },
completeButton: { flexDirection: 'row', backgroundColor: Colors.teal, borderRadius: BorderRadius.xxlarge, paddingVertical: Spacing.lg, paddingHorizontal: Spacing.xxxl, alignItems: 'center', justifyContent: 'center', marginTop: Spacing.xl, gap: Spacing.sm, ...Shadow.soft },
completeButtonText: { fontSize: 18, fontWeight: '600', color: '#FFFFFF' },
citizenBadge: { flexDirection: 'row', alignItems: 'center', backgroundColor: Colors.mint + '20', paddingVertical: Spacing.sm, paddingHorizontal: Spacing.md, borderRadius: BorderRadius.large, marginTop: Spacing.sm, gap: Spacing.xs, alignSelf: 'flex-start' },
citizenBadgeText: { fontSize: 14, fontWeight: '600', color: Colors.mint },
sectionTitle: { fontSize: 20, fontWeight: '600', color: Colors.textDark, marginBottom: Spacing.md },
comingSoon: { fontSize: 16, color: Colors.textGray, textAlign: 'center', marginTop: Spacing.xl },
});
@@ -0,0 +1,320 @@
import React from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
ScrollView,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
interface Ministry {
id: string;
name: string;
nameKu: string;
icon: keyof typeof Ionicons.glyphMap;
color: string;
description: string;
minister?: string;
}
export default function MinistriesScreen({ navigation }: any) {
const ministries: Ministry[] = [
{
id: 'finance',
name: 'Finance',
nameKu: 'Maliye',
icon: 'cash',
color: '#F39C12',
description: 'Treasury, Budget & Economic Policy',
minister: 'Dr. Heval Azad',
},
{
id: 'justice',
name: 'Justice',
nameKu: 'Adalet',
icon: 'scale',
color: '#34495E',
description: 'Legal System & Courts',
minister: 'Av. Leyla Şoreş',
},
{
id: 'health',
name: 'Health',
nameKu: 'Tenduristî',
icon: 'medical',
color: '#E91E63',
description: 'Healthcare Services & Public Health',
minister: 'Dr. Roj Berxwedan',
},
{
id: 'education',
name: 'Education',
nameKu: 'Perwerde',
icon: 'school',
color: '#5C6BC0',
description: 'Education System & Universities',
minister: 'Prof. Aram Zana',
},
{
id: 'commerce',
name: 'Commerce',
nameKu: 'Bazirganî',
icon: 'cart',
color: '#26A69A',
description: 'Trade, Business & Industry',
minister: 'Berivan Qazi',
},
{
id: 'media',
name: 'Media & Communications',
nameKu: 'Medya û Ragihandin',
icon: 'newspaper',
color: '#FF9800',
description: 'Press, Broadcasting & Information',
minister: 'Jiyan Newroz',
},
{
id: 'culture',
name: 'Culture & Arts',
nameKu: 'Çand û Huner',
icon: 'color-palette',
color: '#EC407A',
description: 'Cultural Heritage & Arts',
minister: 'Şêrko Dilan',
},
{
id: 'tourism',
name: 'Tourism',
nameKu: 'Gerîyan',
icon: 'airplane',
color: '#00ACC1',
description: 'Tourism Development & Promotion',
minister: 'Azad Çiya',
},
{
id: 'foreign',
name: 'Foreign Affairs',
nameKu: 'Karûbarên Derve',
icon: 'globe',
color: '#7E57C2',
description: 'International Relations & Diplomacy',
minister: 'Dilşa Rojava',
},
{
id: 'interior',
name: 'Interior',
nameKu: 'Karûbarên Navxwe',
icon: 'shield',
color: '#8D6E63',
description: 'Internal Security & Public Order',
minister: 'Baran Zagros',
},
{
id: 'defense',
name: 'Defense',
nameKu: 'Bergiranî',
icon: 'shield-checkmark',
color: '#455A64',
description: 'National Defense & Security',
minister: 'Gen. Kendal Şoreş',
},
{
id: 'agriculture',
name: 'Agriculture',
nameKu: 'Çandinî',
icon: 'leaf',
color: '#8BC34A',
description: 'Farming, Food Security & Rural Development',
minister: 'Hêvî Berxwedan',
},
{
id: 'environment',
name: 'Environment',
nameKu: 'Jîngeha',
icon: 'earth',
color: '#66BB6A',
description: 'Environmental Protection & Climate',
minister: 'Çiya Newroz',
},
{
id: 'energy',
name: 'Energy',
nameKu: 'Wize',
icon: 'flash',
color: '#FFA726',
description: 'Energy Resources & Infrastructure',
minister: 'Roj Azad',
},
{
id: 'transport',
name: 'Transport',
nameKu: 'Veguhestin',
icon: 'car',
color: '#42A5F5',
description: 'Transportation & Infrastructure',
minister: 'Beritan Qazi',
},
{
id: 'labor',
name: 'Labor & Social Affairs',
nameKu: 'Kar û Karûbarên Civakî',
icon: 'people',
color: '#AB47BC',
description: 'Employment & Social Welfare',
minister: 'Heval Dilan',
},
];
const handleMinistryPress = (ministry: Ministry) => {
Alert.alert(
ministry.name,
`${ministry.nameKu}\n\nMinister: ${ministry.minister}\n\n${ministry.description}\n\nThis feature is under development.`,
[{ text: 'OK' }]
);
};
return (
<SafeAreaView style={styles.container}>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
<Ionicons name="arrow-back" size={24} color={Colors.textDark} />
</TouchableOpacity>
<View style={styles.headerContent}>
<Text style={styles.headerTitle}>Ministries</Text>
<Text style={styles.headerSubtitle}>Wezaretî</Text>
</View>
</View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
{/* Ministries Grid */}
<View style={styles.grid}>
{ministries.map((ministry) => (
<TouchableOpacity
key={ministry.id}
style={styles.ministryCard}
onPress={() => handleMinistryPress(ministry)}
activeOpacity={0.7}
>
<View style={[styles.iconContainer, { backgroundColor: ministry.color }]}>
<Ionicons name={ministry.icon} size={32} color="#FFFFFF" />
</View>
<Text style={styles.ministryName}>{ministry.name}</Text>
<Text style={styles.ministryNameKu}>{ministry.nameKu}</Text>
{ministry.minister && (
<Text style={styles.ministerName}>{ministry.minister}</Text>
)}
</TouchableOpacity>
))}
</View>
{/* Info Footer */}
<View style={styles.infoFooter}>
<Ionicons name="information-circle" size={20} color={Colors.primary} />
<Text style={styles.infoText}>
Cabinet of {ministries.length} ministries serving the people of Kurdistan.
</Text>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFFFFF',
paddingHorizontal: Spacing.lg,
paddingVertical: Spacing.md,
borderBottomWidth: 1,
borderBottomColor: '#E0E0E0',
...Shadow.small,
},
backButton: {
marginRight: Spacing.md,
},
headerContent: {
flex: 1,
},
headerTitle: {
fontSize: Typography.sizes.xxl,
fontWeight: Typography.weights.bold,
color: Colors.textDark,
},
headerSubtitle: {
fontSize: Typography.sizes.md,
color: Colors.textLight,
marginTop: Spacing.xs,
},
content: {
flex: 1,
},
grid: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: Spacing.md,
},
ministryCard: {
width: '31%',
marginHorizontal: '1%',
marginBottom: Spacing.lg,
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.lg,
padding: Spacing.md,
alignItems: 'center',
...Shadow.small,
},
iconContainer: {
width: 64,
height: 64,
borderRadius: BorderRadius.lg,
justifyContent: 'center',
alignItems: 'center',
marginBottom: Spacing.sm,
},
ministryName: {
fontSize: Typography.sizes.xs,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
textAlign: 'center',
marginBottom: Spacing.xs,
},
ministryNameKu: {
fontSize: Typography.sizes.xs,
color: Colors.textLight,
textAlign: 'center',
marginBottom: Spacing.xs,
},
ministerName: {
fontSize: 10,
color: Colors.primary,
textAlign: 'center',
fontStyle: 'italic',
},
infoFooter: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#E3F2FD',
margin: Spacing.lg,
padding: Spacing.md,
borderRadius: BorderRadius.md,
},
infoText: {
flex: 1,
fontSize: Typography.sizes.sm,
color: Colors.primary,
marginLeft: Spacing.sm,
lineHeight: 18,
},
});
+372
View File
@@ -0,0 +1,372 @@
import React, { useEffect, useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
ScrollView,
SafeAreaView,
Image,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow, IconSizes } from '../../constants/theme';
import { blockchainService } from '../../services/blockchain';
import { Balance } from '../../types';
interface QuickAction {
id: string;
label: string;
icon: keyof typeof Ionicons.glyphMap;
color: string;
onPress: () => void;
}
export default function HomeScreen({ navigation }: any) {
const [balance, setBalance] = useState<Balance | null>(null);
const [trustScore] = useState(750);
useEffect(() => {
loadBalance();
}, []);
const loadBalance = async () => {
try {
// Try to connect to blockchain
const connected = await blockchainService.connect();
if (connected) {
const bal = await blockchainService.getBalances(
'5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
);
setBalance(bal);
}
} catch (error) {
console.log('Using mock data');
// Use mock data
setBalance({
hez: '45,750.5',
pez: '1,234,567',
hezStaked: '30,000',
hezUsd: '45,234',
pezUsd: '123,456',
governancePower: '2.5',
});
}
};
const quickActions: QuickAction[] = [
{
id: 'send',
label: 'Send',
icon: 'arrow-forward',
color: Colors.coral,
onPress: () => navigation.navigate('Send', { token: 'HEZ' }),
},
{
id: 'receive',
label: 'Receive',
icon: 'arrow-down',
color: Colors.blue,
onPress: () => navigation.navigate('Receive', { token: 'HEZ' }),
},
{
id: 'vote',
label: 'Vote',
icon: 'checkmark-circle',
color: Colors.mint,
onPress: () => navigation.navigate('Governance'),
},
{
id: 'proposals',
label: 'Proposals',
icon: 'bulb',
color: Colors.peach,
onPress: () => navigation.navigate('Governance'),
},
{
id: 'identity',
label: 'Identity',
icon: 'person',
color: Colors.teal,
onPress: () => navigation.navigate('Identity'),
},
{
id: 'certificates',
label: 'Certificates',
icon: 'school',
color: Colors.gold,
onPress: () => navigation.navigate('Education'),
},
{
id: 'exchange',
label: 'Exchange',
icon: 'swap-horizontal',
color: Colors.cyan,
onPress: () => navigation.navigate('Exchange'),
},
{
id: 'rewards',
label: 'Rewards',
icon: 'star',
color: Colors.lavender,
onPress: () => navigation.navigate('Wallet'),
},
{
id: 'trust',
label: 'Trust',
icon: 'heart',
color: Colors.emerald,
onPress: () => navigation.navigate('Profile'),
},
];
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* Header */}
<LinearGradient
colors={Colors.gradients.header}
style={styles.header}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.headerTop}>
{/* Profile Avatar */}
<TouchableOpacity
style={styles.profileSection}
onPress={() => navigation.navigate('Profile')}
activeOpacity={0.7}
>
<View style={styles.avatar}>
<Ionicons name="person" size={24} color="#FFFFFF" />
</View>
{/* Trust Score Badge */}
<TouchableOpacity
style={styles.trustBadge}
onPress={() => navigation.navigate('TrustScore')}
activeOpacity={0.7}
>
<Ionicons name="star" size={12} color="#FFFFFF" />
<Text style={styles.trustScore}>{trustScore}</Text>
</TouchableOpacity>
</TouchableOpacity>
{/* Action Icons */}
<View style={styles.headerActions}>
<TouchableOpacity
style={styles.headerIcon}
onPress={() => navigation.navigate('QRScanner')}
>
<Ionicons name="qr-code-outline" size={24} color="#FFFFFF" />
</TouchableOpacity>
<TouchableOpacity
style={styles.headerIcon}
onPress={() => navigation.navigate('Notifications')}
>
<Ionicons name="notifications-outline" size={24} color="#FFFFFF" />
</TouchableOpacity>
<TouchableOpacity
style={styles.headerIcon}
onPress={() => navigation.navigate('Profile')}
>
<Ionicons name="settings-outline" size={24} color="#FFFFFF" />
</TouchableOpacity>
</View>
</View>
</LinearGradient>
{/* Balance Card */}
<View style={styles.balanceCardContainer}>
<TouchableOpacity
style={styles.balanceCard}
onPress={() => navigation.navigate('Wallet')}
activeOpacity={0.8}
>
<View style={styles.balanceRow}>
<TouchableOpacity
style={styles.balanceItem}
onPress={() => navigation.navigate('Wallet', { tab: 'HEZ' })}
activeOpacity={0.7}
>
<Text style={styles.balanceLabel}>HEZ Balance</Text>
<Text style={styles.balanceAmount}>{balance?.hez || '0'}</Text>
<View style={styles.underline} />
</TouchableOpacity>
<TouchableOpacity
style={styles.balanceItem}
onPress={() => navigation.navigate('Wallet', { tab: 'PEZ' })}
activeOpacity={0.7}
>
<Text style={styles.balanceLabel}>PEZ Balance</Text>
<Text style={styles.balanceAmountSecondary}>{balance?.pez || '0'}</Text>
<View style={[styles.underline, { backgroundColor: Colors.peach }]} />
</TouchableOpacity>
</View>
</TouchableOpacity>
</View>
{/* Quick Actions */}
<View style={styles.quickActionsSection}>
<Text style={styles.sectionTitle}>Quick Actions</Text>
<View style={styles.quickActionsGrid}>
{quickActions.map((action) => (
<TouchableOpacity
key={action.id}
style={styles.quickActionButton}
onPress={action.onPress}
activeOpacity={0.7}
>
<View style={[styles.quickActionIcon, { backgroundColor: action.color }]}>
<Ionicons name={action.icon} size={24} color="#FFFFFF" />
</View>
<Text style={styles.quickActionLabel}>{action.label}</Text>
</TouchableOpacity>
))}
</View>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
paddingHorizontal: Spacing.xl,
paddingTop: Spacing.xl,
paddingBottom: Spacing.xxxl * 2,
borderBottomLeftRadius: BorderRadius.xxlarge,
borderBottomRightRadius: BorderRadius.xxlarge,
},
headerTop: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
profileSection: {
flexDirection: 'row',
alignItems: 'center',
gap: Spacing.sm,
},
avatar: {
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
justifyContent: 'center',
alignItems: 'center',
},
trustBadge: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: Colors.gold,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.xs,
borderRadius: BorderRadius.round,
gap: Spacing.xs,
},
trustScore: {
fontSize: Typography.sizes.body,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
},
headerActions: {
flexDirection: 'row',
gap: Spacing.md,
},
headerIcon: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: 'rgba(255, 255, 255, 0.2)',
justifyContent: 'center',
alignItems: 'center',
},
balanceCardContainer: {
paddingHorizontal: Spacing.xl,
marginTop: -Spacing.xxxl,
},
balanceCard: {
backgroundColor: '#F5F3FF',
borderRadius: BorderRadius.large,
padding: Spacing.xl,
...Shadow.soft,
},
balanceRow: {
flexDirection: 'row',
justifyContent: 'space-between',
},
balanceItem: {
flex: 1,
},
balanceLabel: {
fontSize: Typography.sizes.body,
color: Colors.textGray,
marginBottom: Spacing.xs,
},
balanceAmount: {
fontSize: Typography.sizes.hero,
fontWeight: Typography.weights.bold,
color: Colors.textDark,
marginBottom: Spacing.xs,
},
balanceAmountSecondary: {
fontSize: Typography.sizes.heading,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
marginBottom: Spacing.xs,
},
underline: {
width: 80,
height: 3,
backgroundColor: Colors.teal,
borderRadius: 2,
},
quickActionsSection: {
paddingHorizontal: Spacing.xl,
marginTop: Spacing.xl,
marginBottom: Spacing.xl,
},
sectionTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
marginBottom: Spacing.lg,
},
quickActionsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: Spacing.md,
},
quickActionButton: {
width: '31%',
backgroundColor: Colors.card,
borderRadius: BorderRadius.large,
padding: Spacing.lg,
alignItems: 'center',
...Shadow.soft,
},
quickActionIcon: {
width: IconSizes.xxlarge,
height: IconSizes.xxlarge,
borderRadius: IconSizes.xxlarge / 2,
justifyContent: 'center',
alignItems: 'center',
marginBottom: Spacing.sm,
},
quickActionLabel: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
textAlign: 'center',
},
});
@@ -0,0 +1,292 @@
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
ScrollView,
ActivityIndicator,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
interface Notification {
id: string;
type: 'transaction' | 'governance' | 'reward' | 'system';
title: string;
message: string;
timestamp: number;
read: boolean;
}
export default function NotificationsScreen({ navigation }: any) {
const [loading, setLoading] = useState(true);
const [notifications, setNotifications] = useState<Notification[]>([]);
useEffect(() => {
loadNotifications();
}, []);
const loadNotifications = async () => {
// Mock data - replace with actual API call
setTimeout(() => {
setNotifications([
{
id: '1',
type: 'transaction',
title: 'Transaction Confirmed',
message: 'You received 1,000 PEZ from 5Grw...KutQY',
timestamp: Date.now() - 3600000,
read: false,
},
{
id: '2',
type: 'governance',
title: 'New Proposal',
message: 'Proposal #47: Increase PEZ rewards by 10%',
timestamp: Date.now() - 7200000,
read: false,
},
{
id: '3',
type: 'reward',
title: 'Staking Rewards',
message: 'You earned 245 PEZ from staking',
timestamp: Date.now() - 86400000,
read: true,
},
{
id: '4',
type: 'system',
title: 'KYC Approved',
message: 'Your Kurdistan Citizen Card is ready',
timestamp: Date.now() - 172800000,
read: true,
},
]);
setLoading(false);
}, 1000);
};
const getNotificationIcon = (type: string) => {
switch (type) {
case 'transaction':
return 'swap-horizontal';
case 'governance':
return 'megaphone';
case 'reward':
return 'star';
case 'system':
return 'information-circle';
default:
return 'notifications';
}
};
const getNotificationColor = (type: string) => {
switch (type) {
case 'transaction':
return Colors.blue;
case 'governance':
return Colors.peach;
case 'reward':
return Colors.gold;
case 'system':
return Colors.teal;
default:
return Colors.textGray;
}
};
const formatTime = (timestamp: number) => {
const date = new Date(timestamp);
const now = new Date();
const diff = now.getTime() - date.getTime();
const hours = Math.floor(diff / (1000 * 60 * 60));
const days = Math.floor(hours / 24);
if (hours < 1) return 'Just now';
if (hours < 24) return `${hours}h ago`;
if (days === 1) return 'Yesterday';
return `${days}d ago`;
};
const markAsRead = (id: string) => {
setNotifications((prev) =>
prev.map((notif) => (notif.id === id ? { ...notif, read: true } : notif))
);
};
const unreadCount = notifications.filter((n) => !n.read).length;
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
<Ionicons name="arrow-back" size={24} color={Colors.textDark} />
</TouchableOpacity>
<Text style={styles.headerTitle}>Notifications</Text>
{unreadCount > 0 && (
<View style={styles.badge}>
<Text style={styles.badgeText}>{unreadCount}</Text>
</View>
)}
</View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
{loading ? (
<ActivityIndicator size="large" color={Colors.teal} style={{ marginTop: 50 }} />
) : notifications.length === 0 ? (
<View style={styles.emptyState}>
<Ionicons name="notifications-off-outline" size={80} color={Colors.textGray} />
<Text style={styles.emptyText}>No notifications yet</Text>
</View>
) : (
notifications.map((notification) => (
<TouchableOpacity
key={notification.id}
style={[
styles.notificationCard,
!notification.read && styles.unreadCard,
]}
onPress={() => markAsRead(notification.id)}
activeOpacity={0.7}
>
<View
style={[
styles.iconContainer,
{ backgroundColor: getNotificationColor(notification.type) + '20' },
]}
>
<Ionicons
name={getNotificationIcon(notification.type)}
size={24}
color={getNotificationColor(notification.type)}
/>
</View>
<View style={styles.notificationContent}>
<View style={styles.notificationHeader}>
<Text style={styles.notificationTitle}>{notification.title}</Text>
{!notification.read && <View style={styles.unreadDot} />}
</View>
<Text style={styles.notificationMessage}>{notification.message}</Text>
<Text style={styles.notificationTime}>{formatTime(notification.timestamp)}</Text>
</View>
</TouchableOpacity>
))
)}
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.lg,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
},
backButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: Colors.card,
justifyContent: 'center',
alignItems: 'center',
marginRight: Spacing.md,
},
headerTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
flex: 1,
},
badge: {
backgroundColor: Colors.coral,
width: 24,
height: 24,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
},
badgeText: {
fontSize: 12,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
},
content: {
flex: 1,
padding: Spacing.lg,
},
notificationCard: {
flexDirection: 'row',
backgroundColor: Colors.card,
padding: Spacing.lg,
borderRadius: BorderRadius.large,
marginBottom: Spacing.md,
...Shadow.small,
},
unreadCard: {
borderLeftWidth: 4,
borderLeftColor: Colors.teal,
},
iconContainer: {
width: 50,
height: 50,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
marginRight: Spacing.md,
},
notificationContent: {
flex: 1,
},
notificationHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 4,
},
notificationTitle: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
flex: 1,
},
unreadDot: {
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: Colors.teal,
},
notificationMessage: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
marginBottom: 4,
},
notificationTime: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
opacity: 0.7,
},
emptyState: {
alignItems: 'center',
justifyContent: 'center',
marginTop: 100,
},
emptyText: {
fontSize: Typography.sizes.medium,
color: Colors.textGray,
marginTop: Spacing.lg,
},
});
@@ -0,0 +1,459 @@
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
Image,
ActivityIndicator,
ScrollView,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import QRCode from 'react-native-qrcode-svg';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
import { kycService } from '../../services/kycService';
import { KurdistanCitizen, REGION_LABELS } from '../../types/kyc';
export default function CitizenCardScreen({ navigation }: any) {
const [loading, setLoading] = useState(true);
const [citizen, setCitizen] = useState<KurdistanCitizen | null>(null);
useEffect(() => {
loadCitizen();
}, []);
const loadCitizen = async () => {
try {
const data = await kycService.getCitizen();
setCitizen(data);
} catch (error) {
console.error('Failed to load citizen data:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<SafeAreaView style={styles.container}>
<ActivityIndicator size="large" color={Colors.teal} />
</SafeAreaView>
);
}
if (!citizen) {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.errorText}>No citizen data found</Text>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate('IdentityKYCForm')}
>
<Text style={styles.buttonText}>Complete KYC</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
const formatDate = (timestamp: number) => {
const date = new Date(timestamp);
return date.toLocaleDateString('en-GB');
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
<Ionicons name="arrow-back" size={24} color={Colors.textDark} />
</TouchableOpacity>
<Text style={styles.headerTitle}>Digital Hemwelatî</Text>
<TouchableOpacity style={styles.shareButton}>
<Ionicons name="share-outline" size={24} color={Colors.textDark} />
</TouchableOpacity>
</View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
{/* Official Citizen Card */}
<View style={styles.card}>
{/* Burgundy Header */}
<View style={styles.cardHeader}>
<Text style={styles.headerText1}>KOMARA KURDISTANÊ</Text>
<Text style={styles.headerText2}>HEMWELATÎ</Text>
</View>
{/* Main Content - White Background */}
<View style={styles.cardBody}>
{/* Photo and Info Section */}
<View style={styles.topSection}>
{/* Photo */}
<View style={styles.photoContainer}>
{citizen.photo ? (
<Image source={{ uri: citizen.photo }} style={styles.photo} />
) : (
<View style={styles.photoPlaceholder}>
<Ionicons name="person" size={50} color="#999" />
</View>
)}
</View>
{/* Info List */}
<View style={styles.infoList}>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>NAV/NAME</Text>
<Text style={styles.infoValue}>{citizen.fullName}</Text>
</View>
{citizen.fatherName && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>BAV/FATHER</Text>
<Text style={styles.infoValue}>{citizen.fatherName}</Text>
</View>
)}
{citizen.grandfatherName && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>DAPÎR/GRANDFATHER</Text>
<Text style={styles.infoValue}>{citizen.grandfatherName}</Text>
</View>
)}
{citizen.greatGrandfatherName && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>DAPÎRA BIRA/G.GRANDFATHER</Text>
<Text style={styles.infoValue}>{citizen.greatGrandfatherName}</Text>
</View>
)}
{citizen.motherName && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>DAY/MOTHER</Text>
<Text style={styles.infoValue}>{citizen.motherName}</Text>
</View>
)}
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>ZEWICÎN/MARITAL</Text>
<Text style={styles.infoValue}>
{citizen.maritalStatus === 'married' ? 'Zewicî' : 'Nezewicî'}
</Text>
</View>
{citizen.maritalStatus === 'married' && citizen.spouseName && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>HEVJÎN/SPOUSE</Text>
<Text style={styles.infoValue}>{citizen.spouseName}</Text>
</View>
)}
{citizen.children && citizen.children.length > 0 && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>ZAROK/CHILDREN</Text>
<Text style={styles.infoValue}>{citizen.children.length}</Text>
</View>
)}
</View>
</View>
{/* PEZ Sun Logo */}
<View style={styles.sunContainer}>
<Ionicons name="sunny" size={80} color="#D4A017" />
</View>
{/* Region and Citizen ID */}
<View style={styles.middleSection}>
<View style={styles.regionRow}>
<Text style={styles.regionLabel}>HEREM/REGION</Text>
<Text style={styles.regionValue}>{REGION_LABELS[citizen.region].en}</Text>
</View>
<View style={styles.citizenIdRow}>
<Text style={styles.citizenIdLabel}>JIMARA HEMWELATÎ/CITIZEN ID</Text>
<Text style={styles.citizenIdValue}>{citizen.citizenId}</Text>
</View>
</View>
</View>
{/* Green Footer */}
<View style={styles.cardFooter}>
<View style={styles.footerLeft}>
<View style={styles.footerRow}>
<Text style={styles.footerLabel}>NASNAMA DIJÎTAL/DIGITAL ID</Text>
<Text style={styles.footerValue}>{citizen.citizenId.replace(/-/g, '')}</Text>
</View>
<View style={styles.footerRow}>
<Text style={styles.footerLabel}>JIMARA VEKIRAN/ACCOUNT#</Text>
<Text style={styles.footerValue}>{citizen.qrCode.substring(0, 12)}</Text>
</View>
<View style={styles.footerRow}>
<Text style={styles.footerLabel}>DAXWAZ/ISSUED</Text>
<Text style={styles.footerValue}>{formatDate(citizen.approvedAt)}</Text>
</View>
</View>
<View style={styles.footerRight}>
<QRCode value={citizen.qrCode} size={70} backgroundColor="transparent" color="#FFFFFF" />
</View>
</View>
</View>
{/* Status Badge */}
<View style={styles.statusBadge}>
<Ionicons name="checkmark-circle" size={24} color={Colors.mint} />
<Text style={styles.statusText}>Verified Kurdistan Citizen</Text>
</View>
{/* Info Text */}
<Text style={styles.infoText}>
This official digital citizenship card grants you full access to Governance (Welati) and all citizen-only features on PezkuwiChain.
</Text>
{/* Action Buttons */}
<View style={styles.actions}>
<TouchableOpacity style={styles.actionButton}>
<Ionicons name="download-outline" size={24} color={Colors.teal} />
<Text style={styles.actionText}>Download</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton}>
<Ionicons name="qr-code-outline" size={24} color={Colors.teal} />
<Text style={styles.actionText}>Show QR</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton}>
<Ionicons name="share-social-outline" size={24} color={Colors.teal} />
<Text style={styles.actionText}>Share</Text>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.lg,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
},
backButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: Colors.card,
justifyContent: 'center',
alignItems: 'center',
},
shareButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: Colors.card,
justifyContent: 'center',
alignItems: 'center',
},
headerTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
},
content: {
flex: 1,
padding: Spacing.xl,
},
card: {
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.xlarge,
overflow: 'hidden',
...Shadow.medium,
},
cardHeader: {
backgroundColor: '#8B1538', // Burgundy
paddingVertical: Spacing.xl,
alignItems: 'center',
},
headerText1: {
fontSize: 20,
fontWeight: '700',
color: '#D4A017', // Gold
letterSpacing: 1,
},
headerText2: {
fontSize: 24,
fontWeight: '700',
color: '#FFFFFF',
letterSpacing: 2,
marginTop: 4,
},
cardBody: {
backgroundColor: '#FFFFFF',
padding: Spacing.lg,
},
topSection: {
flexDirection: 'row',
marginBottom: Spacing.md,
},
photoContainer: {
width: 100,
height: 100,
borderRadius: 50,
overflow: 'hidden',
backgroundColor: '#D0D0D0',
marginRight: Spacing.md,
},
photo: {
width: '100%',
height: '100%',
},
photoPlaceholder: {
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#D0D0D0',
},
infoList: {
flex: 1,
},
infoRow: {
marginBottom: 6,
},
infoLabel: {
fontSize: 9,
fontWeight: '600',
color: '#333',
letterSpacing: 0.3,
},
infoValue: {
fontSize: 11,
fontWeight: '700',
color: '#000',
},
sunContainer: {
alignItems: 'center',
marginVertical: Spacing.md,
},
middleSection: {
marginTop: Spacing.sm,
},
regionRow: {
marginBottom: Spacing.sm,
},
regionLabel: {
fontSize: 11,
fontWeight: '600',
color: '#333',
},
regionValue: {
fontSize: 13,
fontWeight: '700',
color: '#000',
},
citizenIdRow: {
marginTop: Spacing.sm,
},
citizenIdLabel: {
fontSize: 11,
fontWeight: '600',
color: '#333',
},
citizenIdValue: {
fontSize: 15,
fontWeight: '700',
color: '#000',
letterSpacing: 1,
},
cardFooter: {
backgroundColor: '#007A3D', // Green
flexDirection: 'row',
padding: Spacing.lg,
justifyContent: 'space-between',
},
footerLeft: {
flex: 1,
},
footerRow: {
marginBottom: 6,
},
footerLabel: {
fontSize: 9,
fontWeight: '600',
color: '#FFFFFF',
opacity: 0.9,
},
footerValue: {
fontSize: 11,
fontWeight: '700',
color: '#D4A017', // Gold
},
footerRight: {
justifyContent: 'center',
alignItems: 'center',
},
statusBadge: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.mint + '20',
padding: Spacing.md,
borderRadius: BorderRadius.large,
marginTop: Spacing.xl,
gap: Spacing.sm,
},
statusText: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.semibold,
color: Colors.mint,
},
infoText: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
textAlign: 'center',
marginTop: Spacing.lg,
lineHeight: 20,
},
actions: {
flexDirection: 'row',
justifyContent: 'space-around',
marginTop: Spacing.xl,
marginBottom: Spacing.xl,
},
actionButton: {
alignItems: 'center',
gap: Spacing.sm,
},
actionText: {
fontSize: Typography.sizes.small,
color: Colors.teal,
fontWeight: Typography.weights.medium,
},
errorText: {
fontSize: Typography.sizes.medium,
color: Colors.textGray,
textAlign: 'center',
},
button: {
backgroundColor: Colors.teal,
paddingVertical: Spacing.lg,
paddingHorizontal: Spacing.xxxl,
borderRadius: BorderRadius.xxlarge,
marginTop: Spacing.xl,
},
buttonText: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
},
});
@@ -0,0 +1,435 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
ScrollView,
SafeAreaView,
Alert,
ActivityIndicator,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Picker } from '@react-native-picker/picker';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
import { KYCFormData, Region, MaritalStatus, REGION_LABELS } from '../../types/kyc';
import { kycService } from '../../services/kycService';
export default function IdentityKYCFormScreen({ navigation }: any) {
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState<KYCFormData>({
fullName: '',
fatherName: '',
grandfatherName: '',
greatGrandfatherName: '',
motherName: '',
maritalStatus: 'single',
region: 'basur',
});
const updateField = (field: keyof KYCFormData, value: any) => {
setFormData({ ...formData, [field]: value });
};
const updateChild = (index: number, name: string) => {
const children = formData.children || [];
children[index] = { name, order: index + 1 };
setFormData({ ...formData, children });
};
const handleSubmit = async () => {
// Validation
if (!formData.fullName || !formData.fatherName || !formData.motherName) {
Alert.alert('Error', 'Please fill in all required fields');
return;
}
if (formData.maritalStatus === 'married' && !formData.spouseName) {
Alert.alert('Error', 'Please enter your spouse name');
return;
}
setLoading(true);
try {
// Submit KYC
const submission = await kycService.submitKYC(formData, null); // TODO: Add signer
Alert.alert(
'KYC Submitted',
'Your KYC application has been submitted for review. You will be notified once approved.',
[
{
text: 'OK',
onPress: () => navigation.goBack(),
},
]
);
} catch (error) {
Alert.alert('Error', 'Failed to submit KYC. Please try again.');
} finally {
setLoading(false);
}
};
const renderChildInputs = () => {
if (!formData.numberOfChildren || formData.numberOfChildren === 0) return null;
return Array.from({ length: formData.numberOfChildren }, (_, index) => (
<View key={index} style={styles.inputContainer}>
<Text style={styles.label}>{index + 1}. Child's Name</Text>
<View style={styles.inputWrapper}>
<Ionicons name="person-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder={`${index + 1}. child's name`}
placeholderTextColor={Colors.textLight}
value={formData.children?.[index]?.name || ''}
onChangeText={(text) => updateChild(index, text)}
/>
</View>
</View>
));
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
<Ionicons name="arrow-back" size={24} color={Colors.textDark} />
</TouchableOpacity>
<Text style={styles.headerTitle}>Identity KYC</Text>
<View style={{ width: 24 }} />
</View>
<ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
<View style={styles.infoCard}>
<Ionicons name="information-circle" size={24} color={Colors.teal} />
<Text style={styles.infoText}>
Complete this form to become a verified Kurdistan Digital Citizen and access Governance features.
</Text>
</View>
{/* Personal Information */}
<Text style={styles.sectionTitle}>Personal Information</Text>
<View style={styles.inputContainer}>
<Text style={styles.label}>Full Name *</Text>
<View style={styles.inputWrapper}>
<Ionicons name="person-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="Your full name"
placeholderTextColor={Colors.textLight}
value={formData.fullName}
onChangeText={(text) => updateField('fullName', text)}
/>
</View>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Father's Name *</Text>
<View style={styles.inputWrapper}>
<Ionicons name="man-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="Your father's name"
placeholderTextColor={Colors.textLight}
value={formData.fatherName}
onChangeText={(text) => updateField('fatherName', text)}
/>
</View>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Grandfather's Name</Text>
<View style={styles.inputWrapper}>
<Ionicons name="man-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="Your grandfather's name"
placeholderTextColor={Colors.textLight}
value={formData.grandfatherName}
onChangeText={(text) => updateField('grandfatherName', text)}
/>
</View>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Great-Grandfather's Name</Text>
<View style={styles.inputWrapper}>
<Ionicons name="man-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="Your great-grandfather's name"
placeholderTextColor={Colors.textLight}
value={formData.greatGrandfatherName}
onChangeText={(text) => updateField('greatGrandfatherName', text)}
/>
</View>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Mother's Name *</Text>
<View style={styles.inputWrapper}>
<Ionicons name="woman-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="Your mother's name"
placeholderTextColor={Colors.textLight}
value={formData.motherName}
onChangeText={(text) => updateField('motherName', text)}
/>
</View>
</View>
{/* Marital Status */}
<Text style={styles.sectionTitle}>Marital Status</Text>
<View style={styles.radioGroup}>
<TouchableOpacity
style={[
styles.radioButton,
formData.maritalStatus === 'single' && styles.radioButtonActive,
]}
onPress={() => updateField('maritalStatus', 'single')}
>
<Ionicons
name={formData.maritalStatus === 'single' ? 'radio-button-on' : 'radio-button-off'}
size={24}
color={formData.maritalStatus === 'single' ? Colors.teal : Colors.textGray}
/>
<Text style={styles.radioLabel}>Single</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.radioButton,
formData.maritalStatus === 'married' && styles.radioButtonActive,
]}
onPress={() => updateField('maritalStatus', 'married')}
>
<Ionicons
name={formData.maritalStatus === 'married' ? 'radio-button-on' : 'radio-button-off'}
size={24}
color={formData.maritalStatus === 'married' ? Colors.teal : Colors.textGray}
/>
<Text style={styles.radioLabel}>Married</Text>
</TouchableOpacity>
</View>
{formData.maritalStatus === 'married' && (
<>
<View style={styles.inputContainer}>
<Text style={styles.label}>Spouse's Name *</Text>
<View style={styles.inputWrapper}>
<Ionicons name="heart-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="Your spouse's name"
placeholderTextColor={Colors.textLight}
value={formData.spouseName}
onChangeText={(text) => updateField('spouseName', text)}
/>
</View>
</View>
<View style={styles.inputContainer}>
<Text style={styles.label}>Number of Children</Text>
<View style={styles.inputWrapper}>
<Ionicons name="people-outline" size={20} color={Colors.textGray} />
<TextInput
style={styles.input}
placeholder="0"
placeholderTextColor={Colors.textLight}
keyboardType="number-pad"
value={formData.numberOfChildren?.toString() || ''}
onChangeText={(text) => {
const num = parseInt(text) || 0;
updateField('numberOfChildren', num);
if (num > 0) {
updateField('children', Array(num).fill({ name: '', order: 0 }));
}
}}
/>
</View>
</View>
{renderChildInputs()}
</>
)}
{/* Region */}
<Text style={styles.sectionTitle}>Region</Text>
<View style={styles.inputContainer}>
<Text style={styles.label}>Select Your Region *</Text>
<View style={styles.pickerWrapper}>
<Picker
selectedValue={formData.region}
onValueChange={(value) => updateField('region', value)}
style={styles.picker}
>
{Object.entries(REGION_LABELS).map(([key, value]) => (
<Picker.Item key={key} label={value.en} value={key} />
))}
</Picker>
</View>
</View>
{/* Submit Button */}
<TouchableOpacity
style={[styles.submitButton, loading && styles.submitButtonDisabled]}
onPress={handleSubmit}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#FFFFFF" />
) : (
<>
<Text style={styles.submitButtonText}>Submit KYC Application</Text>
<Ionicons name="arrow-forward" size={20} color="#FFFFFF" />
</>
)}
</TouchableOpacity>
<View style={{ height: 40 }} />
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.lg,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
},
backButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: Colors.card,
justifyContent: 'center',
alignItems: 'center',
},
headerTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
},
scrollView: {
flex: 1,
paddingHorizontal: Spacing.xl,
},
infoCard: {
flexDirection: 'row',
backgroundColor: Colors.teal + '20',
padding: Spacing.lg,
borderRadius: BorderRadius.medium,
marginVertical: Spacing.lg,
gap: Spacing.md,
},
infoText: {
flex: 1,
fontSize: Typography.sizes.small,
color: Colors.textDark,
lineHeight: 20,
},
sectionTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
marginTop: Spacing.xl,
marginBottom: Spacing.md,
},
inputContainer: {
marginBottom: Spacing.lg,
},
label: {
fontSize: Typography.sizes.body,
fontWeight: Typography.weights.medium,
color: Colors.textDark,
marginBottom: Spacing.sm,
},
inputWrapper: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: Colors.card,
borderRadius: BorderRadius.medium,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
borderWidth: 1,
borderColor: '#E0E0E0',
},
input: {
flex: 1,
fontSize: Typography.sizes.medium,
color: Colors.textDark,
marginLeft: Spacing.sm,
},
radioGroup: {
flexDirection: 'row',
gap: Spacing.md,
marginBottom: Spacing.lg,
},
radioButton: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: Colors.card,
padding: Spacing.lg,
borderRadius: BorderRadius.medium,
borderWidth: 1,
borderColor: '#E0E0E0',
gap: Spacing.sm,
},
radioButtonActive: {
borderColor: Colors.teal,
backgroundColor: Colors.teal + '10',
},
radioLabel: {
fontSize: Typography.sizes.medium,
color: Colors.textDark,
},
pickerWrapper: {
backgroundColor: Colors.card,
borderRadius: BorderRadius.medium,
borderWidth: 1,
borderColor: '#E0E0E0',
overflow: 'hidden',
},
picker: {
height: 50,
},
submitButton: {
flexDirection: 'row',
backgroundColor: Colors.teal,
borderRadius: BorderRadius.xxlarge,
paddingVertical: Spacing.lg,
paddingHorizontal: Spacing.xxxl,
alignItems: 'center',
justifyContent: 'center',
marginTop: Spacing.xl,
gap: Spacing.sm,
...Shadow.soft,
},
submitButtonDisabled: {
opacity: 0.6,
},
submitButtonText: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
},
});
@@ -0,0 +1,316 @@
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, SafeAreaView, ActivityIndicator } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
import { kycService } from '../../services/kycService';
import { KYCStatus } from '../../types/kyc';
export default function IdentityScreen({ navigation }: any) {
const [loading, setLoading] = useState(true);
const [kycStatus, setKycStatus] = useState<KYCStatus>({
started: false,
submitted: false,
approved: false,
});
useEffect(() => {
loadKYCStatus();
}, []);
const loadKYCStatus = async () => {
try {
const status = await kycService.getKYCStatus();
setKycStatus(status);
} catch (error) {
console.error('Failed to load KYC status:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<SafeAreaView style={styles.container}>
<ActivityIndicator size="large" color={Colors.teal} />
</SafeAreaView>
);
}
// If KYC approved, show citizen card access
if (kycStatus.approved && kycStatus.citizen) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Digital Identity</Text>
</View>
<View style={styles.content}>
<View style={styles.approvedCard}>
<Ionicons name="checkmark-circle" size={60} color={Colors.mint} />
<Text style={styles.approvedTitle}>KYC Approved</Text>
<Text style={styles.approvedSubtitle}>You are a verified Kurdistan Digital Citizen</Text>
</View>
<TouchableOpacity
style={styles.viewCardButton}
onPress={() => navigation.navigate('CitizenCard')}
>
<Ionicons name="card-outline" size={24} color="#FFFFFF" />
<Text style={styles.viewCardButtonText}>View Citizen Card</Text>
</TouchableOpacity>
<View style={styles.infoBox}>
<Ionicons name="information-circle-outline" size={24} color={Colors.teal} />
<Text style={styles.infoText}>
Your citizen card grants you access to Governance (Welati) and other citizen-only features.
</Text>
</View>
</View>
</SafeAreaView>
);
}
// If KYC submitted but not approved
if (kycStatus.submitted && !kycStatus.approved) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Digital Identity</Text>
</View>
<View style={styles.content}>
<View style={styles.pendingCard}>
<Ionicons name="time-outline" size={60} color={Colors.gold} />
<Text style={styles.pendingTitle}>KYC Under Review</Text>
<Text style={styles.pendingSubtitle}>
Your application is being reviewed. You will be notified once approved.
</Text>
</View>
<TouchableOpacity style={styles.refreshButton} onPress={loadKYCStatus}>
<Ionicons name="refresh-outline" size={20} color={Colors.teal} />
<Text style={styles.refreshButtonText}>Check Status</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
// If KYC not started
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Digital Identity</Text>
</View>
<View style={styles.content}>
<View style={styles.welcomeCard}>
<Ionicons name="person-add-outline" size={60} color={Colors.teal} />
<Text style={styles.welcomeTitle}>Become a Digital Citizen</Text>
<Text style={styles.welcomeSubtitle}>
Complete KYC verification to access Governance (Welati) and become a verified Kurdistan Digital Citizen.
</Text>
</View>
<View style={styles.benefitsCard}>
<Text style={styles.benefitsTitle}>Benefits:</Text>
<View style={styles.benefitItem}>
<Ionicons name="checkmark-circle" size={20} color={Colors.mint} />
<Text style={styles.benefitText}>Vote on governance proposals</Text>
</View>
<View style={styles.benefitItem}>
<Ionicons name="checkmark-circle" size={20} color={Colors.mint} />
<Text style={styles.benefitText}>Access Parliamentary NFT elections</Text>
</View>
<View style={styles.benefitItem}>
<Ionicons name="checkmark-circle" size={20} color={Colors.mint} />
<Text style={styles.benefitText}>Digital citizenship certificate</Text>
</View>
<View style={styles.benefitItem}>
<Ionicons name="checkmark-circle" size={20} color={Colors.mint} />
<Text style={styles.benefitText}>Verified trust score</Text>
</View>
</View>
<TouchableOpacity
style={styles.startButton}
onPress={() => navigation.navigate('IdentityKYCForm')}
>
<Text style={styles.startButtonText}>Start KYC Verification</Text>
<Ionicons name="arrow-forward" size={20} color="#FFFFFF" />
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
padding: Spacing.xl,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
},
title: {
fontSize: 28,
fontWeight: '700',
color: Colors.textDark,
},
content: {
flex: 1,
padding: Spacing.xl,
},
welcomeCard: {
backgroundColor: Colors.card,
padding: Spacing.xxxl,
borderRadius: BorderRadius.xlarge,
alignItems: 'center',
...Shadow.soft,
},
welcomeTitle: {
fontSize: 24,
fontWeight: '700',
color: Colors.textDark,
marginTop: Spacing.lg,
textAlign: 'center',
},
welcomeSubtitle: {
fontSize: 16,
color: Colors.textGray,
marginTop: Spacing.md,
textAlign: 'center',
lineHeight: 24,
},
benefitsCard: {
backgroundColor: Colors.mint + '20',
padding: Spacing.xl,
borderRadius: BorderRadius.large,
marginTop: Spacing.xl,
},
benefitsTitle: {
fontSize: 18,
fontWeight: '600',
color: Colors.textDark,
marginBottom: Spacing.md,
},
benefitItem: {
flexDirection: 'row',
alignItems: 'center',
marginTop: Spacing.sm,
gap: Spacing.sm,
},
benefitText: {
fontSize: 16,
color: Colors.textDark,
},
startButton: {
flexDirection: 'row',
backgroundColor: Colors.teal,
borderRadius: BorderRadius.xxlarge,
paddingVertical: Spacing.lg,
paddingHorizontal: Spacing.xxxl,
alignItems: 'center',
justifyContent: 'center',
marginTop: Spacing.xl,
gap: Spacing.sm,
...Shadow.soft,
},
startButtonText: {
fontSize: 18,
fontWeight: '600',
color: '#FFFFFF',
},
approvedCard: {
backgroundColor: Colors.mint + '20',
padding: Spacing.xxxl,
borderRadius: BorderRadius.xlarge,
alignItems: 'center',
...Shadow.soft,
},
approvedTitle: {
fontSize: 24,
fontWeight: '700',
color: Colors.mint,
marginTop: Spacing.lg,
},
approvedSubtitle: {
fontSize: 16,
color: Colors.textGray,
marginTop: Spacing.sm,
textAlign: 'center',
},
viewCardButton: {
flexDirection: 'row',
backgroundColor: Colors.teal,
borderRadius: BorderRadius.xxlarge,
paddingVertical: Spacing.lg,
paddingHorizontal: Spacing.xxxl,
alignItems: 'center',
justifyContent: 'center',
marginTop: Spacing.xl,
gap: Spacing.sm,
...Shadow.soft,
},
viewCardButtonText: {
fontSize: 18,
fontWeight: '600',
color: '#FFFFFF',
},
infoBox: {
flexDirection: 'row',
backgroundColor: Colors.teal + '20',
padding: Spacing.lg,
borderRadius: BorderRadius.medium,
marginTop: Spacing.xl,
gap: Spacing.md,
},
infoText: {
flex: 1,
fontSize: 14,
color: Colors.textDark,
lineHeight: 20,
},
pendingCard: {
backgroundColor: Colors.gold + '20',
padding: Spacing.xxxl,
borderRadius: BorderRadius.xlarge,
alignItems: 'center',
...Shadow.soft,
},
pendingTitle: {
fontSize: 24,
fontWeight: '700',
color: Colors.gold,
marginTop: Spacing.lg,
},
pendingSubtitle: {
fontSize: 16,
color: Colors.textGray,
marginTop: Spacing.sm,
textAlign: 'center',
lineHeight: 24,
},
refreshButton: {
flexDirection: 'row',
backgroundColor: Colors.card,
borderRadius: BorderRadius.large,
paddingVertical: Spacing.md,
paddingHorizontal: Spacing.xl,
alignItems: 'center',
justifyContent: 'center',
marginTop: Spacing.xl,
gap: Spacing.sm,
borderWidth: 1,
borderColor: Colors.teal,
},
refreshButtonText: {
fontSize: 16,
fontWeight: '600',
color: Colors.teal,
},
});
@@ -0,0 +1,410 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
ScrollView,
Switch,
Alert,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
interface MenuItem {
id: string;
icon: keyof typeof Ionicons.glyphMap;
title: string;
subtitle?: string;
onPress: () => void;
showArrow?: boolean;
showSwitch?: boolean;
switchValue?: boolean;
}
export default function ProfileScreen({ navigation }: any) {
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
const [biometricEnabled, setBiometricEnabled] = useState(false);
const accountMenuItems: MenuItem[] = [
{
id: 'edit-profile',
icon: 'person-outline',
title: 'Edit Profile',
subtitle: 'Update your personal information',
onPress: () => Alert.alert('Edit Profile', 'Coming soon'),
showArrow: true,
},
{
id: 'identity',
icon: 'card-outline',
title: 'Digital Identity',
subtitle: 'View your Kurdistan Citizen Card',
onPress: () => navigation.navigate('Identity'),
showArrow: true,
},
{
id: 'trust-score',
icon: 'star-outline',
title: 'Trust Score',
subtitle: '750 points',
onPress: () => navigation.navigate('TrustScore'),
showArrow: true,
},
];
const securityMenuItems: MenuItem[] = [
{
id: 'change-password',
icon: 'lock-closed-outline',
title: 'Change Password',
onPress: () => Alert.alert('Change Password', 'Coming soon'),
showArrow: true,
},
{
id: 'biometric',
icon: 'finger-print-outline',
title: 'Biometric Authentication',
onPress: () => {},
showSwitch: true,
switchValue: biometricEnabled,
},
{
id: 'backup',
icon: 'cloud-upload-outline',
title: 'Backup Wallet',
subtitle: 'Secure your recovery phrase',
onPress: () => Alert.alert('Backup Wallet', 'This feature will help you backup your wallet securely.'),
showArrow: true,
},
];
const preferencesMenuItems: MenuItem[] = [
{
id: 'notifications',
icon: 'notifications-outline',
title: 'Push Notifications',
onPress: () => {},
showSwitch: true,
switchValue: notificationsEnabled,
},
{
id: 'language',
icon: 'language-outline',
title: 'Language',
subtitle: 'English',
onPress: () => navigation.navigate('LanguageSelection'),
showArrow: true,
},
{
id: 'currency',
icon: 'cash-outline',
title: 'Currency',
subtitle: 'USD',
onPress: () => Alert.alert('Currency', 'Currency selection coming soon'),
showArrow: true,
},
];
const supportMenuItems: MenuItem[] = [
{
id: 'help',
icon: 'help-circle-outline',
title: 'Help Center',
onPress: () => Alert.alert('Help Center', 'Visit help.pezkuwichain.io'),
showArrow: true,
},
{
id: 'about',
icon: 'information-circle-outline',
title: 'About PezkuwiChain',
subtitle: 'Version 1.0.0',
onPress: () => Alert.alert('About', 'PezkuwiChain v1.0.0\nBuilding the future of Kurdish digital infrastructure'),
showArrow: true,
},
{
id: 'terms',
icon: 'document-text-outline',
title: 'Terms & Privacy',
onPress: () => Alert.alert('Terms', 'View terms and privacy policy'),
showArrow: true,
},
];
const handleLogout = () => {
Alert.alert(
'Logout',
'Are you sure you want to logout?',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Logout',
style: 'destructive',
onPress: () => navigation.navigate('LanguageSelection'),
},
]
);
};
const renderMenuItem = (item: MenuItem) => (
<TouchableOpacity
key={item.id}
style={styles.menuItem}
onPress={item.onPress}
activeOpacity={0.7}
>
<View style={styles.menuIconContainer}>
<Ionicons name={item.icon} size={24} color={Colors.teal} />
</View>
<View style={styles.menuContent}>
<Text style={styles.menuTitle}>{item.title}</Text>
{item.subtitle && <Text style={styles.menuSubtitle}>{item.subtitle}</Text>}
</View>
{item.showArrow && (
<Ionicons name="chevron-forward" size={20} color={Colors.textGray} />
)}
{item.showSwitch && (
<Switch
value={item.switchValue}
onValueChange={(value) => {
if (item.id === 'notifications') {
setNotificationsEnabled(value);
} else if (item.id === 'biometric') {
setBiometricEnabled(value);
}
}}
trackColor={{ false: '#D0D0D0', true: Colors.teal }}
thumbColor="#FFFFFF"
/>
)}
</TouchableOpacity>
);
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* Profile Header */}
<LinearGradient
colors={Colors.gradients.header}
style={styles.header}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.profileInfo}>
<View style={styles.avatar}>
<Ionicons name="person" size={40} color="#FFFFFF" />
</View>
<View style={styles.userInfo}>
<Text style={styles.userName}>Satoshi Qazi Muhammed</Text>
<Text style={styles.userEmail}>satoshi@pezkuwichain.io</Text>
</View>
<TouchableOpacity
style={styles.editButton}
onPress={() => Alert.alert('Edit Profile', 'Coming soon')}
>
<Ionicons name="create-outline" size={20} color="#FFFFFF" />
</TouchableOpacity>
</View>
{/* Stats */}
<View style={styles.statsContainer}>
<View style={styles.statItem}>
<Text style={styles.statValue}>750</Text>
<Text style={styles.statLabel}>Trust Score</Text>
</View>
<View style={styles.statDivider} />
<View style={styles.statItem}>
<Text style={styles.statValue}>24</Text>
<Text style={styles.statLabel}>Referrals</Text>
</View>
<View style={styles.statDivider} />
<View style={styles.statItem}>
<Text style={styles.statValue}>5</Text>
<Text style={styles.statLabel}>Certificates</Text>
</View>
</View>
</LinearGradient>
{/* Account Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Account</Text>
{accountMenuItems.map(renderMenuItem)}
</View>
{/* Security Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Security</Text>
{securityMenuItems.map(renderMenuItem)}
</View>
{/* Preferences Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Preferences</Text>
{preferencesMenuItems.map(renderMenuItem)}
</View>
{/* Support Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Support</Text>
{supportMenuItems.map(renderMenuItem)}
</View>
{/* Logout Button */}
<TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
<Ionicons name="log-out-outline" size={24} color={Colors.coral} />
<Text style={styles.logoutText}>Logout</Text>
</TouchableOpacity>
<View style={{ height: 40 }} />
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
paddingHorizontal: Spacing.xl,
paddingTop: Spacing.xxxl,
paddingBottom: Spacing.xl,
borderBottomLeftRadius: BorderRadius.xxlarge,
borderBottomRightRadius: BorderRadius.xxlarge,
},
profileInfo: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: Spacing.xl,
},
avatar: {
width: 70,
height: 70,
borderRadius: 35,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
justifyContent: 'center',
alignItems: 'center',
borderWidth: 3,
borderColor: '#FFFFFF',
},
userInfo: {
flex: 1,
marginLeft: Spacing.lg,
},
userName: {
fontSize: Typography.sizes.xlarge,
fontWeight: Typography.weights.bold,
color: '#FFFFFF',
marginBottom: 4,
},
userEmail: {
fontSize: Typography.sizes.small,
color: 'rgba(255, 255, 255, 0.9)',
},
editButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
justifyContent: 'center',
alignItems: 'center',
},
statsContainer: {
flexDirection: 'row',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
borderRadius: BorderRadius.large,
padding: Spacing.lg,
},
statItem: {
flex: 1,
alignItems: 'center',
},
statValue: {
fontSize: 24,
fontWeight: Typography.weights.bold,
color: '#FFFFFF',
marginBottom: 4,
},
statLabel: {
fontSize: Typography.sizes.small,
color: 'rgba(255, 255, 255, 0.9)',
},
statDivider: {
width: 1,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
marginHorizontal: Spacing.md,
},
section: {
marginTop: Spacing.xl,
paddingHorizontal: Spacing.xl,
},
sectionTitle: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.semibold,
color: Colors.textGray,
marginBottom: Spacing.md,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
menuItem: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: Colors.card,
padding: Spacing.lg,
borderRadius: BorderRadius.large,
marginBottom: Spacing.sm,
...Shadow.small,
},
menuIconContainer: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: Colors.teal + '20',
justifyContent: 'center',
alignItems: 'center',
marginRight: Spacing.md,
},
menuContent: {
flex: 1,
},
menuTitle: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.medium,
color: Colors.textDark,
marginBottom: 2,
},
menuSubtitle: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
},
logoutButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.coral + '20',
marginHorizontal: Spacing.xl,
marginTop: Spacing.xxxl,
padding: Spacing.lg,
borderRadius: BorderRadius.large,
gap: Spacing.sm,
},
logoutText: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.semibold,
color: Colors.coral,
},
});
@@ -0,0 +1,18 @@
import React from 'react';
import { View, Text, StyleSheet, SafeAreaView } from 'react-native';
import Colors from '../../constants/colors';
export default function ProfileScreen() {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>Profile</Text>
<Text style={styles.subtitle}>Coming soon...</Text>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: Colors.background, justifyContent: 'center', alignItems: 'center' },
title: { fontSize: 28, fontWeight: '700', color: Colors.textDark, marginBottom: 8 },
subtitle: { fontSize: 16, color: Colors.textGray },
});
@@ -0,0 +1,378 @@
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
ScrollView,
ActivityIndicator,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
interface TrustActivity {
id: string;
type: 'vote' | 'proposal' | 'stake' | 'community';
description: string;
points: number;
timestamp: number;
}
export default function TrustScoreScreen({ navigation }: any) {
const [loading, setLoading] = useState(true);
const [trustScore] = useState(750);
const [activities, setActivities] = useState<TrustActivity[]>([]);
useEffect(() => {
loadTrustData();
}, []);
const loadTrustData = async () => {
// Mock data - replace with blockchain data
setTimeout(() => {
setActivities([
{
id: '1',
type: 'vote',
description: 'Voted on Proposal #42',
points: 10,
timestamp: Date.now() - 86400000,
},
{
id: '2',
type: 'stake',
description: 'Staked 10,000 HEZ',
points: 50,
timestamp: Date.now() - 172800000,
},
{
id: '3',
type: 'proposal',
description: 'Created Proposal #45',
points: 25,
timestamp: Date.now() - 259200000,
},
{
id: '4',
type: 'community',
description: 'Referred 3 new citizens',
points: 30,
timestamp: Date.now() - 345600000,
},
]);
setLoading(false);
}, 1000);
};
const getActivityIcon = (type: string) => {
switch (type) {
case 'vote':
return 'checkmark-circle';
case 'proposal':
return 'bulb';
case 'stake':
return 'lock-closed';
case 'community':
return 'people';
default:
return 'star';
}
};
const getActivityColor = (type: string) => {
switch (type) {
case 'vote':
return Colors.mint;
case 'proposal':
return Colors.peach;
case 'stake':
return Colors.blue;
case 'community':
return Colors.lavender;
default:
return Colors.teal;
}
};
const formatDate = (timestamp: number) => {
const date = new Date(timestamp);
const now = new Date();
const diff = now.getTime() - date.getTime();
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
if (days === 0) return 'Today';
if (days === 1) return 'Yesterday';
return `${days} days ago`;
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
<Ionicons name="arrow-back" size={24} color={Colors.textDark} />
</TouchableOpacity>
<Text style={styles.headerTitle}>Trust Score</Text>
<View style={{ width: 40 }} />
</View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
{/* Trust Score Card */}
<LinearGradient
colors={[Colors.gold, '#E8C896']}
style={styles.scoreCard}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<Ionicons name="star" size={60} color="#FFFFFF" />
<Text style={styles.scoreValue}>{trustScore}</Text>
<Text style={styles.scoreLabel}>Your Trust Score</Text>
<View style={styles.scoreBadge}>
<Text style={styles.badgeText}>🏆 Gold Tier</Text>
</View>
</LinearGradient>
{/* Benefits */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Benefits</Text>
<View style={styles.benefitCard}>
<Ionicons name="trending-up" size={24} color={Colors.mint} />
<View style={styles.benefitText}>
<Text style={styles.benefitTitle}>Higher PEZ Rewards</Text>
<Text style={styles.benefitDescription}>
Earn 1.5x more PEZ rewards from staking
</Text>
</View>
</View>
<View style={styles.benefitCard}>
<Ionicons name="shield-checkmark" size={24} color={Colors.blue} />
<View style={styles.benefitText}>
<Text style={styles.benefitTitle}>Validator Priority</Text>
<Text style={styles.benefitDescription}>
Higher chance of being selected as validator
</Text>
</View>
</View>
<View style={styles.benefitCard}>
<Ionicons name="megaphone" size={24} color={Colors.peach} />
<View style={styles.benefitText}>
<Text style={styles.benefitTitle}>Governance Weight</Text>
<Text style={styles.benefitDescription}>
Your votes carry more weight in proposals
</Text>
</View>
</View>
</View>
{/* Recent Activities */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Recent Activities</Text>
{loading ? (
<ActivityIndicator size="large" color={Colors.teal} />
) : (
activities.map((activity) => (
<View key={activity.id} style={styles.activityCard}>
<View
style={[
styles.activityIcon,
{ backgroundColor: getActivityColor(activity.type) + '20' },
]}
>
<Ionicons
name={getActivityIcon(activity.type)}
size={24}
color={getActivityColor(activity.type)}
/>
</View>
<View style={styles.activityInfo}>
<Text style={styles.activityDescription}>{activity.description}</Text>
<Text style={styles.activityDate}>{formatDate(activity.timestamp)}</Text>
</View>
<View style={styles.activityPoints}>
<Text style={styles.pointsValue}>+{activity.points}</Text>
<Text style={styles.pointsLabel}>points</Text>
</View>
</View>
))
)}
</View>
{/* How to Increase */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>How to Increase Trust Score</Text>
<View style={styles.tipCard}>
<Text style={styles.tipText}> Vote on governance proposals</Text>
</View>
<View style={styles.tipCard}>
<Text style={styles.tipText}> Stake HEZ tokens</Text>
</View>
<View style={styles.tipCard}>
<Text style={styles.tipText}> Create valuable proposals</Text>
</View>
<View style={styles.tipCard}>
<Text style={styles.tipText}> Refer new citizens</Text>
</View>
<View style={styles.tipCard}>
<Text style={styles.tipText}> Maintain long-term participation</Text>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: Spacing.xl,
paddingVertical: Spacing.lg,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
},
backButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: Colors.card,
justifyContent: 'center',
alignItems: 'center',
},
headerTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
},
content: {
flex: 1,
padding: Spacing.xl,
},
scoreCard: {
borderRadius: BorderRadius.xlarge,
padding: Spacing.xxxl,
alignItems: 'center',
...Shadow.large,
},
scoreValue: {
fontSize: 64,
fontWeight: '700',
color: '#FFFFFF',
marginTop: Spacing.md,
},
scoreLabel: {
fontSize: Typography.sizes.medium,
color: '#FFFFFF',
opacity: 0.9,
},
scoreBadge: {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
paddingHorizontal: Spacing.lg,
paddingVertical: Spacing.sm,
borderRadius: BorderRadius.xxlarge,
marginTop: Spacing.lg,
},
badgeText: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.semibold,
color: '#FFFFFF',
},
section: {
marginTop: Spacing.xxxl,
},
sectionTitle: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
marginBottom: Spacing.lg,
},
benefitCard: {
flexDirection: 'row',
backgroundColor: Colors.card,
padding: Spacing.lg,
borderRadius: BorderRadius.large,
marginBottom: Spacing.md,
...Shadow.small,
},
benefitText: {
flex: 1,
marginLeft: Spacing.md,
},
benefitTitle: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.semibold,
color: Colors.textDark,
marginBottom: 4,
},
benefitDescription: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
},
activityCard: {
flexDirection: 'row',
backgroundColor: Colors.card,
padding: Spacing.lg,
borderRadius: BorderRadius.large,
marginBottom: Spacing.md,
alignItems: 'center',
...Shadow.small,
},
activityIcon: {
width: 50,
height: 50,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
},
activityInfo: {
flex: 1,
marginLeft: Spacing.md,
},
activityDescription: {
fontSize: Typography.sizes.medium,
fontWeight: Typography.weights.medium,
color: Colors.textDark,
marginBottom: 4,
},
activityDate: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
},
activityPoints: {
alignItems: 'flex-end',
},
pointsValue: {
fontSize: Typography.sizes.large,
fontWeight: Typography.weights.semibold,
color: Colors.mint,
},
pointsLabel: {
fontSize: Typography.sizes.small,
color: Colors.textGray,
},
tipCard: {
backgroundColor: Colors.mint + '10',
padding: Spacing.md,
borderRadius: BorderRadius.medium,
marginBottom: Spacing.sm,
},
tipText: {
fontSize: Typography.sizes.medium,
color: Colors.textDark,
},
});
@@ -0,0 +1,587 @@
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
SafeAreaView,
ScrollView,
TouchableOpacity,
Share,
Alert,
} from 'react-native';
import * as Clipboard from 'expo-clipboard';
import QRCode from 'react-native-qrcode-svg';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
interface ReferralStats {
totalReferrals: number;
activeReferrals: number;
totalRewards: number;
pendingRewards: number;
}
interface Referral {
id: string;
name: string;
joinDate: string;
status: 'active' | 'pending' | 'inactive';
rewardEarned: number;
tier: number;
}
export default function ReferralScreen() {
const [referralCode] = useState('PKW-2024-KURD-5X7Y');
const [stats] = useState<ReferralStats>({
totalReferrals: 12,
activeReferrals: 8,
totalRewards: 2450,
pendingRewards: 350,
});
const [referrals] = useState<Referral[]>([
{
id: '1',
name: 'Ahmed Karwan',
joinDate: '2024-01-15',
status: 'active',
rewardEarned: 500,
tier: 1,
},
{
id: '2',
name: 'Layla Sherzad',
joinDate: '2024-01-20',
status: 'active',
rewardEarned: 500,
tier: 1,
},
{
id: '3',
name: 'Saman Aziz',
joinDate: '2024-02-05',
status: 'active',
rewardEarned: 250,
tier: 2,
},
{
id: '4',
name: 'Hana Dilshad',
joinDate: '2024-02-18',
status: 'pending',
rewardEarned: 0,
tier: 1,
},
]);
const copyToClipboard = async () => {
await Clipboard.setStringAsync(referralCode);
Alert.alert('✓ Copied!', 'Referral code copied to clipboard');
};
const shareReferralCode = async () => {
try {
await Share.share({
message: `Join PezkuwiChain - Digital Kurdistan's Blockchain Platform!\n\nUse my referral code: ${referralCode}\n\nEarn rewards and be part of building Digital Kurdistan! 🦅`,
});
} catch (error) {
console.error('Error sharing:', error);
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'active':
return Colors.success;
case 'pending':
return Colors.warning;
case 'inactive':
return Colors.textGray;
default:
return Colors.textGray;
}
};
const getTierReward = (tier: number) => {
switch (tier) {
case 1:
return '500 PEZ';
case 2:
return '250 PEZ';
case 3:
return '100 PEZ';
default:
return '50 PEZ';
}
};
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{/* Header */}
<View style={styles.header}>
<Text style={styles.headerTitle}>Referral Program</Text>
<Text style={styles.headerSubtitle}>
Invite friends and earn rewards together
</Text>
</View>
{/* Referral Code Card */}
<View style={styles.codeCard}>
<Text style={styles.codeLabel}>Your Referral Code</Text>
<View style={styles.qrContainer}>
<QRCode value={referralCode} size={120} />
</View>
<View style={styles.codeBox}>
<Text style={styles.codeText}>{referralCode}</Text>
</View>
<View style={styles.actionButtons}>
<TouchableOpacity
style={styles.actionButton}
onPress={copyToClipboard}
>
<Ionicons name="copy-outline" size={20} color={Colors.primary} />
<Text style={styles.actionButtonText}>Copy Code</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionButton, styles.shareButton]}
onPress={shareReferralCode}
>
<Ionicons name="share-social-outline" size={20} color="white" />
<Text style={styles.shareButtonText}>Share</Text>
</TouchableOpacity>
</View>
</View>
{/* Statistics Grid */}
<View style={styles.statsGrid}>
<View style={styles.statCard}>
<Ionicons name="people-outline" size={28} color={Colors.primary} />
<Text style={styles.statValue}>{stats.totalReferrals}</Text>
<Text style={styles.statLabel}>Total Referrals</Text>
</View>
<View style={styles.statCard}>
<Ionicons
name="checkmark-circle-outline"
size={28}
color={Colors.success}
/>
<Text style={styles.statValue}>{stats.activeReferrals}</Text>
<Text style={styles.statLabel}>Active</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="trophy-outline" size={28} color={Colors.kurdishGold} />
<Text style={styles.statValue}>{stats.totalRewards}</Text>
<Text style={styles.statLabel}>Total Rewards (PEZ)</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="time-outline" size={28} color={Colors.warning} />
<Text style={styles.statValue}>{stats.pendingRewards}</Text>
<Text style={styles.statLabel}>Pending (PEZ)</Text>
</View>
</View>
{/* Reward Tiers */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Reward Tiers</Text>
<View style={styles.tierCard}>
<View style={styles.tierRow}>
<View style={styles.tierBadge}>
<Text style={styles.tierBadgeText}>Tier 1</Text>
</View>
<Text style={styles.tierDescription}>Direct Referrals</Text>
<Text style={styles.tierReward}>500 PEZ</Text>
</View>
<View style={styles.tierRow}>
<View style={[styles.tierBadge, { backgroundColor: Colors.success }]}>
<Text style={styles.tierBadgeText}>Tier 2</Text>
</View>
<Text style={styles.tierDescription}>2nd Level Referrals</Text>
<Text style={styles.tierReward}>250 PEZ</Text>
</View>
<View style={styles.tierRow}>
<View style={[styles.tierBadge, { backgroundColor: Colors.kurdishGold }]}>
<Text style={styles.tierBadgeText}>Tier 3</Text>
</View>
<Text style={styles.tierDescription}>3rd Level Referrals</Text>
<Text style={styles.tierReward}>100 PEZ</Text>
</View>
</View>
</View>
{/* Referral List */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Your Referrals</Text>
{referrals.map((referral) => (
<View key={referral.id} style={styles.referralCard}>
<View style={styles.referralHeader}>
<View style={styles.avatarCircle}>
<Text style={styles.avatarText}>
{referral.name.charAt(0)}
</Text>
</View>
<View style={styles.referralInfo}>
<Text style={styles.referralName}>{referral.name}</Text>
<Text style={styles.referralDate}>
Joined: {referral.joinDate}
</Text>
</View>
<View
style={[
styles.statusBadge,
{ backgroundColor: getStatusColor(referral.status) + '20' },
]}
>
<Text
style={[
styles.statusText,
{ color: getStatusColor(referral.status) },
]}
>
{referral.status.charAt(0).toUpperCase() +
referral.status.slice(1)}
</Text>
</View>
</View>
<View style={styles.referralFooter}>
<View style={styles.tierInfo}>
<Ionicons
name="ribbon-outline"
size={16}
color={Colors.primary}
/>
<Text style={styles.tierText}>Tier {referral.tier}</Text>
</View>
<Text style={styles.rewardText}>
Earned: {referral.rewardEarned} PEZ
</Text>
</View>
</View>
))}
</View>
{/* How It Works */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>How It Works</Text>
<View style={styles.infoCard}>
<View style={styles.infoRow}>
<View style={styles.stepNumber}>
<Text style={styles.stepNumberText}>1</Text>
</View>
<Text style={styles.infoText}>
Share your unique referral code with friends
</Text>
</View>
<View style={styles.infoRow}>
<View style={styles.stepNumber}>
<Text style={styles.stepNumberText}>2</Text>
</View>
<Text style={styles.infoText}>
They sign up and complete KYC verification
</Text>
</View>
<View style={styles.infoRow}>
<View style={styles.stepNumber}>
<Text style={styles.stepNumberText}>3</Text>
</View>
<Text style={styles.infoText}>
Both of you earn PEZ rewards based on tier level
</Text>
</View>
<View style={styles.infoRow}>
<View style={styles.stepNumber}>
<Text style={styles.stepNumberText}>4</Text>
</View>
<Text style={styles.infoText}>
Rewards are distributed automatically to your wallet
</Text>
</View>
</View>
</View>
<View style={{ height: 40 }} />
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
paddingHorizontal: 20,
paddingTop: 20,
paddingBottom: 16,
},
headerTitle: {
fontSize: 28,
fontWeight: '700',
color: Colors.textDark,
marginBottom: 4,
},
headerSubtitle: {
fontSize: 15,
color: Colors.textGray,
},
codeCard: {
backgroundColor: 'white',
marginHorizontal: 20,
marginBottom: 20,
borderRadius: 16,
padding: 24,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
},
codeLabel: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 20,
},
qrContainer: {
padding: 16,
backgroundColor: Colors.background,
borderRadius: 12,
marginBottom: 20,
},
codeBox: {
backgroundColor: Colors.background,
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 10,
marginBottom: 20,
},
codeText: {
fontSize: 18,
fontWeight: '700',
color: Colors.primary,
letterSpacing: 1,
},
actionButtons: {
flexDirection: 'row',
gap: 12,
width: '100%',
},
actionButton: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
borderRadius: 10,
backgroundColor: Colors.background,
gap: 6,
},
actionButtonText: {
fontSize: 15,
fontWeight: '600',
color: Colors.primary,
},
shareButton: {
backgroundColor: Colors.primary,
},
shareButtonText: {
fontSize: 15,
fontWeight: '600',
color: 'white',
},
statsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
paddingHorizontal: 20,
gap: 12,
marginBottom: 24,
},
statCard: {
flex: 1,
minWidth: '47%',
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
statValue: {
fontSize: 24,
fontWeight: '700',
color: Colors.textDark,
marginTop: 8,
marginBottom: 4,
},
statLabel: {
fontSize: 13,
color: Colors.textGray,
textAlign: 'center',
},
section: {
paddingHorizontal: 20,
marginBottom: 24,
},
sectionTitle: {
fontSize: 20,
fontWeight: '700',
color: Colors.textDark,
marginBottom: 12,
},
tierCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
gap: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
tierRow: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
tierBadge: {
backgroundColor: Colors.primary,
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 8,
minWidth: 70,
alignItems: 'center',
},
tierBadgeText: {
fontSize: 13,
fontWeight: '700',
color: 'white',
},
tierDescription: {
flex: 1,
fontSize: 14,
color: Colors.textDark,
},
tierReward: {
fontSize: 15,
fontWeight: '700',
color: Colors.kurdishGold,
},
referralCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 16,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
referralHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
avatarCircle: {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: Colors.primary + '20',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
avatarText: {
fontSize: 18,
fontWeight: '700',
color: Colors.primary,
},
referralInfo: {
flex: 1,
},
referralName: {
fontSize: 16,
fontWeight: '600',
color: Colors.textDark,
marginBottom: 2,
},
referralDate: {
fontSize: 13,
color: Colors.textGray,
},
statusBadge: {
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 8,
},
statusText: {
fontSize: 12,
fontWeight: '600',
},
referralFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: 12,
borderTopWidth: 1,
borderTopColor: Colors.background,
},
tierInfo: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
},
tierText: {
fontSize: 14,
color: Colors.textDark,
fontWeight: '500',
},
rewardText: {
fontSize: 14,
fontWeight: '600',
color: Colors.success,
},
infoCard: {
backgroundColor: 'white',
borderRadius: 14,
padding: 20,
gap: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 6,
elevation: 1,
},
infoRow: {
flexDirection: 'row',
alignItems: 'flex-start',
gap: 12,
},
stepNumber: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: Colors.primary,
alignItems: 'center',
justifyContent: 'center',
},
stepNumberText: {
fontSize: 14,
fontWeight: '700',
color: 'white',
},
infoText: {
flex: 1,
fontSize: 14,
color: Colors.textDark,
lineHeight: 20,
paddingTop: 4,
},
});
@@ -0,0 +1,501 @@
import React, { useState, useEffect } from 'react';
import SendModal from '../../components/SendModal';
import ReceiveModal from '../../components/ReceiveModal';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
ScrollView,
TextInput,
Image,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
interface CoinRow {
symbol: string;
name: string;
amount: string;
network: string;
icon: any;
color: string;
}
export default function WalletScreen({ navigation, route }: any) {
const [withdrawalAddresses, setWithdrawalAddresses] = useState<{ [key: string]: string }>({});
const [sendModalVisible, setSendModalVisible] = useState(false);
const [receiveModalVisible, setReceiveModalVisible] = useState(false);
const [selectedToken, setSelectedToken] = useState<CoinRow | null>(null);
const [totalReward] = useState('50000');
const [trustScore] = useState('150000');
const [totalReferral] = useState('500');
const [circleScore] = useState('30000');
const [eduScore] = useState('1000');
const [stakeAmount] = useState('1000');
const coins: CoinRow[] = [
{
symbol: 'PEZ',
name: 'PezkuwiChain',
amount: '100',
network: 'op',
icon: require('../../../assets/tokens/pez.png'),
color: '#E8C896',
},
{
symbol: 'HEZ',
name: 'Hemwelatî',
amount: '100',
network: 'op',
icon: require('../../../assets/tokens/hez.png'),
color: '#98D8C8',
},
];
const handleWithdraw = (coin: CoinRow) => {
setSelectedToken(coin);
setSendModalVisible(true);
};
const handleDeposit = (coin: CoinRow) => {
setSelectedToken(coin);
setReceiveModalVisible(true);
};
return (
<SafeAreaView style={styles.container}>
{/* Colorful Top Band */}
<View style={styles.topBand}>
<View style={[styles.bandSegment, { backgroundColor: '#E74C3C' }]} />
<View style={[styles.bandSegment, { backgroundColor: '#27AE60' }]} />
<View style={[styles.bandSegment, { backgroundColor: '#3498DB' }]} />
<View style={[styles.bandSegment, { backgroundColor: '#F39C12' }]} />
</View>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
<View style={styles.mainSection}>
{/* NFT Card - Top Right */}
<View style={styles.nftCard}>
<View style={styles.nftHeader}>
<Text style={styles.nftLabel}>NFT</Text>
<Text style={styles.nftId}>123456784444444444444444912</Text>
</View>
<View style={styles.nftBody}>
<View style={styles.nftAvatar}>
<Ionicons name="person" size={40} color="#FFFFFF" />
</View>
<Text style={styles.nftName}>Satoshi Qazi M.</Text>
</View>
<View style={styles.nftFooter}>
<Ionicons name="trophy" size={20} color="#D4A017" />
<Text style={styles.nftRewardLabel}>TOTAL REWARD</Text>
<Text style={styles.nftRewardValue}>{totalReward}</Text>
</View>
</View>
{/* Coins Table */}
<View style={styles.tableContainer}>
{/* Table Header */}
<View style={styles.tableHeader}>
<Text style={[styles.headerCell, { flex: 1 }]}>coin</Text>
<Text style={[styles.headerCell, { flex: 1 }]}>amount</Text>
<Text style={[styles.headerCell, { flex: 1 }]}>Network</Text>
<Text style={[styles.headerCell, { flex: 3 }]}>Witdrawal adress</Text>
<Text style={[styles.headerCell, { flex: 1.5 }]}>withdraw deposit</Text>
</View>
{/* Table Rows */}
{coins.map((coin) => (
<View key={coin.symbol} style={styles.tableRow}>
{/* Coin */}
<View style={[styles.cell, { flex: 1 }]}>
<Image source={coin.icon} style={styles.coinIcon} />
<Text style={styles.coinSymbol}>{coin.symbol}</Text>
</View>
{/* Amount */}
<View style={[styles.cell, { flex: 1 }]}>
<Text style={styles.cellText}>{coin.amount}</Text>
</View>
{/* Network */}
<View style={[styles.cell, { flex: 1 }]}>
<Text style={styles.cellText}>{coin.network}</Text>
</View>
{/* Withdrawal Address */}
<View style={[styles.cell, { flex: 3 }]}>
<TextInput
style={styles.addressInput}
placeholder="Withdrawal adresinizi yazin"
placeholderTextColor="#999"
value={withdrawalAddresses[coin.symbol] || ''}
onChangeText={(text) =>
setWithdrawalAddresses({ ...withdrawalAddresses, [coin.symbol]: text })
}
/>
</View>
{/* Withdraw/Deposit Buttons */}
<View style={[styles.cell, { flex: 1.5, flexDirection: 'row', gap: 8 }]}>
<TouchableOpacity
style={styles.actionButton}
onPress={() => handleWithdraw(coin)}
>
<View style={styles.withdrawButton}>
<Ionicons name="arrow-down" size={20} color="#FFFFFF" />
</View>
</TouchableOpacity>
<TouchableOpacity
style={styles.actionButton}
onPress={() => handleDeposit(coin)}
>
<View style={styles.depositButton}>
<Ionicons name="arrow-up" size={20} color="#FFFFFF" />
</View>
</TouchableOpacity>
</View>
</View>
))}
</View>
</View>
{/* Bottom Stats Cards */}
<View style={styles.statsSection}>
{/* Stake/Unstake Card */}
<View style={[styles.statsCard, { backgroundColor: '#FFFFFF' }]}>
<TouchableOpacity style={styles.stakeButton}>
<Text style={styles.stakeLabel}>STAKE</Text>
<Text style={styles.stakeValue}>{stakeAmount}</Text>
<View style={styles.stakeIcon}>
<Ionicons name="arrow-up" size={16} color="#FFFFFF" />
</View>
</TouchableOpacity>
<View style={styles.stakeDivider} />
<TouchableOpacity style={styles.unstakeButton}>
<Text style={styles.unstakeLabel}>UNSTAKE</Text>
<Text style={styles.unstakeValue}>{stakeAmount}</Text>
<View style={styles.unstakeIcon}>
<Ionicons name="arrow-down" size={16} color="#FFFFFF" />
</View>
</TouchableOpacity>
</View>
{/* Trust Score */}
<TouchableOpacity
style={[styles.statsCard, styles.scoreCard]}
onPress={() => navigation.navigate('TrustScore')}
>
<Ionicons name="shield-checkmark" size={32} color="#D4A017" />
<Text style={styles.scoreLabel}>TRUST SCORE</Text>
<Text style={styles.scoreValue}>{trustScore}</Text>
</TouchableOpacity>
{/* Total Referral */}
<TouchableOpacity
style={[styles.statsCard, styles.scoreCard]}
onPress={() => navigation.navigate('Referral')}
>
<Ionicons name="people" size={32} color="#D4A017" />
<Text style={styles.scoreLabel}>TOTAL REFERAL</Text>
<Text style={styles.scoreValue}>{totalReferral}</Text>
</TouchableOpacity>
{/* Circle Score */}
<TouchableOpacity style={[styles.statsCard, styles.scoreCard]}>
<Ionicons name="radio-button-on" size={32} color="#D4A017" />
<Text style={styles.scoreLabel}>CIRCLE SCORE</Text>
<Text style={styles.scoreValue}>{circleScore}</Text>
</TouchableOpacity>
{/* Edu Score */}
<TouchableOpacity
style={[styles.statsCard, styles.scoreCard]}
onPress={() => navigation.navigate('Education')}
>
<Ionicons name="school" size={32} color="#D4A017" />
<Text style={styles.scoreLabel}>EDU. SCORE</Text>
<Text style={styles.scoreValue}>{eduScore}</Text>
</TouchableOpacity>
</View>
</ScrollView>
{/* Send Modal */}
{selectedToken && (
<SendModal
visible={sendModalVisible}
onClose={() => setSendModalVisible(false)}
token={{
symbol: selectedToken.symbol,
name: selectedToken.name,
balance: selectedToken.amount,
icon: selectedToken.icon,
color: selectedToken.color,
}}
/>
)}
{/* Receive Modal */}
{selectedToken && (
<ReceiveModal
visible={receiveModalVisible}
onClose={() => setReceiveModalVisible(false)}
token={{
symbol: selectedToken.symbol,
name: selectedToken.name,
icon: selectedToken.icon,
color: selectedToken.color,
}}
/>
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#E8F0F7',
},
topBand: {
flexDirection: 'row',
height: 8,
},
bandSegment: {
flex: 1,
},
content: {
flex: 1,
},
mainSection: {
padding: Spacing.lg,
position: 'relative',
},
nftCard: {
position: 'absolute',
top: Spacing.lg,
right: Spacing.lg,
width: 140,
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.large,
padding: Spacing.md,
...Shadow.medium,
zIndex: 10,
},
nftHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: Spacing.sm,
},
nftLabel: {
fontSize: 12,
fontWeight: '700',
color: Colors.textDark,
},
nftId: {
fontSize: 8,
color: Colors.textGray,
},
nftBody: {
alignItems: 'center',
marginVertical: Spacing.sm,
},
nftAvatar: {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: Colors.teal,
justifyContent: 'center',
alignItems: 'center',
marginBottom: Spacing.xs,
},
nftName: {
fontSize: 11,
fontWeight: '600',
color: Colors.textDark,
textAlign: 'center',
},
nftFooter: {
alignItems: 'center',
borderTopWidth: 1,
borderTopColor: '#F0F0F0',
paddingTop: Spacing.sm,
},
nftRewardLabel: {
fontSize: 9,
fontWeight: '600',
color: Colors.textGray,
marginTop: 4,
},
nftRewardValue: {
fontSize: 16,
fontWeight: '700',
color: Colors.textDark,
marginTop: 2,
},
tableContainer: {
backgroundColor: '#FFFFFF',
borderRadius: BorderRadius.large,
padding: Spacing.md,
marginRight: 160,
...Shadow.small,
},
tableHeader: {
flexDirection: 'row',
paddingBottom: Spacing.sm,
borderBottomWidth: 2,
borderBottomColor: '#E0E0E0',
marginBottom: Spacing.sm,
},
headerCell: {
fontSize: 11,
fontWeight: '700',
color: Colors.textDark,
textAlign: 'center',
},
tableRow: {
flexDirection: 'row',
paddingVertical: Spacing.md,
borderBottomWidth: 1,
borderBottomColor: '#F0F0F0',
alignItems: 'center',
},
cell: {
justifyContent: 'center',
alignItems: 'center',
},
coinIcon: {
width: 32,
height: 32,
marginBottom: 4,
},
coinSymbol: {
fontSize: 12,
fontWeight: '700',
color: Colors.textDark,
marginTop: 2,
},
cellText: {
fontSize: 12,
fontWeight: '600',
color: Colors.textDark,
},
addressInput: {
flex: 1,
backgroundColor: '#F5F5F5',
borderRadius: BorderRadius.small,
paddingHorizontal: Spacing.sm,
paddingVertical: Spacing.xs,
fontSize: 10,
color: Colors.textDark,
},
actionButton: {
width: 32,
height: 32,
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
},
withdrawButton: {
width: '100%',
height: '100%',
backgroundColor: '#E74C3C',
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
},
depositButton: {
width: '100%',
height: '100%',
backgroundColor: '#27AE60',
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
},
statsSection: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: Spacing.lg,
gap: Spacing.md,
},
statsCard: {
backgroundColor: '#F5F5F5',
borderRadius: BorderRadius.large,
padding: Spacing.lg,
...Shadow.small,
},
stakeButton: {
alignItems: 'center',
},
stakeLabel: {
fontSize: 12,
fontWeight: '700',
color: Colors.textDark,
},
stakeValue: {
fontSize: 18,
fontWeight: '700',
color: Colors.textDark,
marginVertical: Spacing.xs,
},
stakeIcon: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: '#27AE60',
justifyContent: 'center',
alignItems: 'center',
},
stakeDivider: {
height: 1,
backgroundColor: '#E0E0E0',
marginVertical: Spacing.md,
},
unstakeButton: {
alignItems: 'center',
},
unstakeLabel: {
fontSize: 12,
fontWeight: '700',
color: Colors.textDark,
},
unstakeValue: {
fontSize: 18,
fontWeight: '700',
color: Colors.textDark,
marginVertical: Spacing.xs,
},
unstakeIcon: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: '#E74C3C',
justifyContent: 'center',
alignItems: 'center',
},
scoreCard: {
alignItems: 'center',
minWidth: 110,
},
scoreLabel: {
fontSize: 10,
fontWeight: '700',
color: Colors.textDark,
marginTop: Spacing.sm,
textAlign: 'center',
},
scoreValue: {
fontSize: 20,
fontWeight: '700',
color: Colors.textDark,
marginTop: Spacing.xs,
},
});
@@ -0,0 +1,98 @@
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ScrollView, SafeAreaView } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
import Colors from '../../constants/colors';
import { Typography, Spacing, BorderRadius, Shadow } from '../../constants/theme';
export default function WalletScreen({ navigation }: any) {
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
<Text style={styles.title}>Wallet</Text>
{/* HEZ Card */}
<LinearGradient
colors={Colors.gradients.hezCard}
style={styles.tokenCard}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.tokenHeader}>
<Text style={styles.tokenName}>HEZ</Text>
<Text style={styles.tokenSubtitle}>The People's Currency</Text>
</View>
<Text style={styles.tokenBalance}>45,750.5</Text>
<Text style={styles.tokenUsd}>$45,234 USD</Text>
<Text style={styles.tokenStaked}>Staked: 30,000 HEZ</Text>
</LinearGradient>
{/* PEZ Card */}
<LinearGradient
colors={Colors.gradients.pezCard}
style={styles.tokenCard}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.tokenHeader}>
<Text style={styles.tokenName}>PEZ</Text>
<Text style={styles.tokenSubtitle}>Governance Token</Text>
</View>
<Text style={styles.tokenBalance}>1,234,567</Text>
<Text style={styles.tokenUsd}>$123,456 USD</Text>
<Text style={styles.tokenStaked}>Governance Power: 2.5%</Text>
</LinearGradient>
{/* Action Buttons */}
<View style={styles.actions}>
<TouchableOpacity style={[styles.actionButton, { backgroundColor: Colors.peach }]}>
<Ionicons name="arrow-forward" size={24} color="#FFFFFF" />
<Text style={styles.actionText}>Send</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.actionButton, { backgroundColor: Colors.teal }]}>
<Ionicons name="arrow-down" size={24} color="#FFFFFF" />
<Text style={styles.actionText}>Receive</Text>
</TouchableOpacity>
</View>
{/* Transactions */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Recent Transactions</Text>
<View style={styles.transactionItem}>
<View style={styles.transactionIcon}>
<Ionicons name="arrow-forward" size={20} color={Colors.coral} />
</View>
<View style={styles.transactionInfo}>
<Text style={styles.transactionTitle}>Sent HEZ</Text>
<Text style={styles.transactionDate}>Yesterday</Text>
</View>
<Text style={styles.transactionAmount}>-500 HEZ</Text>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: Colors.background },
title: { fontSize: 28, fontWeight: '700', color: Colors.textDark, padding: 20 },
tokenCard: { margin: 20, padding: 24, borderRadius: 20, ...Shadow.soft },
tokenHeader: { marginBottom: 16 },
tokenName: { fontSize: 24, fontWeight: '700', color: '#FFFFFF' },
tokenSubtitle: { fontSize: 14, color: 'rgba(255,255,255,0.8)' },
tokenBalance: { fontSize: 36, fontWeight: '700', color: '#FFFFFF', marginBottom: 8 },
tokenUsd: { fontSize: 18, color: 'rgba(255,255,255,0.9)', marginBottom: 8 },
tokenStaked: { fontSize: 14, color: 'rgba(255,255,255,0.8)' },
actions: { flexDirection: 'row', paddingHorizontal: 20, gap: 12 },
actionButton: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', padding: 16, borderRadius: 16, gap: 8 },
actionText: { fontSize: 16, fontWeight: '600', color: '#FFFFFF' },
section: { padding: 20 },
sectionTitle: { fontSize: 18, fontWeight: '600', color: Colors.textDark, marginBottom: 16 },
transactionItem: { flexDirection: 'row', alignItems: 'center', backgroundColor: Colors.card, padding: 16, borderRadius: 12, marginBottom: 12 },
transactionIcon: { width: 40, height: 40, borderRadius: 20, backgroundColor: Colors.background, justifyContent: 'center', alignItems: 'center', marginRight: 12 },
transactionInfo: { flex: 1 },
transactionTitle: { fontSize: 16, fontWeight: '600', color: Colors.textDark },
transactionDate: { fontSize: 14, color: Colors.textGray, marginTop: 4 },
transactionAmount: { fontSize: 16, fontWeight: '600', color: Colors.coral },
});
+307
View File
@@ -0,0 +1,307 @@
/**
* PezkuwiChain Blockchain Service
* Handles all interactions with the PezkuwiChain blockchain via Polkadot.js
*/
import { ApiPromise, WsProvider } from '@polkadot/api';
import { CURRENT_CHAIN_CONFIG, ASSET_IDS } from '../constants/blockchain';
import { Balance, Transaction, Proposal } from '../types';
class BlockchainService {
private api: ApiPromise | null = null;
private provider: WsProvider | null = null;
private isConnected: boolean = false;
/**
* Initialize connection to PezkuwiChain
*/
async connect(): Promise<boolean> {
try {
console.log(`Connecting to ${CURRENT_CHAIN_CONFIG.name}...`);
console.log(`RPC URL: ${CURRENT_CHAIN_CONFIG.rpcUrl}`);
this.provider = new WsProvider(CURRENT_CHAIN_CONFIG.rpcUrl);
this.api = await ApiPromise.create({ provider: this.provider });
await this.api.isReady;
this.isConnected = true;
console.log('✅ Connected to PezkuwiChain');
console.log(`Chain: ${await this.api.rpc.system.chain()}`);
console.log(`Node: ${await this.api.rpc.system.name()}`);
console.log(`Version: ${await this.api.rpc.system.version()}`);
return true;
} catch (error) {
console.error('❌ Failed to connect to PezkuwiChain:', error);
this.isConnected = false;
return false;
}
}
/**
* Disconnect from blockchain
*/
async disconnect(): Promise<void> {
if (this.api) {
await this.api.disconnect();
this.api = null;
this.provider = null;
this.isConnected = false;
console.log('Disconnected from PezkuwiChain');
}
}
/**
* Check if connected to blockchain
*/
isApiConnected(): boolean {
return this.isConnected && this.api !== null;
}
/**
* Get API instance (throws if not connected)
*/
private getApi(): ApiPromise {
if (!this.api || !this.isConnected) {
throw new Error('Not connected to blockchain. Call connect() first.');
}
return this.api;
}
/**
* Get account balances (HEZ and PEZ)
*/
async getBalances(address: string): Promise<Balance> {
try {
const api = this.getApi();
// Get HEZ balance (native token)
const { data: hezBalance } = await api.query.system.account(address);
const hezFree = hezBalance.free.toString();
const hezReserved = hezBalance.reserved.toString();
// Get PEZ balance (asset)
const pezBalance = await api.query.assets.account(ASSET_IDS.PEZ, address);
const pezFree = pezBalance.isSome ? pezBalance.unwrap().balance.toString() : '0';
// Get staked HEZ
const stakingInfo = await api.query.staking.ledger(address);
const hezStaked = stakingInfo.isSome ? stakingInfo.unwrap().active.toString() : '0';
// Calculate governance power (PEZ balance as percentage)
const totalPezSupply = '5000000000000000000000'; // 5 billion
const governancePower = (parseFloat(pezFree) / parseFloat(totalPezSupply) * 100).toFixed(2);
// Mock USD values (would come from price oracle in production)
const hezUsd = (parseFloat(hezFree) / 1e12 * 1.0).toFixed(2);
const pezUsd = (parseFloat(pezFree) / 1e12 * 0.1).toFixed(2);
return {
hez: this.formatBalance(hezFree, 12),
pez: this.formatBalance(pezFree, 12),
hezStaked: this.formatBalance(hezStaked, 12),
hezUsd,
pezUsd,
governancePower,
};
} catch (error) {
console.error('Error fetching balances:', error);
// Return mock data if blockchain not available
return this.getMockBalances();
}
}
/**
* Get transaction history
*/
async getTransactions(address: string, limit: number = 10): Promise<Transaction[]> {
try {
// This would query blockchain events and filter for transfers
// For now, return mock data
return this.getMockTransactions();
} catch (error) {
console.error('Error fetching transactions:', error);
return this.getMockTransactions();
}
}
/**
* Get active governance proposals
*/
async getProposals(): Promise<Proposal[]> {
try {
const api = this.getApi();
// Query welati pallet for active proposals
const proposals = await api.query.welati.proposals.entries();
return proposals.map(([key, value]: any) => {
const proposalId = key.args[0].toNumber();
const proposal = value.unwrap();
return {
id: proposalId,
title: `Proposal ${proposalId}`,
description: proposal.description?.toString() || 'No description',
proposer: proposal.proposer.toString(),
votingDeadline: proposal.deadline.toNumber(),
yesVotes: proposal.yesVotes.toString(),
noVotes: proposal.noVotes.toString(),
status: proposal.status.toString() as any,
};
});
} catch (error) {
console.error('Error fetching proposals:', error);
return this.getMockProposals();
}
}
/**
* Send HEZ or PEZ tokens
*/
async sendTokens(
from: string,
to: string,
amount: string,
token: 'HEZ' | 'PEZ',
signer: any
): Promise<string> {
try {
const api = this.getApi();
let tx;
if (token === 'HEZ') {
tx = api.tx.balances.transfer(to, amount);
} else {
tx = api.tx.assets.transfer(ASSET_IDS.PEZ, to, amount);
}
const hash = await tx.signAndSend(signer);
return hash.toString();
} catch (error) {
console.error('Error sending tokens:', error);
throw error;
}
}
/**
* Stake HEZ tokens
*/
async stakeTokens(
address: string,
amount: string,
signer: any
): Promise<string> {
try {
const api = this.getApi();
const tx = api.tx.staking.bond(address, amount, 'Staked');
const hash = await tx.signAndSend(signer);
return hash.toString();
} catch (error) {
console.error('Error staking tokens:', error);
throw error;
}
}
/**
* Vote on governance proposal
*/
async voteOnProposal(
proposalId: number,
vote: 'yes' | 'no',
amount: string,
signer: any
): Promise<string> {
try {
const api = this.getApi();
const tx = api.tx.welati.vote(proposalId, vote === 'yes', amount);
const hash = await tx.signAndSend(signer);
return hash.toString();
} catch (error) {
console.error('Error voting on proposal:', error);
throw error;
}
}
/**
* Format balance with decimals
*/
private formatBalance(balance: string, decimals: number): string {
const value = parseFloat(balance) / Math.pow(10, decimals);
return value.toLocaleString('en-US', { maximumFractionDigits: 2 });
}
/**
* Mock data for development/testing
*/
private getMockBalances(): Balance {
return {
hez: '45,750.5',
pez: '1,234,567',
hezStaked: '30,000',
hezUsd: '45,234',
pezUsd: '123,456',
governancePower: '2.5',
};
}
private getMockTransactions(): Transaction[] {
return [
{
id: '1',
type: 'send' as any,
amount: '500',
token: 'HEZ',
from: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
to: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
timestamp: Date.now() - 86400000,
blockNumber: 123456,
hash: '0x1234567890abcdef',
status: 'confirmed',
},
{
id: '2',
type: 'receive' as any,
amount: '300',
token: 'PEZ',
from: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
to: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
timestamp: Date.now() - 172800000,
blockNumber: 123450,
hash: '0xabcdef1234567890',
status: 'confirmed',
},
];
}
private getMockProposals(): Proposal[] {
return [
{
id: 1,
title: 'Proposal 1',
description: 'Description of proposal 1',
proposer: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
votingDeadline: Date.now() + 172800000,
yesVotes: '10400',
noVotes: '4600',
status: 'active',
},
{
id: 2,
title: 'Proposal 2',
description: 'Description of proposal 2',
proposer: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
votingDeadline: Date.now() + 432000000,
yesVotes: '198',
noVotes: '0',
status: 'active',
},
];
}
}
// Export singleton instance
export const blockchainService = new BlockchainService();
export default blockchainService;
+262
View File
@@ -0,0 +1,262 @@
/**
* KYC Service
* Handles Identity-KYC form submission, encryption, and blockchain interaction
*/
import * as SecureStore from 'expo-secure-store';
import { KYCFormData, KYCSubmission, KurdistanCitizen, KYCStatus } from '../types/kyc';
import { blockchainService } from './blockchain';
const KYC_DATA_KEY = 'pezkuwi_kyc_data';
const CITIZEN_DATA_KEY = 'pezkuwi_citizen_data';
const KYC_STATUS_KEY = 'pezkuwi_kyc_status';
class KYCService {
/**
* Generate hash from KYC data
* This hash will be submitted to blockchain
*/
private async generateHash(data: KYCFormData): Promise<string> {
// In production, use proper cryptographic hash (SHA-256)
const dataString = JSON.stringify(data);
// Simple hash for development (replace with proper crypto in production)
let hash = 0;
for (let i = 0; i < dataString.length; i++) {
const char = dataString.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return `0x${Math.abs(hash).toString(16).padStart(64, '0')}`;
}
/**
* Save KYC form data locally (encrypted)
*/
async saveKYCData(data: KYCFormData): Promise<void> {
try {
const encrypted = JSON.stringify(data);
await SecureStore.setItemAsync(KYC_DATA_KEY, encrypted);
console.log('✅ KYC data saved locally (encrypted)');
} catch (error) {
console.error('❌ Failed to save KYC data:', error);
throw error;
}
}
/**
* Get saved KYC data
*/
async getKYCData(): Promise<KYCFormData | null> {
try {
const encrypted = await SecureStore.getItemAsync(KYC_DATA_KEY);
if (!encrypted) return null;
return JSON.parse(encrypted);
} catch (error) {
console.error('❌ Failed to get KYC data:', error);
return null;
}
}
/**
* Submit KYC to blockchain
* Only hash is sent, not the actual data
*/
async submitKYC(data: KYCFormData, signer: any): Promise<KYCSubmission> {
try {
// 1. Save data locally first
await this.saveKYCData(data);
// 2. Generate hash
const dataHash = await this.generateHash(data);
console.log('📝 Generated KYC hash:', dataHash);
// 3. Submit hash to blockchain (identity-kyc pallet)
// TODO: Replace with actual blockchain call when testnet is active
const txHash = await this.submitHashToBlockchain(dataHash, signer);
const submission: KYCSubmission = {
dataHash,
submittedAt: Date.now(),
txHash,
};
// 4. Update KYC status
await this.updateKYCStatus({ started: true, submitted: true, approved: false });
console.log('✅ KYC submitted to blockchain');
return submission;
} catch (error) {
console.error('❌ Failed to submit KYC:', error);
throw error;
}
}
/**
* Submit hash to blockchain (identity-kyc pallet)
*/
private async submitHashToBlockchain(dataHash: string, signer: any): Promise<string> {
try {
// Check if blockchain is connected
if (!blockchainService.isApiConnected()) {
console.log('⚠️ Blockchain not connected, using mock submission');
// Mock transaction hash for development
return `0x${Math.random().toString(16).substr(2, 64)}`;
}
// TODO: Actual blockchain submission
// const api = blockchainService.getApi();
// const tx = api.tx.identityKyc.submitKyc(dataHash);
// const hash = await tx.signAndSend(signer);
// return hash.toString();
// Mock for now
return `0x${Math.random().toString(16).substr(2, 64)}`;
} catch (error) {
console.error('❌ Failed to submit to blockchain:', error);
throw error;
}
}
/**
* Check KYC approval status on blockchain
*/
async checkApprovalStatus(address: string): Promise<boolean> {
try {
// TODO: Query blockchain for approval status
// const api = blockchainService.getApi();
// const approval = await api.query.identityKyc.approvals(address);
// return approval.isSome;
// Mock: Auto-approve after 5 seconds (for development)
const status = await this.getKYCStatus();
if (status.submitted) {
const timeSinceSubmission = Date.now() - (status.citizen?.approvedAt || 0);
return timeSinceSubmission > 5000; // 5 seconds
}
return false;
} catch (error) {
console.error('❌ Failed to check approval status:', error);
return false;
}
}
/**
* Generate Kurdistan Digital Citizen certificate
* Called after KYC is approved on blockchain
*/
async generateCitizen(data: KYCFormData, dataHash: string): Promise<KurdistanCitizen> {
try {
// Generate unique Citizen ID
const citizenId = `KRD-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
// Generate QR code data
const qrData = JSON.stringify({
citizenId,
name: data.fullName,
region: data.region,
hash: dataHash,
});
const citizen: KurdistanCitizen = {
citizenId,
fullName: data.fullName,
photo: data.photo || '',
region: data.region,
kycApproved: true,
approvedAt: Date.now(),
dataHash,
qrCode: qrData,
};
// Save citizen data locally (encrypted)
await SecureStore.setItemAsync(CITIZEN_DATA_KEY, JSON.stringify(citizen));
// Update KYC status
await this.updateKYCStatus({
started: true,
submitted: true,
approved: true,
citizen,
});
console.log('✅ Kurdistan Digital Citizen generated:', citizenId);
return citizen;
} catch (error) {
console.error('❌ Failed to generate citizen:', error);
throw error;
}
}
/**
* Get citizen data
*/
async getCitizen(): Promise<KurdistanCitizen | null> {
try {
const data = await SecureStore.getItemAsync(CITIZEN_DATA_KEY);
if (!data) return null;
return JSON.parse(data);
} catch (error) {
console.error('❌ Failed to get citizen data:', error);
return null;
}
}
/**
* Get KYC status
*/
async getKYCStatus(): Promise<KYCStatus> {
try {
const statusData = await SecureStore.getItemAsync(KYC_STATUS_KEY);
if (!statusData) {
return { started: false, submitted: false, approved: false };
}
return JSON.parse(statusData);
} catch (error) {
console.error('❌ Failed to get KYC status:', error);
return { started: false, submitted: false, approved: false };
}
}
/**
* Update KYC status
*/
private async updateKYCStatus(status: KYCStatus): Promise<void> {
try {
await SecureStore.setItemAsync(KYC_STATUS_KEY, JSON.stringify(status));
} catch (error) {
console.error('❌ Failed to update KYC status:', error);
}
}
/**
* Check if user has access to Governance
*/
async hasGovernanceAccess(): Promise<boolean> {
const status = await this.getKYCStatus();
return status.approved;
}
/**
* Clear all KYC data (for testing)
*/
async clearKYCData(): Promise<void> {
try {
await SecureStore.deleteItemAsync(KYC_DATA_KEY);
await SecureStore.deleteItemAsync(CITIZEN_DATA_KEY);
await SecureStore.deleteItemAsync(KYC_STATUS_KEY);
console.log('✅ KYC data cleared');
} catch (error) {
console.error('❌ Failed to clear KYC data:', error);
}
}
}
export const kycService = new KYCService();
export default kycService;
+197
View File
@@ -0,0 +1,197 @@
/**
* PezkuwiChain Mobile App - Type Definitions
*/
// User Types
export interface User {
id: string;
name: string;
email: string;
address: string;
trustScore: number;
kycLevel: number;
avatar?: string;
}
// Balance Types
export interface Balance {
hez: string;
pez: string;
hezStaked: string;
hezUsd: string;
pezUsd: string;
governancePower: string;
}
// Transaction Types
export enum TransactionType {
SEND = 'send',
RECEIVE = 'receive',
STAKE = 'stake',
UNSTAKE = 'unstake',
REWARD = 'reward',
GOVERNANCE = 'governance',
}
export interface Transaction {
id: string;
type: TransactionType;
amount: string;
token: 'HEZ' | 'PEZ';
from: string;
to: string;
timestamp: number;
blockNumber: number;
hash: string;
status: 'pending' | 'confirmed' | 'failed';
}
// Governance Types
export interface Proposal {
id: number;
title: string;
description: string;
proposer: string;
votingDeadline: number;
yesVotes: string;
noVotes: string;
status: 'active' | 'passed' | 'rejected' | 'executed';
}
export interface Vote {
proposalId: number;
voter: string;
vote: 'yes' | 'no';
amount: string;
conviction: number;
timestamp: number;
}
// Parliamentary NFT Types
export interface ParliamentaryNFT {
tokenId: number;
owner: string;
monthlySalary: string;
electionDate: number;
termEnd: number;
status: 'active' | 'inactive';
}
// Identity Types
export interface DigitalIdentity {
idNumber: string;
name: string;
photo: string;
trustScore: number;
kycLevel: number;
verifications: {
identity: boolean;
education: boolean;
kyc: boolean;
};
qrCode: string;
}
// Education Certificate Types
export interface Certificate {
id: string;
title: string;
institution: string;
issueDate: number;
certificateType: 'bachelor' | 'master' | 'phd' | 'diploma' | 'certificate';
verified: boolean;
nftId?: number;
qrCode: string;
}
// Referral Types
export interface Referral {
code: string;
totalReferrals: number;
activeReferrals: number;
rewardsEarned: string;
referrals: ReferralUser[];
}
export interface ReferralUser {
name: string;
avatar: string;
joinDate: number;
reward: string;
status: 'active' | 'inactive';
}
// Business Types
export interface MerchantDashboard {
monthlyRevenue: string;
transactions: number;
customers: number;
recentTransactions: MerchantTransaction[];
}
export interface MerchantTransaction {
customerName: string;
amount: string;
token: 'HEZ' | 'PEZ';
timestamp: number;
}
// Exchange Types
export interface ExchangeRate {
from: 'HEZ' | 'PEZ';
to: 'HEZ' | 'PEZ';
rate: number;
timestamp: number;
}
export interface SwapTransaction {
fromToken: 'HEZ' | 'PEZ';
toToken: 'HEZ' | 'PEZ';
fromAmount: string;
toAmount: string;
rate: number;
slippage: number;
timestamp: number;
}
// Navigation Types
export type RootStackParamList = {
LanguageSelection: undefined;
SignIn: undefined;
SignUp: undefined;
MainTabs: undefined;
Send: { token: 'HEZ' | 'PEZ' };
Receive: { token: 'HEZ' | 'PEZ' };
ProposalDetail: { proposalId: number };
CertificateDetail: { certificateId: string };
QRScanner: undefined;
};
export type MainTabsParamList = {
Home: undefined;
Wallet: undefined;
Governance: undefined;
Referral: undefined;
Profile: undefined;
};
// Blockchain Types
export interface ChainConfig {
name: string;
rpcUrl: string;
chainId: string;
genesisHash: string;
decimals: {
hez: number;
pez: number;
};
}
// API Response Types
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
message?: string;
}
+89
View File
@@ -0,0 +1,89 @@
/**
* Identity KYC Types
*/
export type Region = 'basur' | 'bakur' | 'rojava' | 'rojhelat' | 'kurdistan_a_sor' | 'diaspora';
export type MaritalStatus = 'single' | 'married';
export interface Child {
name: string;
order: number; // 1st child, 2nd child, etc.
}
export interface KYCFormData {
// Personal Information
fullName: string;
fatherName: string;
grandfatherName: string;
greatGrandfatherName: string;
motherName: string;
// Marital Status
maritalStatus: MaritalStatus;
spouseName?: string;
numberOfChildren?: number;
children?: Child[];
// Region
region: Region;
// Photo (base64 or file URI)
photo?: string;
}
export interface KYCSubmission {
// Hash of the KYC data (sent to blockchain)
dataHash: string;
// Timestamp
submittedAt: number;
// Blockchain transaction hash
txHash?: string;
}
export interface KurdistanCitizen {
// Citizen ID (generated after KYC approval)
citizenId: string;
// Personal Info (stored locally, encrypted)
fullName: string;
photo: string;
region: Region;
// KYC Status
kycApproved: boolean;
approvedAt: number;
// Blockchain reference (only hash)
dataHash: string;
// QR Code for verification
qrCode: string;
}
export interface KYCStatus {
// Has user started KYC?
started: boolean;
// Has user submitted KYC?
submitted: boolean;
// Is KYC approved on blockchain?
approved: boolean;
// Citizen data (only if approved)
citizen?: KurdistanCitizen;
}
// Region labels for UI
export const REGION_LABELS: Record<Region, { en: string; ku: string }> = {
basur: { en: 'Başur (South Kurdistan)', ku: 'باشوور (کوردستانی باشوور)' },
bakur: { en: 'Bakur (North Kurdistan)', ku: 'باکوور (کوردستانی باکوور)' },
rojava: { en: 'Rojava (West Kurdistan)', ku: 'رۆژاڤا (کوردستانی رۆژاڤا)' },
rojhelat: { en: 'Rojhelat (East Kurdistan)', ku: 'رۆژهەڵات (کوردستانی رۆژهەڵات)' },
kurdistan_a_sor: { en: 'Kurdistan a Sor (Red Kurdistan)', ku: 'کوردستانا سور' },
diaspora: { en: 'Diaspora', ku: 'دیاسپۆرا' },
};