mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-29 08:47:55 +00:00
feat: complete i18n support for all components (6 languages)
Add full internationalization across 127+ components and pages. 790+ translation keys in en, tr, kmr, ckb, ar, fa locales. Remove duplicate keys and delete unused .json locale files.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -6,14 +7,14 @@ import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import {
|
||||
Plus,
|
||||
Trash2,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import {
|
||||
Plus,
|
||||
Trash2,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AlertCircle
|
||||
} from 'lucide-react';
|
||||
|
||||
@@ -34,6 +35,7 @@ interface Milestone {
|
||||
}
|
||||
|
||||
export const FundingProposal: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [proposalTitle, setProposalTitle] = useState('');
|
||||
const [proposalDescription, setProposalDescription] = useState('');
|
||||
const [category, setCategory] = useState('');
|
||||
@@ -92,42 +94,42 @@ export const FundingProposal: React.FC = () => {
|
||||
{/* Proposal Header */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Create Funding Proposal</CardTitle>
|
||||
<CardDescription>Submit a detailed budget request for treasury funding</CardDescription>
|
||||
<CardTitle>{t('funding.createTitle')}</CardTitle>
|
||||
<CardDescription>{t('funding.createDesc')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="title">Proposal Title</Label>
|
||||
<Label htmlFor="title">{t('funding.proposalTitle')}</Label>
|
||||
<Input
|
||||
id="title"
|
||||
placeholder="Enter a clear, descriptive title"
|
||||
placeholder={t('funding.titlePlaceholder')}
|
||||
value={proposalTitle}
|
||||
onChange={(e) => setProposalTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="category">Category</Label>
|
||||
<Label htmlFor="category">{t('funding.category')}</Label>
|
||||
<Select value={category} onValueChange={setCategory}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select category" />
|
||||
<SelectValue placeholder={t('funding.selectCategory')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="development">Development</SelectItem>
|
||||
<SelectItem value="marketing">Marketing</SelectItem>
|
||||
<SelectItem value="operations">Operations</SelectItem>
|
||||
<SelectItem value="community">Community</SelectItem>
|
||||
<SelectItem value="research">Research</SelectItem>
|
||||
<SelectItem value="infrastructure">Infrastructure</SelectItem>
|
||||
<SelectItem value="development">{t('funding.catDevelopment')}</SelectItem>
|
||||
<SelectItem value="marketing">{t('funding.catMarketing')}</SelectItem>
|
||||
<SelectItem value="operations">{t('funding.catOperations')}</SelectItem>
|
||||
<SelectItem value="community">{t('funding.catCommunity')}</SelectItem>
|
||||
<SelectItem value="research">{t('funding.catResearch')}</SelectItem>
|
||||
<SelectItem value="infrastructure">{t('funding.catInfrastructure')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Label htmlFor="description">{t('funding.description')}</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
placeholder="Provide a detailed description of the proposal"
|
||||
placeholder={t('funding.descPlaceholder')}
|
||||
rows={4}
|
||||
value={proposalDescription}
|
||||
onChange={(e) => setProposalDescription(e.target.value)}
|
||||
@@ -140,9 +142,9 @@ export const FundingProposal: React.FC = () => {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<span>Budget Breakdown</span>
|
||||
<span>{t('funding.budgetBreakdown')}</span>
|
||||
<Badge variant="outline" className="text-lg px-3 py-1">
|
||||
Total: ${totalBudget.toLocaleString()}
|
||||
{t('funding.total', { amount: totalBudget.toLocaleString() })}
|
||||
</Badge>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -150,7 +152,7 @@ export const FundingProposal: React.FC = () => {
|
||||
{budgetItems.map((item, index) => (
|
||||
<div key={item.id} className="p-4 border rounded-lg space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">Item {index + 1}</span>
|
||||
<span className="font-medium">{t('funding.item', { index: index + 1 })}</span>
|
||||
{budgetItems.length > 1 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -164,16 +166,16 @@ export const FundingProposal: React.FC = () => {
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label>Description</Label>
|
||||
<Label>{t('funding.itemDesc')}</Label>
|
||||
<Input
|
||||
placeholder="Budget item description"
|
||||
placeholder={t('funding.itemDescPlaceholder')}
|
||||
value={item.description}
|
||||
onChange={(e) => updateBudgetItem(item.id, 'description', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Amount ($)</Label>
|
||||
<Label>{t('funding.amountUsd')}</Label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="0"
|
||||
@@ -184,9 +186,9 @@ export const FundingProposal: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Justification</Label>
|
||||
<Label>{t('funding.justification')}</Label>
|
||||
<Textarea
|
||||
placeholder="Explain why this expense is necessary"
|
||||
placeholder={t('funding.justificationPlaceholder')}
|
||||
rows={2}
|
||||
value={item.justification}
|
||||
onChange={(e) => updateBudgetItem(item.id, 'justification', e.target.value)}
|
||||
@@ -197,7 +199,7 @@ export const FundingProposal: React.FC = () => {
|
||||
|
||||
<Button onClick={addBudgetItem} variant="outline" className="w-full">
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Budget Item
|
||||
{t('funding.addBudgetItem')}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -205,14 +207,14 @@ export const FundingProposal: React.FC = () => {
|
||||
{/* Milestones */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Milestones & Deliverables</CardTitle>
|
||||
<CardDescription>Define clear milestones with payment schedule</CardDescription>
|
||||
<CardTitle>{t('funding.milestones')}</CardTitle>
|
||||
<CardDescription>{t('funding.milestonesDesc')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{milestones.map((milestone, index) => (
|
||||
<div key={milestone.id} className="p-4 border rounded-lg space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">Milestone {index + 1}</span>
|
||||
<span className="font-medium">{t('funding.milestone', { index: index + 1 })}</span>
|
||||
{milestones.length > 1 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -226,16 +228,16 @@ export const FundingProposal: React.FC = () => {
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label>Title</Label>
|
||||
<Label>{t('funding.milestoneTitle')}</Label>
|
||||
<Input
|
||||
placeholder="Milestone title"
|
||||
placeholder={t('funding.milestoneTitlePlaceholder')}
|
||||
value={milestone.title}
|
||||
onChange={(e) => updateMilestone(milestone.id, 'title', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Payment Amount ($)</Label>
|
||||
<Label>{t('funding.paymentAmount')}</Label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="0"
|
||||
@@ -247,9 +249,9 @@ export const FundingProposal: React.FC = () => {
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label>Deliverables</Label>
|
||||
<Label>{t('funding.deliverables')}</Label>
|
||||
<Textarea
|
||||
placeholder="What will be delivered"
|
||||
placeholder={t('funding.deliverablesPlaceholder')}
|
||||
rows={2}
|
||||
value={milestone.deliverables}
|
||||
onChange={(e) => updateMilestone(milestone.id, 'deliverables', e.target.value)}
|
||||
@@ -257,7 +259,7 @@ export const FundingProposal: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Deadline</Label>
|
||||
<Label>{t('funding.deadline')}</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={milestone.deadline}
|
||||
@@ -270,14 +272,14 @@ export const FundingProposal: React.FC = () => {
|
||||
|
||||
<Button onClick={addMilestone} variant="outline" className="w-full">
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Milestone
|
||||
{t('funding.addMilestone')}
|
||||
</Button>
|
||||
|
||||
{totalMilestoneAmount !== totalBudget && totalMilestoneAmount > 0 && (
|
||||
<div className="flex items-center gap-2 p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg text-gray-900">
|
||||
<AlertCircle className="h-5 w-5 text-yellow-600" />
|
||||
<span className="text-sm text-gray-900">
|
||||
Milestone total (${totalMilestoneAmount.toLocaleString()}) doesn't match budget total (${totalBudget.toLocaleString()})
|
||||
{t('funding.mismatchWarning', { milestoneTotal: totalMilestoneAmount.toLocaleString(), budgetTotal: totalBudget.toLocaleString() })}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -286,8 +288,8 @@ export const FundingProposal: React.FC = () => {
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button variant="outline">Save Draft</Button>
|
||||
<Button>Submit Proposal</Button>
|
||||
<Button variant="outline">{t('funding.saveDraft')}</Button>
|
||||
<Button>{t('funding.submitProposal')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import {
|
||||
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
import {
|
||||
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
Clock,
|
||||
Users,
|
||||
AlertTriangle,
|
||||
|
||||
|
||||
DollarSign
|
||||
} from 'lucide-react';
|
||||
|
||||
@@ -36,6 +37,7 @@ interface Approval {
|
||||
}
|
||||
|
||||
export const MultiSigApproval: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState('pending');
|
||||
|
||||
const [approvals] = useState<Approval[]>([
|
||||
@@ -117,22 +119,22 @@ export const MultiSigApproval: React.FC = () => {
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>Deadline: {approval.deadline}</span>
|
||||
<span>{t('msApproval.deadline', { date: approval.deadline })}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span>Approval Progress</span>
|
||||
<span>{t('msApproval.approvalProgress')}</span>
|
||||
<span className="font-medium">
|
||||
{approval.currentSignatures}/{approval.requiredSignatures} signatures
|
||||
{t('msApproval.signatures', { current: approval.currentSignatures, required: approval.requiredSignatures })}
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={progress} className="h-2" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">Signers</p>
|
||||
<p className="text-sm font-medium">{t('msApproval.signers')}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{approval.signers.map((signer, index) => (
|
||||
<div key={index} className="flex items-center gap-2">
|
||||
@@ -153,14 +155,14 @@ export const MultiSigApproval: React.FC = () => {
|
||||
<div className="flex gap-2">
|
||||
<Button className="flex-1" size="sm">
|
||||
<CheckCircle className="h-4 w-4 mr-2" />
|
||||
Approve
|
||||
{t('msApproval.approve')}
|
||||
</Button>
|
||||
<Button variant="outline" className="flex-1" size="sm">
|
||||
<XCircle className="h-4 w-4 mr-2" />
|
||||
Reject
|
||||
{t('msApproval.reject')}
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
View Details
|
||||
{t('msApproval.viewDetails')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -176,7 +178,7 @@ export const MultiSigApproval: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Pending Approvals</p>
|
||||
<p className="text-sm text-muted-foreground">{t('msApproval.pendingApprovals')}</p>
|
||||
<p className="text-2xl font-bold">{pendingApprovals.length}</p>
|
||||
</div>
|
||||
<Clock className="h-8 w-8 text-yellow-500" />
|
||||
@@ -188,7 +190,7 @@ export const MultiSigApproval: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Total Value</p>
|
||||
<p className="text-sm text-muted-foreground">{t('msApproval.totalValue')}</p>
|
||||
<p className="text-2xl font-bold">
|
||||
${(pendingApprovals.reduce((sum, a) => sum + a.amount, 0) / 1000).toFixed(0)}k
|
||||
</p>
|
||||
@@ -202,7 +204,7 @@ export const MultiSigApproval: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Active Signers</p>
|
||||
<p className="text-sm text-muted-foreground">{t('msApproval.activeSigners')}</p>
|
||||
<p className="text-2xl font-bold">5</p>
|
||||
</div>
|
||||
<Users className="h-8 w-8 text-blue-500" />
|
||||
@@ -214,7 +216,7 @@ export const MultiSigApproval: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Expiring Soon</p>
|
||||
<p className="text-sm text-muted-foreground">{t('msApproval.expiringSoon')}</p>
|
||||
<p className="text-2xl font-bold">2</p>
|
||||
</div>
|
||||
<AlertTriangle className="h-8 w-8 text-orange-500" />
|
||||
@@ -227,13 +229,13 @@ export const MultiSigApproval: React.FC = () => {
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="pending">
|
||||
Pending ({pendingApprovals.length})
|
||||
{t('msApproval.pending', { count: pendingApprovals.length })}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="approved">
|
||||
Approved ({approvedApprovals.length})
|
||||
{t('msApproval.approved', { count: approvedApprovals.length })}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="rejected">
|
||||
Rejected ({rejectedApprovals.length})
|
||||
{t('msApproval.rejected', { count: rejectedApprovals.length })}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
@@ -247,7 +249,7 @@ export const MultiSigApproval: React.FC = () => {
|
||||
{approvedApprovals.length === 0 ? (
|
||||
<Card>
|
||||
<CardContent className="p-6 text-center text-muted-foreground">
|
||||
No approved proposals yet
|
||||
{t('msApproval.noApproved')}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
@@ -261,7 +263,7 @@ export const MultiSigApproval: React.FC = () => {
|
||||
{rejectedApprovals.length === 0 ? (
|
||||
<Card>
|
||||
<CardContent className="p-6 text-center text-muted-foreground">
|
||||
No rejected proposals
|
||||
{t('msApproval.noRejected')}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -29,6 +30,7 @@ interface Transaction {
|
||||
}
|
||||
|
||||
export const SpendingHistory: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [filterCategory, setFilterCategory] = useState('all');
|
||||
const [filterStatus, setFilterStatus] = useState('all');
|
||||
@@ -102,11 +104,11 @@ export const SpendingHistory: React.FC = () => {
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return <Badge className="bg-green-600"><CheckCircle className="w-3 h-3 mr-1" />Completed</Badge>;
|
||||
return <Badge className="bg-green-600"><CheckCircle className="w-3 h-3 mr-1" />{t('spending.completed')}</Badge>;
|
||||
case 'pending':
|
||||
return <Badge className="bg-yellow-600"><Clock className="w-3 h-3 mr-1" />Pending</Badge>;
|
||||
return <Badge className="bg-yellow-600"><Clock className="w-3 h-3 mr-1" />{t('spending.pending')}</Badge>;
|
||||
case 'rejected':
|
||||
return <Badge className="bg-red-600"><XCircle className="w-3 h-3 mr-1" />Rejected</Badge>;
|
||||
return <Badge className="bg-red-600"><XCircle className="w-3 h-3 mr-1" />{t('spending.rejected')}</Badge>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -129,9 +131,9 @@ export const SpendingHistory: React.FC = () => {
|
||||
<div className="space-y-6">
|
||||
<Card className="bg-gray-900 border-gray-800">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white">Treasury Spending History</CardTitle>
|
||||
<CardTitle className="text-white">{t('spending.title')}</CardTitle>
|
||||
<CardDescription className="text-gray-400">
|
||||
Track all treasury expenditures and approved proposals
|
||||
{t('spending.description')}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
@@ -141,7 +143,7 @@ export const SpendingHistory: React.FC = () => {
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-3 h-4 w-4 text-gray-500" />
|
||||
<Input
|
||||
placeholder="Search transactions..."
|
||||
placeholder={t('spending.searchPlaceholder')}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10 bg-gray-800 border-gray-700 text-white"
|
||||
@@ -154,11 +156,11 @@ export const SpendingHistory: React.FC = () => {
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-gray-800 border-gray-700">
|
||||
<SelectItem value="all">All Categories</SelectItem>
|
||||
<SelectItem value="Development">Development</SelectItem>
|
||||
<SelectItem value="Marketing">Marketing</SelectItem>
|
||||
<SelectItem value="Infrastructure">Infrastructure</SelectItem>
|
||||
<SelectItem value="Community">Community</SelectItem>
|
||||
<SelectItem value="all">{t('spending.allCategories')}</SelectItem>
|
||||
<SelectItem value="Development">{t('funding.catDevelopment')}</SelectItem>
|
||||
<SelectItem value="Marketing">{t('funding.catMarketing')}</SelectItem>
|
||||
<SelectItem value="Infrastructure">{t('funding.catInfrastructure')}</SelectItem>
|
||||
<SelectItem value="Community">{t('funding.catCommunity')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
@@ -167,16 +169,16 @@ export const SpendingHistory: React.FC = () => {
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-gray-800 border-gray-700">
|
||||
<SelectItem value="all">All Status</SelectItem>
|
||||
<SelectItem value="completed">Completed</SelectItem>
|
||||
<SelectItem value="pending">Pending</SelectItem>
|
||||
<SelectItem value="rejected">Rejected</SelectItem>
|
||||
<SelectItem value="all">{t('spending.allStatus')}</SelectItem>
|
||||
<SelectItem value="completed">{t('spending.completed')}</SelectItem>
|
||||
<SelectItem value="pending">{t('spending.pending')}</SelectItem>
|
||||
<SelectItem value="rejected">{t('spending.rejected')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Button className="bg-blue-600 hover:bg-blue-700">
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Export
|
||||
{t('spending.export')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -184,13 +186,13 @@ export const SpendingHistory: React.FC = () => {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-gray-700 hover:bg-gray-750">
|
||||
<TableHead className="text-gray-400">Date</TableHead>
|
||||
<TableHead className="text-gray-400">Description</TableHead>
|
||||
<TableHead className="text-gray-400">Category</TableHead>
|
||||
<TableHead className="text-gray-400">Amount</TableHead>
|
||||
<TableHead className="text-gray-400">Status</TableHead>
|
||||
<TableHead className="text-gray-400">Proposal ID</TableHead>
|
||||
<TableHead className="text-gray-400">Actions</TableHead>
|
||||
<TableHead className="text-gray-400">{t('spending.date')}</TableHead>
|
||||
<TableHead className="text-gray-400">{t('spending.desc')}</TableHead>
|
||||
<TableHead className="text-gray-400">{t('spending.category')}</TableHead>
|
||||
<TableHead className="text-gray-400">{t('spending.amount')}</TableHead>
|
||||
<TableHead className="text-gray-400">{t('spending.status')}</TableHead>
|
||||
<TableHead className="text-gray-400">{t('spending.proposalId')}</TableHead>
|
||||
<TableHead className="text-gray-400">{t('spending.actions')}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -210,7 +212,7 @@ export const SpendingHistory: React.FC = () => {
|
||||
<TableCell>{getStatusBadge(tx.status)}</TableCell>
|
||||
<TableCell className="text-gray-300 font-mono">{tx.proposalId}</TableCell>
|
||||
<TableCell>
|
||||
<Button variant="ghost" size="sm">View</Button>
|
||||
<Button variant="ghost" size="sm">{t('spending.view')}</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -10,7 +11,7 @@ import {
|
||||
DollarSign,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
|
||||
|
||||
Activity,
|
||||
AlertCircle,
|
||||
CheckCircle,
|
||||
@@ -30,6 +31,7 @@ interface BudgetCategory {
|
||||
}
|
||||
|
||||
export const TreasuryOverview: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { metrics, proposals, loading, error } = useTreasury();
|
||||
|
||||
const [categories] = useState<BudgetCategory[]>([
|
||||
@@ -42,17 +44,17 @@ export const TreasuryOverview: React.FC = () => {
|
||||
]);
|
||||
|
||||
const getHealthStatus = (score: number) => {
|
||||
if (score >= 80) return { label: 'Excellent', color: 'text-green-500', icon: CheckCircle };
|
||||
if (score >= 60) return { label: 'Good', color: 'text-blue-500', icon: Activity };
|
||||
if (score >= 40) return { label: 'Fair', color: 'text-yellow-500', icon: AlertCircle };
|
||||
return { label: 'Critical', color: 'text-red-500', icon: AlertCircle };
|
||||
if (score >= 80) return { label: 'treasury.healthExcellent', color: 'text-green-500', icon: CheckCircle };
|
||||
if (score >= 60) return { label: 'treasury.healthGood', color: 'text-blue-500', icon: Activity };
|
||||
if (score >= 40) return { label: 'treasury.healthFair', color: 'text-yellow-500', icon: AlertCircle };
|
||||
return { label: 'treasury.healthCritical', color: 'text-red-500', icon: AlertCircle };
|
||||
};
|
||||
|
||||
const healthStatus = getHealthStatus(metrics.healthScore);
|
||||
const HealthIcon = healthStatus.icon;
|
||||
|
||||
if (loading) {
|
||||
return <LoadingState message="Loading treasury data from blockchain..." />;
|
||||
return <LoadingState message={t('treasury.loading')} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
@@ -60,7 +62,7 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<Alert variant="destructive">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
Failed to load treasury data: {error}
|
||||
{t('treasury.errorLoad', { error })}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
@@ -72,10 +74,10 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20">
|
||||
<Activity className="h-3 w-3 mr-1" />
|
||||
Live Blockchain Data
|
||||
{t('treasury.liveData')}
|
||||
</Badge>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{proposals.length} active proposals • {metrics.totalBalance.toFixed(2)} HEZ in treasury
|
||||
{t('treasury.activeProposals', { count: proposals.length })} • {t('treasury.hezInTreasury', { amount: metrics.totalBalance.toFixed(2) })}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -83,7 +85,7 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<span>Treasury Health</span>
|
||||
<span>{t('treasury.health')}</span>
|
||||
<HealthIcon className={`h-6 w-6 ${healthStatus.color}`} />
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -91,16 +93,16 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-2xl font-bold">{metrics.healthScore}%</span>
|
||||
<Badge className={healthStatus.color}>{healthStatus.label}</Badge>
|
||||
<Badge className={healthStatus.color}>{t(healthStatus.label)}</Badge>
|
||||
</div>
|
||||
<Progress value={metrics.healthScore} className="h-3" />
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<p className="text-muted-foreground">Runway</p>
|
||||
<p className="font-semibold">20.8 months</p>
|
||||
<p className="text-muted-foreground">{t('treasury.runway')}</p>
|
||||
<p className="font-semibold">{t('treasury.runwayMonths', { months: '20.8' })}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground">Burn Rate</p>
|
||||
<p className="text-muted-foreground">{t('treasury.burnRate')}</p>
|
||||
<p className="font-semibold">$120k/month</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,11 +116,11 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Total Balance</p>
|
||||
<p className="text-sm text-muted-foreground">{t('treasury.totalBalance')}</p>
|
||||
<p className="text-2xl font-bold">${(metrics.totalBalance / 1000000).toFixed(2)}M</p>
|
||||
<p className="text-xs text-green-500 flex items-center mt-1">
|
||||
<ArrowUpRight className="h-3 w-3 mr-1" />
|
||||
+12.5% this month
|
||||
{t('treasury.thisMonth', { percent: '12.5' })}
|
||||
</p>
|
||||
</div>
|
||||
<DollarSign className="h-8 w-8 text-green-500" />
|
||||
@@ -130,11 +132,11 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Monthly Income</p>
|
||||
<p className="text-sm text-muted-foreground">{t('treasury.monthlyIncome')}</p>
|
||||
<p className="text-2xl font-bold">${(metrics.monthlyIncome / 1000).toFixed(0)}k</p>
|
||||
<p className="text-xs text-green-500 flex items-center mt-1">
|
||||
<TrendingUp className="h-3 w-3 mr-1" />
|
||||
+8.3% vs last month
|
||||
{t('treasury.vsLastMonth', { percent: '+8.3' })}
|
||||
</p>
|
||||
</div>
|
||||
<TrendingUp className="h-8 w-8 text-blue-500" />
|
||||
@@ -146,11 +148,11 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Monthly Expenses</p>
|
||||
<p className="text-sm text-muted-foreground">{t('treasury.monthlyExpenses')}</p>
|
||||
<p className="text-2xl font-bold">${(metrics.monthlyExpenses / 1000).toFixed(0)}k</p>
|
||||
<p className="text-xs text-red-500 flex items-center mt-1">
|
||||
<ArrowDownRight className="h-3 w-3 mr-1" />
|
||||
-5.2% vs last month
|
||||
{t('treasury.vsLastMonth', { percent: '-5.2' })}
|
||||
</p>
|
||||
</div>
|
||||
<TrendingDown className="h-8 w-8 text-red-500" />
|
||||
@@ -162,11 +164,11 @@ export const TreasuryOverview: React.FC = () => {
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Pending Proposals</p>
|
||||
<p className="text-sm text-muted-foreground">{t('treasury.pendingProposals')}</p>
|
||||
<p className="text-2xl font-bold">{metrics.pendingProposals}</p>
|
||||
<p className="text-xs text-yellow-500 flex items-center mt-1">
|
||||
<Clock className="h-3 w-3 mr-1" />
|
||||
$450k requested
|
||||
{t('treasury.requested', { amount: '450' })}
|
||||
</p>
|
||||
</div>
|
||||
<Clock className="h-8 w-8 text-yellow-500" />
|
||||
@@ -178,8 +180,8 @@ export const TreasuryOverview: React.FC = () => {
|
||||
{/* Budget Categories */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Budget Allocation by Category</CardTitle>
|
||||
<CardDescription>Current quarter budget utilization</CardDescription>
|
||||
<CardTitle>{t('treasury.budgetAllocation')}</CardTitle>
|
||||
<CardDescription>{t('treasury.quarterUtilization')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
|
||||
Reference in New Issue
Block a user