Compare commits
3 Commits
feature/ne
...
adad656df2
| Author | SHA1 | Date | |
|---|---|---|---|
| adad656df2 | |||
|
|
6f679a5066 | ||
| 0f72d62d3e |
@@ -1,7 +1,6 @@
|
||||
use family_budget::*;
|
||||
use sea_orm::DbErr;
|
||||
use sea_orm_migration::prelude::*;
|
||||
//TODO: НЕУДОБНОЕ РАСПОЛОЖЕНИЕ ДОБАВИТЬ РАСХОД + ИСТОРИЯ, ВОЗВРАЩАЕТ В НАЧАЛО ПОСЛЕ ДОБАВЛЕНИЯ РАСХОДА + ЗАКРЫВАЕТ ДОБАВИТЬ РАСХОД, ИСТОРИЯ НЕ ОБНОВЛЯЕТСЯ
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), DbErr> {
|
||||
let db = establish_connection().await?;
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#667eea" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<title>Family budget</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -16,9 +16,20 @@ function AppContent() {
|
||||
const { user, isAuthenticated, isLoading, setUser, setIsLoading } = useStore();
|
||||
const location = useLocation();
|
||||
|
||||
const themeColors: Record<string, string> = {
|
||||
light: '#667eea',
|
||||
dark: '#000000',
|
||||
sunset: '#f97316',
|
||||
ocean: '#3b82f6',
|
||||
forest: '#22c55e',
|
||||
purple: '#8b5cf6',
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const storedTheme = localStorage.getItem('theme') || 'light';
|
||||
document.documentElement.setAttribute('data-theme', storedTheme);
|
||||
const metaTheme = document.querySelector('meta[name="theme-color"]');
|
||||
if (metaTheme) metaTheme.setAttribute('content', themeColors[storedTheme] ?? '#667eea');
|
||||
|
||||
const storedLocale = localStorage.getItem('locale');
|
||||
if (storedLocale && storedLocale !== i18n.language) {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
html,
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--gradient-start) 0%, var(--gradient-end) 100%);
|
||||
background-attachment: fixed;
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
|
||||
:root,
|
||||
[data-theme="light"] {
|
||||
--gradient-start: #667eea;
|
||||
|
||||
@@ -164,7 +164,17 @@ export default function FamilyView() {
|
||||
setExpenseAmount('');
|
||||
setExpenseDescription('');
|
||||
setShowAddExpense(null);
|
||||
loadCategories();
|
||||
|
||||
const limitResponse = await expenseApi.getRemainingLimit(parseInt(familyId), categoryId);
|
||||
const limitValue = typeof limitResponse.data.remaining_limit === 'string'
|
||||
? parseFloat(limitResponse.data.remaining_limit)
|
||||
: limitResponse.data.remaining_limit;
|
||||
setRemainingLimits(prev => new Map(prev).set(categoryId, limitValue));
|
||||
|
||||
if (showHistory === categoryId) {
|
||||
const historyResponse = await expenseApi.getHistory(parseInt(familyId), categoryId, false);
|
||||
setHistoryData(historyResponse.data);
|
||||
}
|
||||
} catch (err) {
|
||||
alert(t('expense.addError'));
|
||||
console.error(err);
|
||||
@@ -452,6 +462,55 @@ export default function FamilyView() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showAddExpense === category.id && (
|
||||
<div className="glass-effect p-6 rounded-2xl border-2 border-gray-200 mt-4">
|
||||
<h3 className="font-semibold text-gray-800 mb-4 text-center">
|
||||
{t('expense.addTitle')}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('expense.amount')}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="0.00"
|
||||
value={expenseAmount}
|
||||
onChange={(e) => setExpenseAmount(e.target.value)}
|
||||
className="w-full px-4 py-3 border-2 border-gray-300 rounded-2xl focus:border-purple-500 focus:ring-2 focus:ring-purple-200 transition-all text-center font-semibold text-lg"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('expense.description')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={t('expense.descriptionPlaceholder')}
|
||||
value={expenseDescription}
|
||||
onChange={(e) => setExpenseDescription(e.target.value)}
|
||||
className="w-full px-4 py-3 border-2 border-gray-300 rounded-2xl focus:border-purple-500 focus:ring-2 focus:ring-purple-200 transition-all"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => handleAddExpense(category.id)}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-5 py-3 btn-success text-white rounded-2xl hover:shadow-xl transition-all font-semibold"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
{t('common.add')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowAddExpense(null)}
|
||||
className="px-5 py-3 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-2xl transition-all font-medium"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showHistory === category.id && historyData && (
|
||||
<div className="mt-4 glass-effect p-4 rounded-2xl border-2 border-blue-200">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
@@ -585,54 +644,6 @@ export default function FamilyView() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showAddExpense === category.id && (
|
||||
<div className="glass-effect p-6 rounded-2xl border-2 border-gray-200 mt-4">
|
||||
<h3 className="font-semibold text-gray-800 mb-4 text-center">
|
||||
{t('expense.addTitle')}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('expense.amount')}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="0.00"
|
||||
value={expenseAmount}
|
||||
onChange={(e) => setExpenseAmount(e.target.value)}
|
||||
className="w-full px-4 py-3 border-2 border-gray-300 rounded-2xl focus:border-purple-500 focus:ring-2 focus:ring-purple-200 transition-all text-center font-semibold text-lg"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('expense.description')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={t('expense.descriptionPlaceholder')}
|
||||
value={expenseDescription}
|
||||
onChange={(e) => setExpenseDescription(e.target.value)}
|
||||
className="w-full px-4 py-3 border-2 border-gray-300 rounded-2xl focus:border-purple-500 focus:ring-2 focus:ring-purple-200 transition-all"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => handleAddExpense(category.id)}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-5 py-3 btn-success text-white rounded-2xl hover:shadow-xl transition-all font-semibold"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
{t('common.add')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowAddExpense(null)}
|
||||
className="px-5 py-3 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-2xl transition-all font-medium"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -97,9 +97,20 @@ export default function Profile() {
|
||||
}
|
||||
};
|
||||
|
||||
const themeColors: Record<string, string> = {
|
||||
light: '#667eea',
|
||||
dark: '#000000',
|
||||
sunset: '#f97316',
|
||||
ocean: '#3b82f6',
|
||||
forest: '#22c55e',
|
||||
purple: '#8b5cf6',
|
||||
};
|
||||
|
||||
const handleThemeChange = (theme: Theme) => {
|
||||
setPreferences({ theme });
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
const metaTheme = document.querySelector('meta[name="theme-color"]');
|
||||
if (metaTheme) metaTheme.setAttribute('content', themeColors[theme] ?? '#667eea');
|
||||
};
|
||||
|
||||
const handleLocaleChange = (locale: 'ru' | 'en') => {
|
||||
|
||||
Reference in New Issue
Block a user