feat: initial smart home dashboard
All checks were successful
Deploy to Coolify / deploy (push) Successful in 44s

- Next.js 14 + TypeScript + Tailwind CSS
- Glassmorphism design with ambient orbs
- Cards: Light x2, Temperature, AirPurifier, Tasks, Weather, Savings
- Home Assistant integration (demo mode if no token)
- Vikunja tasks API
- Pulse savings API
- wttr.in weather
- Framer Motion animations
- Dark/light theme toggle
- Bottom navigation
- Dockerfile for deployment
This commit is contained in:
Cosmo
2026-04-22 10:00:41 +00:00
commit 9044869fa4
29 changed files with 2439 additions and 0 deletions

54
app/api/savings/route.ts Normal file
View File

@@ -0,0 +1,54 @@
import { NextResponse } from "next/server";
const PULSE_API = process.env.PULSE_API_URL || "https://api.digital-home.site";
const PULSE_REFRESH = process.env.PULSE_REFRESH_TOKEN || "";
const MOCK_SAVINGS = [
{
id: 1,
name: "Квартира Pulse Premier",
current_amount: 450000,
target_amount: 800000,
color: "#6366f1",
icon: "🏠",
},
{
id: 2,
name: "Отпуск",
current_amount: 95000,
target_amount: 200000,
color: "#8b5cf6",
icon: "✈️",
},
];
async function getAccessToken(): Promise<string> {
const res = await fetch(`${PULSE_API}/auth/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: PULSE_REFRESH }),
});
if (!res.ok) throw new Error("Refresh failed");
const data = await res.json();
return data.access_token;
}
export async function GET() {
if (!PULSE_REFRESH) {
return NextResponse.json({ savings: MOCK_SAVINGS, demo: true });
}
try {
const token = await getAccessToken();
const res = await fetch(`${PULSE_API}/savings`, {
headers: { Authorization: `Bearer ${token}` },
next: { revalidate: 300 },
});
if (!res.ok) throw new Error(`Pulse responded ${res.status}`);
const data = await res.json();
return NextResponse.json({ savings: Array.isArray(data) ? data : [] });
} catch (e) {
return NextResponse.json({ savings: MOCK_SAVINGS, demo: true });
}
}