| import React, { memo } from 'react'; |
| import { Target, TrendingUp, Trash2, Calendar, Plus, CheckCircle } from 'lucide-react'; |
|
|
| const SavingsGoalsWidget = memo(({ goals, currencySymbol, onAddGoal, onDeleteGoal, onUpdateGoal }) => { |
| |
| const availableBalance = goals.length > 0 ? goals[0].current_amount : 0; |
|
|
| return ( |
| <div className="glass-panel" style={{ padding: '2rem', marginBottom: '2rem', background: 'rgba(15, 23, 42, 0.6)' }}> |
| <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2.5rem' }}> |
| <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}> |
| <div style={{ |
| width: '48px', |
| height: '48px', |
| borderRadius: '14px', |
| background: 'linear-gradient(135deg, rgba(99, 102, 241, 0.2), rgba(139, 92, 246, 0.2))', |
| display: 'flex', |
| alignItems: 'center', |
| justifyContent: 'center', |
| color: '#818cf8', |
| boxShadow: '0 8px 16px -4px rgba(99, 102, 241, 0.2)' |
| }}> |
| <Target size={28} /> |
| </div> |
| <div> |
| <h3 style={{ margin: 0, fontSize: '1.4rem', fontWeight: 'bold' }}>Financial Goals</h3> |
| <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginTop: '0.2rem' }}> |
| <span style={{ fontSize: '0.9rem', color: 'var(--text-muted)' }}>Available Balance:</span> |
| <span style={{ fontSize: '1rem', fontWeight: 'bold', color: '#10b981' }}>{currencySymbol}{availableBalance.toLocaleString()}</span> |
| </div> |
| </div> |
| </div> |
| <button |
| onClick={onAddGoal} |
| className="btn-primary" |
| style={{ |
| display: 'flex', |
| alignItems: 'center', |
| gap: '0.6rem', |
| background: 'linear-gradient(135deg, #6366f1, #8b5cf6)', |
| border: 'none', |
| color: 'white', |
| padding: '0.75rem 1.5rem', |
| borderRadius: '0.8rem', |
| fontWeight: '600', |
| boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' |
| }} |
| > |
| <Plus size={18} /> Set New Goal |
| </button> |
| </div> |
| |
| <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', gap: '2rem' }}> |
| {goals.length === 0 ? ( |
| <div style={{ |
| gridColumn: '1 / -1', |
| textAlign: 'center', |
| padding: '4rem 2rem', |
| background: 'rgba(255,255,255,0.02)', |
| borderRadius: '1.5rem', |
| border: '1px dashed rgba(255,255,255,0.1)' |
| }}> |
| <div style={{ fontSize: '3rem', marginBottom: '1.5rem' }}>🎯</div> |
| <h4 style={{ margin: '0 0 0.5rem 0', fontSize: '1.2rem' }}>No goals active</h4> |
| <p style={{ color: 'var(--text-muted)', margin: 0 }}>Start by setting a target for your savings!</p> |
| </div> |
| ) : ( |
| goals.map((goal) => { |
| const remaining = goal.target_amount - goal.current_amount; |
| const isAchieved = goal.current_amount >= goal.target_amount; |
| |
| return ( |
| <div key={goal.id} className="hover-lift" style={{ |
| padding: '1.75rem', |
| background: isAchieved |
| ? `linear-gradient(145deg, rgba(16, 185, 129, 0.1), rgba(30, 41, 59, 0.4))` |
| : 'rgba(30, 41, 59, 0.4)', |
| borderRadius: '1.5rem', |
| border: `1px solid ${isAchieved ? '#10b98140' : goal.color + '20'}`, |
| position: 'relative', |
| overflow: 'hidden', |
| transition: 'all 0.3s ease' |
| }}> |
| {/* Achievement Glow */} |
| {isAchieved && ( |
| <div style={{ |
| position: 'absolute', |
| top: '-20%', |
| right: '-20%', |
| width: '150px', |
| height: '150px', |
| background: 'radial-gradient(circle, rgba(16, 185, 129, 0.1) 0%, transparent 70%)', |
| pointerEvents: 'none' |
| }} /> |
| )} |
| |
| <div style={{ position: 'absolute', top: '1.25rem', right: '1.25rem' }}> |
| <button |
| onClick={() => onDeleteGoal(goal.id)} |
| style={{ |
| background: 'rgba(239, 68, 68, 0.1)', |
| border: 'none', |
| color: '#ef4444', |
| padding: '0.4rem', |
| borderRadius: '0.6rem', |
| cursor: 'pointer', |
| opacity: 0.6, |
| transition: 'opacity 0.2s' |
| }} |
| onMouseEnter={(e) => e.currentTarget.style.opacity = '1'} |
| onMouseLeave={(e) => e.currentTarget.style.opacity = '0.6'} |
| > |
| <Trash2 size={16} /> |
| </button> |
| </div> |
| |
| <div style={{ display: 'flex', alignItems: 'center', gap: '1.25rem', marginBottom: '1.5rem' }}> |
| <div style={{ |
| width: '56px', |
| height: '56px', |
| borderRadius: '16px', |
| background: isAchieved ? 'rgba(16, 185, 129, 0.15)' : `${goal.color}15`, |
| display: 'flex', |
| alignItems: 'center', |
| justifyContent: 'center', |
| color: isAchieved ? '#10b981' : goal.color, |
| fontSize: '1.5rem', |
| boxShadow: `0 4px 12px ${isAchieved ? 'rgba(16, 185, 129, 0.2)' : goal.color + '20'}` |
| }}> |
| {isAchieved ? '✨' : <TrendingUp size={28} />} |
| </div> |
| <div> |
| <h4 style={{ margin: 0, fontSize: '1.2rem', fontWeight: 'bold' }}>{goal.name}</h4> |
| <div style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', fontSize: '0.85rem', color: 'var(--text-muted)', marginTop: '0.2rem' }}> |
| <Calendar size={14} /> |
| <span>Target: {goal.target_date || 'Ongoing'}</span> |
| </div> |
| </div> |
| </div> |
| |
| <div style={{ marginBottom: '1.5rem' }}> |
| <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '0.6rem', fontSize: '0.95rem' }}> |
| <span style={{ color: 'var(--text-muted)' }}>Progress</span> |
| <span style={{ fontWeight: '700', color: isAchieved ? '#10b981' : 'white' }}> |
| {goal.progress_percentage.toFixed(0)}% |
| </span> |
| </div> |
| <div style={{ height: '10px', background: 'rgba(255,255,255,0.05)', borderRadius: '5px', overflow: 'hidden' }}> |
| <div style={{ |
| height: '100%', |
| width: `${goal.progress_percentage}%`, |
| background: isAchieved |
| ? 'linear-gradient(90deg, #10b981, #34d399)' |
| : `linear-gradient(90deg, ${goal.color}, ${goal.color}cc)`, |
| borderRadius: '5px', |
| transition: 'width 1s cubic-bezier(0.34, 1.56, 0.64, 1)', |
| boxShadow: isAchieved ? '0 0 15px rgba(16, 185, 129, 0.4)' : `0 0 10px ${goal.color}40` |
| }} /> |
| </div> |
| <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '0.6rem', fontSize: '0.85rem' }}> |
| <span style={{ color: 'var(--text-muted)' }}>Current: {currencySymbol}{goal.current_amount.toLocaleString()}</span> |
| <span style={{ color: 'var(--text-muted)' }}>Goal: {currencySymbol}{goal.target_amount.toLocaleString()}</span> |
| </div> |
| </div> |
| |
| {/* Personalized Feedback */} |
| <div style={{ |
| padding: '1rem', |
| borderRadius: '1rem', |
| background: isAchieved ? 'rgba(16, 185, 129, 0.1)' : 'rgba(255, 255, 255, 0.03)', |
| border: `1px solid ${isAchieved ? '#10b98130' : 'rgba(255, 255, 255, 0.05)'}`, |
| textAlign: 'center', |
| animation: isAchieved ? 'pulse-success 2s infinite' : 'none' |
| }}> |
| {isAchieved ? ( |
| <div style={{ color: '#10b981', fontWeight: 'bold', fontSize: '0.95rem' }}> |
| 🎉 Congratulations! Goal Achieved! |
| <p style={{ margin: '0.2rem 0 0 0', fontSize: '0.8rem', opacity: 0.8, fontWeight: 'normal' }}> |
| You've successfully saved enough for this milestone. |
| </p> |
| </div> |
| ) : ( |
| <div style={{ color: 'white', fontWeight: '500', fontSize: '0.9rem' }}> |
| 🚀 Keep going! |
| <p style={{ margin: '0.2rem 0 0 0', fontSize: '0.85rem', color: 'var(--text-muted)' }}> |
| Save <span style={{ color: '#f59e0b', fontWeight: 'bold' }}>{currencySymbol}{remaining.toLocaleString()}</span> more to reach your goal. |
| </p> |
| </div> |
| )} |
| </div> |
| </div> |
| ); |
| }) |
| )} |
| </div> |
| |
| <style dangerouslySetInnerHTML={{ |
| __html: ` |
| @keyframes pulse-success { |
| 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); } |
| 70% { box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); } |
| 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); } |
| } |
| .hover-lift:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.2); |
| } |
| `}} /> |
| </div> |
| ); |
| }); |
|
|
| export default SavingsGoalsWidget; |
|
|