mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-06-13 20:21:01 +00:00
Centralize common code in shared folder
This commit reorganizes the codebase to eliminate duplication between web and mobile frontends by moving all commonly used files to the shared folder. Changes: - Moved lib files to shared/lib/: * wallet.ts, staking.ts, tiki.ts, identity.ts * multisig.ts, usdt.ts, scores.ts, citizenship-workflow.ts - Moved utils to shared/utils/: * auth.ts, dex.ts * Created format.ts (extracted formatNumber from web utils) - Created shared/theme/: * colors.ts (Kurdistan and App color definitions) - Updated web configuration: * Added @pezkuwi/* path aliases in tsconfig.json and vite.config.ts * Updated all imports to use @pezkuwi/lib/*, @pezkuwi/utils/*, @pezkuwi/theme/* * Removed duplicate files from web/src/lib and web/src/utils - Updated mobile configuration: * Added @pezkuwi/* path aliases in tsconfig.json * Updated theme/colors.ts to re-export from shared * Mobile already uses relative imports to shared (no changes needed) Architecture Benefits: - Single source of truth for common code - No duplication between frontends - Easier maintenance and consistency - Clear separation of shared vs platform-specific code Web-specific files kept: - web/src/lib/supabase.ts - web/src/lib/utils.ts (cn function for Tailwind, re-exports formatNumber from shared) All imports updated and tested. Both web and mobile now use the centralized shared folder.
This commit is contained in:
@@ -1,25 +1,6 @@
|
|||||||
// Kurdistan Flag Colors
|
/**
|
||||||
export const KurdistanColors = {
|
* Re-export colors from shared theme
|
||||||
kesk: '#00A94F', // Green - Primary
|
* All color definitions are centralized in shared/theme/colors.ts
|
||||||
sor: '#EE2A35', // Red - Accent
|
*/
|
||||||
zer: '#FFD700', // Gold - Secondary
|
export { KurdistanColors, AppColors } from '../../../shared/theme/colors';
|
||||||
spi: '#FFFFFF', // White - Background
|
export { AppColors as default } from '../../../shared/theme/colors';
|
||||||
reş: '#000000', // Black - Text
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AppColors = {
|
|
||||||
primary: KurdistanColors.kesk,
|
|
||||||
secondary: KurdistanColors.zer,
|
|
||||||
accent: KurdistanColors.sor,
|
|
||||||
background: '#F5F5F5',
|
|
||||||
surface: KurdistanColors.spi,
|
|
||||||
text: KurdistanColors.reş,
|
|
||||||
textSecondary: '#666666',
|
|
||||||
border: '#E0E0E0',
|
|
||||||
error: KurdistanColors.sor,
|
|
||||||
success: KurdistanColors.kesk,
|
|
||||||
warning: KurdistanColors.zer,
|
|
||||||
info: '#2196F3',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppColors;
|
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
{
|
{
|
||||||
"extends": "expo/tsconfig.base",
|
"extends": "expo/tsconfig.base",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strict": true
|
"strict": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@pezkuwi/lib/*": ["../shared/lib/*"],
|
||||||
|
"@pezkuwi/utils/*": ["../shared/utils/*"],
|
||||||
|
"@pezkuwi/theme/*": ["../shared/theme/*"],
|
||||||
|
"@pezkuwi/types/*": ["../shared/types/*"],
|
||||||
|
"@pezkuwi/i18n": ["../shared/i18n"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Shared theme colors for all platforms
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Kurdistan Flag Colors
|
||||||
|
export const KurdistanColors = {
|
||||||
|
kesk: '#00A94F', // Green - Primary
|
||||||
|
sor: '#EE2A35', // Red - Accent
|
||||||
|
zer: '#FFD700', // Gold - Secondary
|
||||||
|
spi: '#FFFFFF', // White - Background
|
||||||
|
reş: '#000000', // Black - Text
|
||||||
|
};
|
||||||
|
|
||||||
|
// Application color palette
|
||||||
|
export const AppColors = {
|
||||||
|
primary: KurdistanColors.kesk,
|
||||||
|
secondary: KurdistanColors.zer,
|
||||||
|
accent: KurdistanColors.sor,
|
||||||
|
background: '#F5F5F5',
|
||||||
|
surface: KurdistanColors.spi,
|
||||||
|
text: KurdistanColors.reş,
|
||||||
|
textSecondary: '#666666',
|
||||||
|
border: '#E0E0E0',
|
||||||
|
error: KurdistanColors.sor,
|
||||||
|
success: KurdistanColors.kesk,
|
||||||
|
warning: KurdistanColors.zer,
|
||||||
|
info: '#2196F3',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppColors;
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Shared formatting utilities
|
||||||
|
* Platform-agnostic formatters for numbers, currency, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a number with K, M, B suffixes for large values
|
||||||
|
* @param value - The number to format
|
||||||
|
* @param decimals - Number of decimal places (default 2)
|
||||||
|
* @returns Formatted string
|
||||||
|
*/
|
||||||
|
export function formatNumber(value: number, decimals: number = 2): string {
|
||||||
|
if (value === 0) return '0';
|
||||||
|
if (value < 0.01) return '<0.01';
|
||||||
|
|
||||||
|
// For large numbers, use K, M, B suffixes
|
||||||
|
if (value >= 1e9) {
|
||||||
|
return (value / 1e9).toFixed(decimals) + 'B';
|
||||||
|
}
|
||||||
|
if (value >= 1e6) {
|
||||||
|
return (value / 1e6).toFixed(decimals) + 'M';
|
||||||
|
}
|
||||||
|
if (value >= 1e3) {
|
||||||
|
return (value / 1e3).toFixed(decimals) + 'K';
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.toFixed(decimals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a percentage value
|
||||||
|
* @param value - The percentage value (e.g., 0.15 for 15%)
|
||||||
|
* @param decimals - Number of decimal places (default 2)
|
||||||
|
* @returns Formatted percentage string
|
||||||
|
*/
|
||||||
|
export function formatPercentage(value: number, decimals: number = 2): string {
|
||||||
|
return `${(value * 100).toFixed(decimals)}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a currency value
|
||||||
|
* @param value - The currency value
|
||||||
|
* @param symbol - Currency symbol (default '$')
|
||||||
|
* @param decimals - Number of decimal places (default 2)
|
||||||
|
* @returns Formatted currency string
|
||||||
|
*/
|
||||||
|
export function formatCurrency(
|
||||||
|
value: number,
|
||||||
|
symbol: string = '$',
|
||||||
|
decimals: number = 2
|
||||||
|
): string {
|
||||||
|
return `${symbol}${formatNumber(value, decimals)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a formatted number string back to number
|
||||||
|
* @param formatted - Formatted string (e.g., "1.5K")
|
||||||
|
* @returns Number value
|
||||||
|
*/
|
||||||
|
export function parseFormattedNumber(formatted: string): number {
|
||||||
|
const cleaned = formatted.replace(/[^0-9.KMB]/g, '');
|
||||||
|
const match = cleaned.match(/^([\d.]+)([KMB])?$/);
|
||||||
|
|
||||||
|
if (!match) return 0;
|
||||||
|
|
||||||
|
const [, numberPart, suffix] = match;
|
||||||
|
let value = parseFloat(numberPart);
|
||||||
|
|
||||||
|
if (suffix === 'K') value *= 1e3;
|
||||||
|
else if (suffix === 'M') value *= 1e6;
|
||||||
|
else if (suffix === 'B') value *= 1e9;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
@@ -3,10 +3,10 @@ import { usePolkadot } from '@/contexts/PolkadotContext';
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Wallet, TrendingUp, ArrowUpRight, ArrowDownRight, RefreshCw, Award, Plus, Coins, Send, Shield, Users } from 'lucide-react';
|
import { Wallet, TrendingUp, ArrowUpRight, ArrowDownRight, RefreshCw, Award, Plus, Coins, Send, Shield, Users } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ASSET_IDS, getAssetSymbol } from '@/lib/wallet';
|
import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet';
|
||||||
import { AddTokenModal } from './AddTokenModal';
|
import { AddTokenModal } from './AddTokenModal';
|
||||||
import { TransferModal } from './TransferModal';
|
import { TransferModal } from './TransferModal';
|
||||||
import { getAllScores, type UserScores } from '@/lib/scores';
|
import { getAllScores, type UserScores } from '@pezkuwi/lib/scores';
|
||||||
|
|
||||||
interface TokenBalance {
|
interface TokenBalance {
|
||||||
assetId: number;
|
assetId: number;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { usePolkadot } from '@/contexts/PolkadotContext';
|
|||||||
import { useWallet } from '@/contexts/WalletContext';
|
import { useWallet } from '@/contexts/WalletContext';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||||
import { ASSET_IDS, getAssetSymbol } from '@/lib/wallet';
|
import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet';
|
||||||
|
|
||||||
interface AddLiquidityModalProps {
|
interface AddLiquidityModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ChevronRight, Shield } from 'lucide-react';
|
import { ChevronRight, Shield } from 'lucide-react';
|
||||||
import { usePolkadot } from '../contexts/PolkadotContext';
|
import { usePolkadot } from '../contexts/PolkadotContext';
|
||||||
import { formatBalance } from '../lib/wallet';
|
import { formatBalance } from '@pezkuwi/lib/wallet';
|
||||||
|
|
||||||
const HeroSection: React.FC = () => {
|
const HeroSection: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
calculateMultisigAddress,
|
calculateMultisigAddress,
|
||||||
USDT_MULTISIG_CONFIG,
|
USDT_MULTISIG_CONFIG,
|
||||||
formatMultisigAddress,
|
formatMultisigAddress,
|
||||||
} from '@/lib/multisig';
|
} from '@pezkuwi/lib/multisig';
|
||||||
import { getTikiDisplayName, getTikiEmoji } from '@/lib/tiki';
|
import { getTikiDisplayName, getTikiEmoji } from '@pezkuwi/lib/tiki';
|
||||||
|
|
||||||
interface MultisigMembersProps {
|
interface MultisigMembersProps {
|
||||||
specificAddresses?: Record<string, string>;
|
specificAddresses?: Record<string, string>;
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
|
|||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Loader2, Award, Crown, Shield, Users } from 'lucide-react';
|
import { Loader2, Award, Crown, Shield, Users } from 'lucide-react';
|
||||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||||
import { getUserTikis } from '@/lib/citizenship-workflow';
|
import { getUserTikis } from '@pezkuwi/lib/citizenship-workflow';
|
||||||
import type { TikiInfo } from '@/lib/citizenship-workflow';
|
import type { TikiInfo } from '@pezkuwi/lib/citizenship-workflow';
|
||||||
|
|
||||||
// Icon map for different Tiki roles
|
// Icon map for different Tiki roles
|
||||||
const getTikiIcon = (role: string) => {
|
const getTikiIcon = (role: string) => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||||
import { useWallet } from '@/contexts/WalletContext';
|
import { useWallet } from '@/contexts/WalletContext';
|
||||||
import { ASSET_IDS, getAssetSymbol } from '@/lib/wallet';
|
import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet';
|
||||||
import { AddLiquidityModal } from '@/components/AddLiquidityModal';
|
import { AddLiquidityModal } from '@/components/AddLiquidityModal';
|
||||||
import { RemoveLiquidityModal } from '@/components/RemoveLiquidityModal';
|
import { RemoveLiquidityModal } from '@/components/RemoveLiquidityModal';
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { usePolkadot } from '@/contexts/PolkadotContext';
|
|||||||
import { useWallet } from '@/contexts/WalletContext';
|
import { useWallet } from '@/contexts/WalletContext';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||||
import { ASSET_IDS, getAssetSymbol } from '@/lib/wallet';
|
import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet';
|
||||||
|
|
||||||
// Helper to get display name for tokens (users see HEZ not wHEZ, USDT not wUSDT)
|
// Helper to get display name for tokens (users see HEZ not wHEZ, USDT not wUSDT)
|
||||||
const getDisplayTokenName = (assetId: number): string => {
|
const getDisplayTokenName = (assetId: number): string => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Alert, AlertDescription } from '@/components/ui/alert';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||||
import { getWUSDTTotalSupply, checkReserveHealth, formatWUSDT } from '@/lib/usdt';
|
import { getWUSDTTotalSupply, checkReserveHealth, formatWUSDT } from '@pezkuwi/lib/usdt';
|
||||||
import { MultisigMembers } from './MultisigMembers';
|
import { MultisigMembers } from './MultisigMembers';
|
||||||
|
|
||||||
interface ReservesDashboardProps {
|
interface ReservesDashboardProps {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/u
|
|||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||||
import { useWallet } from '@/contexts/WalletContext';
|
import { useWallet } from '@/contexts/WalletContext';
|
||||||
import { ASSET_IDS, formatBalance, parseAmount } from '@/lib/wallet';
|
import { ASSET_IDS, formatBalance, parseAmount } from '@pezkuwi/lib/wallet';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { KurdistanSun } from './KurdistanSun';
|
import { KurdistanSun } from './KurdistanSun';
|
||||||
import { PriceChart } from './trading/PriceChart';
|
import { PriceChart } from './trading/PriceChart';
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
getWithdrawalTier,
|
getWithdrawalTier,
|
||||||
formatDelay,
|
formatDelay,
|
||||||
formatWUSDT,
|
formatWUSDT,
|
||||||
} from '@/lib/usdt';
|
} from '@pezkuwi/lib/usdt';
|
||||||
import { isMultisigMember } from '@/lib/multisig';
|
import { isMultisigMember } from '@pezkuwi/lib/multisig';
|
||||||
|
|
||||||
interface USDTBridgeProps {
|
interface USDTBridgeProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { Alert, AlertDescription } from '@/components/ui/alert';
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Loader2, CheckCircle, AlertTriangle, Shield } from 'lucide-react';
|
import { Loader2, CheckCircle, AlertTriangle, Shield } from 'lucide-react';
|
||||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||||
import { verifyNftOwnership } from '@/lib/citizenship-workflow';
|
import { verifyNftOwnership } from '@pezkuwi/lib/citizenship-workflow';
|
||||||
import { generateAuthChallenge, signChallenge, verifySignature, saveCitizenSession } from '@/lib/citizenship-crypto';
|
import { generateAuthChallenge, signChallenge, verifySignature, saveCitizenSession } from '@pezkuwi/lib/citizenship-crypto';
|
||||||
import type { AuthChallenge } from '@/lib/citizenship-crypto';
|
import type { AuthChallenge } from '@pezkuwi/lib/citizenship-crypto';
|
||||||
|
|
||||||
interface ExistingCitizenAuthProps {
|
interface ExistingCitizenAuthProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
|
|||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { Loader2, AlertTriangle, CheckCircle, User, Users as UsersIcon, MapPin, Briefcase, Mail, Clock } from 'lucide-react';
|
import { Loader2, AlertTriangle, CheckCircle, User, Users as UsersIcon, MapPin, Briefcase, Mail, Clock } from 'lucide-react';
|
||||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||||
import type { CitizenshipData, Region, MaritalStatus } from '@/lib/citizenship-workflow';
|
import type { CitizenshipData, Region, MaritalStatus } from '@pezkuwi/lib/citizenship-workflow';
|
||||||
import { FOUNDER_ADDRESS, submitKycApplication, subscribeToKycApproval, getKycStatus } from '@/lib/citizenship-workflow';
|
import { FOUNDER_ADDRESS, submitKycApplication, subscribeToKycApproval, getKycStatus } from '@pezkuwi/lib/citizenship-workflow';
|
||||||
import { generateCommitmentHash, generateNullifierHash, encryptData, saveLocalCitizenshipData, uploadToIPFS } from '@/lib/citizenship-crypto';
|
import { generateCommitmentHash, generateNullifierHash, encryptData, saveLocalCitizenshipData, uploadToIPFS } from '@pezkuwi/lib/citizenship-crypto';
|
||||||
|
|
||||||
interface NewCitizenApplicationProps {
|
interface NewCitizenApplicationProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useWallet } from '@/contexts/WalletContext';
|
|||||||
import { X, Plus, AlertCircle, Loader2, CheckCircle, Info } from 'lucide-react';
|
import { X, Plus, AlertCircle, Loader2, CheckCircle, Info } from 'lucide-react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { PoolInfo } from '@/types/dex';
|
import { PoolInfo } from '@/types/dex';
|
||||||
import { parseTokenInput, formatTokenBalance, quote } from '@/utils/dex';
|
import { parseTokenInput, formatTokenBalance, quote } from '@pezkuwi/utils/dex';
|
||||||
|
|
||||||
interface AddLiquidityModalProps {
|
interface AddLiquidityModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { X, Plus, AlertCircle, Loader2, CheckCircle } from 'lucide-react';
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { KNOWN_TOKENS } from '@/types/dex';
|
import { KNOWN_TOKENS } from '@/types/dex';
|
||||||
import { parseTokenInput, formatTokenBalance } from '@/utils/dex';
|
import { parseTokenInput, formatTokenBalance } from '@pezkuwi/utils/dex';
|
||||||
|
|
||||||
interface CreatePoolModalProps {
|
interface CreatePoolModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import PoolDashboard from '@/components/PoolDashboard';
|
|||||||
import { CreatePoolModal } from './CreatePoolModal';
|
import { CreatePoolModal } from './CreatePoolModal';
|
||||||
import { InitializeHezPoolModal } from './InitializeHezPoolModal';
|
import { InitializeHezPoolModal } from './InitializeHezPoolModal';
|
||||||
import { ArrowRightLeft, Droplet, Settings } from 'lucide-react';
|
import { ArrowRightLeft, Droplet, Settings } from 'lucide-react';
|
||||||
import { isFounderWallet } from '@/utils/auth';
|
import { isFounderWallet } from '@pezkuwi/utils/auth';
|
||||||
|
|
||||||
export const DEXDashboard: React.FC = () => {
|
export const DEXDashboard: React.FC = () => {
|
||||||
const { account } = useWallet();
|
const { account } = useWallet();
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { TrendingUp, Droplet, BarChart3, Search, Plus } from 'lucide-react';
|
import { TrendingUp, Droplet, BarChart3, Search, Plus } from 'lucide-react';
|
||||||
import { PoolInfo } from '@/types/dex';
|
import { PoolInfo } from '@/types/dex';
|
||||||
import { fetchPools, formatTokenBalance } from '@/utils/dex';
|
import { fetchPools, formatTokenBalance } from '@pezkuwi/utils/dex';
|
||||||
import { isFounderWallet } from '@/utils/auth';
|
import { isFounderWallet } from '@pezkuwi/utils/auth';
|
||||||
|
|
||||||
interface PoolBrowserProps {
|
interface PoolBrowserProps {
|
||||||
onAddLiquidity?: (pool: PoolInfo) => void;
|
onAddLiquidity?: (pool: PoolInfo) => void;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useWallet } from '@/contexts/WalletContext';
|
|||||||
import { X, Minus, AlertCircle, Loader2, CheckCircle, Info } from 'lucide-react';
|
import { X, Minus, AlertCircle, Loader2, CheckCircle, Info } from 'lucide-react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { PoolInfo } from '@/types/dex';
|
import { PoolInfo } from '@/types/dex';
|
||||||
import { formatTokenBalance } from '@/utils/dex';
|
import { formatTokenBalance } from '@pezkuwi/utils/dex';
|
||||||
|
|
||||||
interface RemoveLiquidityModalProps {
|
interface RemoveLiquidityModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
formatTokenBalance,
|
formatTokenBalance,
|
||||||
getAmountOut,
|
getAmountOut,
|
||||||
calculatePriceImpact,
|
calculatePriceImpact,
|
||||||
} from '@/utils/dex';
|
} from '@pezkuwi/utils/dex';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
|
||||||
interface SwapInterfaceProps {
|
interface SwapInterfaceProps {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';
|
|||||||
import { Badge } from '../ui/badge';
|
import { Badge } from '../ui/badge';
|
||||||
import { Progress } from '../ui/progress';
|
import { Progress } from '../ui/progress';
|
||||||
import { usePolkadot } from '../../contexts/PolkadotContext';
|
import { usePolkadot } from '../../contexts/PolkadotContext';
|
||||||
import { formatBalance } from '../../lib/wallet';
|
import { formatBalance } from '../@pezkuwi/lib/wallet';
|
||||||
|
|
||||||
interface GovernanceStats {
|
interface GovernanceStats {
|
||||||
activeProposals: number;
|
activeProposals: number;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
getCurrentEra,
|
getCurrentEra,
|
||||||
parseAmount,
|
parseAmount,
|
||||||
type StakingInfo
|
type StakingInfo
|
||||||
} from '@/lib/staking';
|
} from '@pezkuwi/lib/staking';
|
||||||
|
|
||||||
export const StakingDashboard: React.FC = () => {
|
export const StakingDashboard: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { useWallet } from '@/contexts/WalletContext';
|
import { useWallet } from '@/contexts/WalletContext';
|
||||||
import { formatAddress, formatBalance } from '@/lib/wallet';
|
import { formatAddress, formatBalance } from '@pezkuwi/lib/wallet';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
|
||||||
export const WalletButton: React.FC = () => {
|
export const WalletButton: React.FC = () => {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||||
import { formatAddress } from '@/lib/wallet';
|
import { formatAddress } from '@pezkuwi/lib/wallet';
|
||||||
import { getAllScores, type UserScores } from '@/lib/scores';
|
import { getAllScores, type UserScores } from '@pezkuwi/lib/scores';
|
||||||
|
|
||||||
interface WalletModalProps {
|
interface WalletModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
generateZKProof,
|
generateZKProof,
|
||||||
DEFAULT_BADGES,
|
DEFAULT_BADGES,
|
||||||
ROLES
|
ROLES
|
||||||
} from '@/lib/identity';
|
} from '@pezkuwi/lib/identity';
|
||||||
|
|
||||||
interface IdentityContextType {
|
interface IdentityContextType {
|
||||||
profile: IdentityProfile | null;
|
profile: IdentityProfile | null;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
||||||
import { usePolkadot } from './PolkadotContext';
|
import { usePolkadot } from './PolkadotContext';
|
||||||
import { WALLET_ERRORS, formatBalance, ASSET_IDS } from '@/lib/wallet';
|
import { WALLET_ERRORS, formatBalance, ASSET_IDS } from '@pezkuwi/lib/wallet';
|
||||||
import type { InjectedAccountWithMeta } from '@polkadot/extension-inject/types';
|
import type { InjectedAccountWithMeta } from '@polkadot/extension-inject/types';
|
||||||
import type { Signer } from '@polkadot/api/types';
|
import type { Signer } from '@polkadot/api/types';
|
||||||
import { web3FromAddress } from '@polkadot/extension-dapp';
|
import { web3FromAddress } from '@polkadot/extension-dapp';
|
||||||
|
|||||||
@@ -1,404 +0,0 @@
|
|||||||
// ========================================
|
|
||||||
// Citizenship Crypto Utilities
|
|
||||||
// ========================================
|
|
||||||
// Handles encryption, hashing, signatures for citizenship data
|
|
||||||
|
|
||||||
import { web3FromAddress } from '@polkadot/extension-dapp';
|
|
||||||
import { stringToHex, hexToU8a, u8aToHex, stringToU8a } from '@polkadot/util';
|
|
||||||
import { decodeAddress, signatureVerify, cryptoWaitReady } from '@polkadot/util-crypto';
|
|
||||||
import type { InjectedAccountWithMeta } from '@polkadot/extension-inject/types';
|
|
||||||
import type { CitizenshipData } from './citizenship-workflow';
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// HASHING FUNCTIONS
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate SHA-256 hash from data
|
|
||||||
*/
|
|
||||||
export async function generateHash(data: string): Promise<string> {
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
const dataBuffer = encoder.encode(data);
|
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer);
|
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
||||||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
||||||
return `0x${hashHex}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate commitment hash from citizenship data
|
|
||||||
*/
|
|
||||||
export async function generateCommitmentHash(
|
|
||||||
data: CitizenshipData
|
|
||||||
): Promise<string> {
|
|
||||||
const dataString = JSON.stringify({
|
|
||||||
fullName: data.fullName,
|
|
||||||
fatherName: data.fatherName,
|
|
||||||
grandfatherName: data.grandfatherName,
|
|
||||||
motherName: data.motherName,
|
|
||||||
tribe: data.tribe,
|
|
||||||
maritalStatus: data.maritalStatus,
|
|
||||||
childrenCount: data.childrenCount,
|
|
||||||
children: data.children,
|
|
||||||
region: data.region,
|
|
||||||
email: data.email,
|
|
||||||
profession: data.profession,
|
|
||||||
referralCode: data.referralCode,
|
|
||||||
walletAddress: data.walletAddress,
|
|
||||||
timestamp: data.timestamp
|
|
||||||
});
|
|
||||||
|
|
||||||
return generateHash(dataString);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate nullifier hash (prevents double-registration)
|
|
||||||
*/
|
|
||||||
export async function generateNullifierHash(
|
|
||||||
walletAddress: string,
|
|
||||||
timestamp: number
|
|
||||||
): Promise<string> {
|
|
||||||
const nullifierData = `${walletAddress}-${timestamp}-nullifier`;
|
|
||||||
return generateHash(nullifierData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// ENCRYPTION / DECRYPTION (AES-GCM)
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derive encryption key from wallet address
|
|
||||||
* NOTE: For MVP, we use a deterministic key. For production, use proper key derivation
|
|
||||||
*/
|
|
||||||
async function deriveEncryptionKey(walletAddress: string): Promise<CryptoKey> {
|
|
||||||
// Create a deterministic seed from wallet address
|
|
||||||
const seed = await generateHash(walletAddress);
|
|
||||||
|
|
||||||
// Convert hex to ArrayBuffer
|
|
||||||
const keyMaterial = hexToU8a(seed).slice(0, 32); // 256-bit key
|
|
||||||
|
|
||||||
// Import as AES-GCM key
|
|
||||||
return crypto.subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
keyMaterial,
|
|
||||||
{ name: 'AES-GCM', length: 256 },
|
|
||||||
false,
|
|
||||||
['encrypt', 'decrypt']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt citizenship data
|
|
||||||
*/
|
|
||||||
export async function encryptData(
|
|
||||||
data: CitizenshipData,
|
|
||||||
walletAddress: string
|
|
||||||
): Promise<string> {
|
|
||||||
try {
|
|
||||||
const key = await deriveEncryptionKey(walletAddress);
|
|
||||||
|
|
||||||
// Generate random IV (Initialization Vector)
|
|
||||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
||||||
|
|
||||||
// Encrypt data
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
const dataBuffer = encoder.encode(JSON.stringify(data));
|
|
||||||
|
|
||||||
const encryptedBuffer = await crypto.subtle.encrypt(
|
|
||||||
{ name: 'AES-GCM', iv },
|
|
||||||
key,
|
|
||||||
dataBuffer
|
|
||||||
);
|
|
||||||
|
|
||||||
// Combine IV + encrypted data
|
|
||||||
const combined = new Uint8Array(iv.length + encryptedBuffer.byteLength);
|
|
||||||
combined.set(iv, 0);
|
|
||||||
combined.set(new Uint8Array(encryptedBuffer), iv.length);
|
|
||||||
|
|
||||||
// Convert to hex
|
|
||||||
return u8aToHex(combined);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Encryption error:', error);
|
|
||||||
throw new Error('Failed to encrypt data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt citizenship data
|
|
||||||
*/
|
|
||||||
export async function decryptData(
|
|
||||||
encryptedHex: string,
|
|
||||||
walletAddress: string
|
|
||||||
): Promise<CitizenshipData> {
|
|
||||||
try {
|
|
||||||
const key = await deriveEncryptionKey(walletAddress);
|
|
||||||
|
|
||||||
// Convert hex to Uint8Array
|
|
||||||
const combined = hexToU8a(encryptedHex);
|
|
||||||
|
|
||||||
// Extract IV and encrypted data
|
|
||||||
const iv = combined.slice(0, 12);
|
|
||||||
const encryptedData = combined.slice(12);
|
|
||||||
|
|
||||||
// Decrypt
|
|
||||||
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
||||||
{ name: 'AES-GCM', iv },
|
|
||||||
key,
|
|
||||||
encryptedData
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert to string and parse JSON
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
const decryptedString = decoder.decode(decryptedBuffer);
|
|
||||||
|
|
||||||
return JSON.parse(decryptedString) as CitizenshipData;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Decryption error:', error);
|
|
||||||
throw new Error('Failed to decrypt data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// SIGNATURE GENERATION & VERIFICATION
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
export interface AuthChallenge {
|
|
||||||
nonce: string; // Random UUID
|
|
||||||
timestamp: number; // Current timestamp
|
|
||||||
tikiNumber: string; // NFT number to prove
|
|
||||||
expiresAt: number; // Expiry timestamp (5 min)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate authentication challenge
|
|
||||||
*/
|
|
||||||
export function generateAuthChallenge(tikiNumber: string): AuthChallenge {
|
|
||||||
const now = Date.now();
|
|
||||||
const nonce = crypto.randomUUID();
|
|
||||||
|
|
||||||
return {
|
|
||||||
nonce,
|
|
||||||
timestamp: now,
|
|
||||||
tikiNumber,
|
|
||||||
expiresAt: now + (5 * 60 * 1000) // 5 minutes
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format challenge message for signing
|
|
||||||
*/
|
|
||||||
export function formatChallengeMessage(challenge: AuthChallenge): string {
|
|
||||||
return `Prove ownership of Welati Tiki #${challenge.tikiNumber}
|
|
||||||
|
|
||||||
Nonce: ${challenge.nonce}
|
|
||||||
Timestamp: ${challenge.timestamp}
|
|
||||||
Expires: ${new Date(challenge.expiresAt).toISOString()}
|
|
||||||
|
|
||||||
By signing this message, you prove you control the wallet that owns this Tiki NFT.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign authentication challenge with wallet
|
|
||||||
*/
|
|
||||||
export async function signChallenge(
|
|
||||||
account: InjectedAccountWithMeta,
|
|
||||||
challenge: AuthChallenge
|
|
||||||
): Promise<string> {
|
|
||||||
try {
|
|
||||||
await cryptoWaitReady();
|
|
||||||
|
|
||||||
const injector = await web3FromAddress(account.address);
|
|
||||||
const signRaw = injector?.signer?.signRaw;
|
|
||||||
|
|
||||||
if (!signRaw) {
|
|
||||||
throw new Error('Signer not available');
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = formatChallengeMessage(challenge);
|
|
||||||
|
|
||||||
const { signature } = await signRaw({
|
|
||||||
address: account.address,
|
|
||||||
data: stringToHex(message),
|
|
||||||
type: 'bytes'
|
|
||||||
});
|
|
||||||
|
|
||||||
return signature;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Signature error:', error);
|
|
||||||
throw new Error('Failed to sign challenge');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify signature
|
|
||||||
*/
|
|
||||||
export async function verifySignature(
|
|
||||||
signature: string,
|
|
||||||
challenge: AuthChallenge,
|
|
||||||
expectedAddress: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
await cryptoWaitReady();
|
|
||||||
|
|
||||||
// Check if challenge has expired
|
|
||||||
if (Date.now() > challenge.expiresAt) {
|
|
||||||
console.warn('Challenge has expired');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = formatChallengeMessage(challenge);
|
|
||||||
const messageU8a = stringToU8a(message);
|
|
||||||
const signatureU8a = hexToU8a(signature);
|
|
||||||
const publicKey = decodeAddress(expectedAddress);
|
|
||||||
|
|
||||||
const result = signatureVerify(messageU8a, signatureU8a, publicKey);
|
|
||||||
|
|
||||||
return result.isValid;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Verification error:', error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// LOCAL STORAGE UTILITIES
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
const STORAGE_KEY_PREFIX = 'pezkuwi_citizen_';
|
|
||||||
|
|
||||||
export interface CitizenSession {
|
|
||||||
tikiNumber: string;
|
|
||||||
walletAddress: string;
|
|
||||||
sessionToken: string; // JWT-like token
|
|
||||||
encryptedDataCID?: string; // IPFS CID
|
|
||||||
lastAuthenticated: number; // Timestamp
|
|
||||||
expiresAt: number; // Session expiry (24h)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save encrypted citizen session to localStorage
|
|
||||||
*/
|
|
||||||
export async function saveCitizenSession(session: CitizenSession): Promise<void> {
|
|
||||||
try {
|
|
||||||
const sessionJson = JSON.stringify(session);
|
|
||||||
const sessionKey = `${STORAGE_KEY_PREFIX}session`;
|
|
||||||
|
|
||||||
// For MVP, store plainly. For production, encrypt with device key
|
|
||||||
localStorage.setItem(sessionKey, sessionJson);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error saving session:', error);
|
|
||||||
throw new Error('Failed to save session');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load citizen session from localStorage
|
|
||||||
*/
|
|
||||||
export function loadCitizenSession(): CitizenSession | null {
|
|
||||||
try {
|
|
||||||
const sessionKey = `${STORAGE_KEY_PREFIX}session`;
|
|
||||||
const sessionJson = localStorage.getItem(sessionKey);
|
|
||||||
|
|
||||||
if (!sessionJson) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const session = JSON.parse(sessionJson) as CitizenSession;
|
|
||||||
|
|
||||||
// Check if session has expired
|
|
||||||
if (Date.now() > session.expiresAt) {
|
|
||||||
clearCitizenSession();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading session:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear citizen session from localStorage
|
|
||||||
*/
|
|
||||||
export function clearCitizenSession(): void {
|
|
||||||
try {
|
|
||||||
const sessionKey = `${STORAGE_KEY_PREFIX}session`;
|
|
||||||
localStorage.removeItem(sessionKey);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error clearing session:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save encrypted citizenship data to localStorage (backup)
|
|
||||||
*/
|
|
||||||
export async function saveLocalCitizenshipData(
|
|
||||||
data: CitizenshipData,
|
|
||||||
walletAddress: string
|
|
||||||
): Promise<void> {
|
|
||||||
try {
|
|
||||||
const encrypted = await encryptData(data, walletAddress);
|
|
||||||
const dataKey = `${STORAGE_KEY_PREFIX}data_${walletAddress}`;
|
|
||||||
|
|
||||||
localStorage.setItem(dataKey, encrypted);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error saving citizenship data:', error);
|
|
||||||
throw new Error('Failed to save citizenship data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load encrypted citizenship data from localStorage
|
|
||||||
*/
|
|
||||||
export async function loadLocalCitizenshipData(
|
|
||||||
walletAddress: string
|
|
||||||
): Promise<CitizenshipData | null> {
|
|
||||||
try {
|
|
||||||
const dataKey = `${STORAGE_KEY_PREFIX}data_${walletAddress}`;
|
|
||||||
const encrypted = localStorage.getItem(dataKey);
|
|
||||||
|
|
||||||
if (!encrypted) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await decryptData(encrypted, walletAddress);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading citizenship data:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// IPFS UTILITIES (Placeholder)
|
|
||||||
// ========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload encrypted data to IPFS
|
|
||||||
* NOTE: This is a placeholder. Implement with actual IPFS client (Pinata, Web3.Storage, etc.)
|
|
||||||
*/
|
|
||||||
export async function uploadToIPFS(encryptedData: string): Promise<string> {
|
|
||||||
// TODO: Implement actual IPFS upload
|
|
||||||
// For MVP, we can use Pinata API or Web3.Storage
|
|
||||||
|
|
||||||
console.warn('IPFS upload not yet implemented. Using mock CID.');
|
|
||||||
|
|
||||||
// Mock CID for development
|
|
||||||
const mockCid = `Qm${Math.random().toString(36).substring(2, 15)}`;
|
|
||||||
|
|
||||||
return mockCid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch encrypted data from IPFS
|
|
||||||
* NOTE: This is a placeholder. Implement with actual IPFS client
|
|
||||||
*/
|
|
||||||
export async function fetchFromIPFS(cid: string): Promise<string> {
|
|
||||||
// TODO: Implement actual IPFS fetch
|
|
||||||
// For MVP, use public IPFS gateways or dedicated service
|
|
||||||
|
|
||||||
console.warn('IPFS fetch not yet implemented. Returning mock data.');
|
|
||||||
|
|
||||||
// Mock encrypted data
|
|
||||||
return '0x000000000000000000000000';
|
|
||||||
}
|
|
||||||
+7
-17
@@ -1,24 +1,14 @@
|
|||||||
import { clsx, type ClassValue } from "clsx"
|
import { clsx, type ClassValue } from "clsx"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web-specific className utility (uses Tailwind merge)
|
||||||
|
*/
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatNumber(value: number, decimals: number = 2): string {
|
/**
|
||||||
if (value === 0) return '0';
|
* Re-export formatNumber from shared utils
|
||||||
if (value < 0.01) return '<0.01';
|
*/
|
||||||
|
export { formatNumber } from '@pezkuwi/utils/format';
|
||||||
// For large numbers, use K, M, B suffixes
|
|
||||||
if (value >= 1e9) {
|
|
||||||
return (value / 1e9).toFixed(decimals) + 'B';
|
|
||||||
}
|
|
||||||
if (value >= 1e6) {
|
|
||||||
return (value / 1e6).toFixed(decimals) + 'M';
|
|
||||||
}
|
|
||||||
if (value >= 1e3) {
|
|
||||||
return (value / 1e3).toFixed(decimals) + 'K';
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.toFixed(decimals);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { usePolkadot } from '@/contexts/PolkadotContext';
|
|||||||
import { supabase } from '@/lib/supabase';
|
import { supabase } from '@/lib/supabase';
|
||||||
import { User, Mail, Phone, Globe, MapPin, Calendar, Shield, AlertCircle, ArrowLeft, Award, Users, TrendingUp } from 'lucide-react';
|
import { User, Mail, Phone, Globe, MapPin, Calendar, Shield, AlertCircle, ArrowLeft, Award, Users, TrendingUp } from 'lucide-react';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { fetchUserTikis, calculateTikiScore, getPrimaryRole, getTikiDisplayName, getTikiColor, getTikiEmoji, getUserRoleCategories } from '@/lib/tiki';
|
import { fetchUserTikis, calculateTikiScore, getPrimaryRole, getTikiDisplayName, getTikiColor, getTikiEmoji, getUserRoleCategories } from '@pezkuwi/lib/tiki';
|
||||||
import { getAllScores, type UserScores } from '@/lib/scores';
|
import { getAllScores, type UserScores } from '@pezkuwi/lib/scores';
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|||||||
+5
-1
@@ -8,7 +8,11 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"],
|
||||||
"@pezkuwi/i18n": ["../shared/i18n"]
|
"@pezkuwi/i18n": ["../shared/i18n"],
|
||||||
|
"@pezkuwi/lib": ["../shared/lib"],
|
||||||
|
"@pezkuwi/utils": ["../shared/utils"],
|
||||||
|
"@pezkuwi/theme": ["../shared/theme"],
|
||||||
|
"@pezkuwi/types": ["../shared/types"]
|
||||||
},
|
},
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"noUnusedParameters": false,
|
"noUnusedParameters": false,
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ export default defineConfig(({ mode }) => ({
|
|||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "./src"),
|
"@": path.resolve(__dirname, "./src"),
|
||||||
"@pezkuwi/i18n": path.resolve(__dirname, "../shared/i18n"),
|
"@pezkuwi/i18n": path.resolve(__dirname, "../shared/i18n"),
|
||||||
|
"@pezkuwi/lib": path.resolve(__dirname, "../shared/lib"),
|
||||||
|
"@pezkuwi/utils": path.resolve(__dirname, "../shared/utils"),
|
||||||
|
"@pezkuwi/theme": path.resolve(__dirname, "../shared/theme"),
|
||||||
|
"@pezkuwi/types": path.resolve(__dirname, "../shared/types"),
|
||||||
|
'buffer': 'buffer/',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
json: {
|
json: {
|
||||||
|
|||||||
Reference in New Issue
Block a user