diff --git a/Dockerfile.dev b/Dockerfile.dev
new file mode 100644
index 0000000..d5f1ebf
--- /dev/null
+++ b/Dockerfile.dev
@@ -0,0 +1,13 @@
+FROM node:20-alpine AS builder
+WORKDIR /app
+COPY package*.json ./
+RUN npm install
+COPY . .
+ENV VITE_API_URL=http://192.168.31.60:8081
+RUN npm run build
+
+FROM nginx:alpine
+COPY --from=builder /app/dist /usr/share/nginx/html
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 2a49885..d2a83ed 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -5,7 +5,9 @@ networks:
services:
web-dev:
- build: .
+ build:
+ context: .
+ dockerfile: Dockerfile.dev
container_name: pulse-web-dev
restart: always
ports:
diff --git a/src/components/finance/FinanceAnalytics.jsx b/src/components/finance/FinanceAnalytics.jsx
index 8f4c14c..1dc597d 100644
--- a/src/components/finance/FinanceAnalytics.jsx
+++ b/src/components/finance/FinanceAnalytics.jsx
@@ -18,16 +18,16 @@ const MONTH_NAMES = [
"Июл", "Авг", "Сен", "Окт", "Ноя", "Дек",
]
-export default function FinanceAnalytics() {
+export default function FinanceAnalytics({ month, year }) {
const [analytics, setAnalytics] = useState(null)
const [summary, setSummary] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
- const now = new Date()
+ setLoading(true)
Promise.all([
financeApi.getAnalytics({ months: 6 }),
- financeApi.getSummary({ month: now.getMonth() + 1, year: now.getFullYear() }),
+ financeApi.getSummary({ month, year }),
])
.then(([a, s]) => {
setAnalytics(a)
@@ -35,7 +35,7 @@ export default function FinanceAnalytics() {
})
.catch(console.error)
.finally(() => setLoading(false))
- }, [])
+ }, [month, year])
if (loading) {
return (
@@ -72,7 +72,6 @@ export default function FinanceAnalytics() {
return (
- {/* Summary cards */}
@@ -97,7 +96,6 @@ export default function FinanceAnalytics() {
- {/* Bar chart */}
{barData.length > 0 && (
@@ -131,7 +129,6 @@ export default function FinanceAnalytics() {
)}
- {/* Donut chart */}
{pieData.length > 0 && (
@@ -179,7 +176,6 @@ export default function FinanceAnalytics() {
)}
- {/* Monthly trend */}
{monthlyData.length > 0 && (
diff --git a/src/components/finance/FinanceDashboard.jsx b/src/components/finance/FinanceDashboard.jsx
index 8b711be..cae0c32 100644
--- a/src/components/finance/FinanceDashboard.jsx
+++ b/src/components/finance/FinanceDashboard.jsx
@@ -12,18 +12,18 @@ const COLORS = [
const fmt = (n) => Number(n).toLocaleString("ru-RU") + " ₽"
-export default function FinanceDashboard() {
+export default function FinanceDashboard({ month, year }) {
const [summary, setSummary] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
- const now = new Date()
+ setLoading(true)
financeApi
- .getSummary({ month: now.getMonth() + 1, year: now.getFullYear() })
+ .getSummary({ month, year })
.then(setSummary)
.catch(console.error)
.finally(() => setLoading(false))
- }, [])
+ }, [month, year])
if (loading) {
return (
@@ -63,7 +63,6 @@ export default function FinanceDashboard() {
return (
- {/* Balance Card */}
Баланс за месяц
{fmt(summary.balance)}
@@ -83,7 +82,6 @@ export default function FinanceDashboard() {
- {/* Top Categories */}
{expenseCategories.length > 0 && (
@@ -120,7 +118,6 @@ export default function FinanceDashboard() {
)}
- {/* Donut Chart */}
{pieData.length > 0 && (
@@ -168,7 +165,6 @@ export default function FinanceDashboard() {
)}
- {/* Daily Line Chart */}
{dailyData.length > 0 && (
diff --git a/src/components/finance/TransactionList.jsx b/src/components/finance/TransactionList.jsx
index b2f90be..a276ce7 100644
--- a/src/components/finance/TransactionList.jsx
+++ b/src/components/finance/TransactionList.jsx
@@ -8,7 +8,7 @@ const formatDate = (d) => {
return dt.toLocaleDateString("ru-RU", { day: "numeric", month: "long" })
}
-export default function TransactionList({ onAdd }) {
+export default function TransactionList({ onAdd, month, year }) {
const [transactions, setTransactions] = useState([])
const [categories, setCategories] = useState([])
const [loading, setLoading] = useState(true)
@@ -17,11 +17,12 @@ export default function TransactionList({ onAdd }) {
const [search, setSearch] = useState("")
useEffect(() => {
+ setLoading(true)
Promise.all([
financeApi.listCategories(),
financeApi.listTransactions({
- month: new Date().getMonth() + 1,
- year: new Date().getFullYear(),
+ month,
+ year,
limit: 100,
}),
])
@@ -31,7 +32,7 @@ export default function TransactionList({ onAdd }) {
})
.catch(console.error)
.finally(() => setLoading(false))
- }, [])
+ }, [month, year])
const filtered = transactions.filter((t) => {
if (filter !== "all" && t.type !== filter) return false
@@ -67,7 +68,6 @@ export default function TransactionList({ onAdd }) {
return (
- {/* Search */}
setSearch(e.target.value)}
/>
- {/* Type filter */}
{[
["all", "Все"],
@@ -96,7 +95,6 @@ export default function TransactionList({ onAdd }) {
))}
- {/* Category filter */}
- {/* Transaction groups */}
{Object.keys(grouped).length === 0 ? (
🔍
diff --git a/src/pages/Finance.jsx b/src/pages/Finance.jsx
index e27dd04..6c6aa77 100644
--- a/src/pages/Finance.jsx
+++ b/src/pages/Finance.jsx
@@ -13,13 +13,32 @@ const tabs = [
{ key: "categories", label: "Категории", icon: "🏷️" },
]
+const MONTH_NAMES = [
+ "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
+ "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь",
+]
+
export default function Finance() {
+ const now = new Date()
const [activeTab, setActiveTab] = useState("dashboard")
const [showAdd, setShowAdd] = useState(false)
const [refreshKey, setRefreshKey] = useState(0)
+ const [month, setMonth] = useState(now.getMonth() + 1)
+ const [year, setYear] = useState(now.getFullYear())
const refresh = () => setRefreshKey((k) => k + 1)
+ const prevMonth = () => {
+ if (month === 1) { setMonth(12); setYear(y => y - 1) }
+ else setMonth(m => m - 1)
+ }
+ const nextMonth = () => {
+ if (month === 12) { setMonth(1); setYear(y => y + 1) }
+ else setMonth(m => m + 1)
+ }
+
+ const isCurrentMonth = month === now.getMonth() + 1 && year === now.getFullYear()
+
return (
@@ -36,6 +55,31 @@ export default function Finance() {
+
+
+ {/* Month Switcher */}
+
+
+
+
+
+
+
+
{tabs.map((t) => (