205 lines
4.8 KiB
Vue
205 lines
4.8 KiB
Vue
<template>
|
|
<div class="home-page">
|
|
<div v-if="isAuthenticated && user" class="dashboard-content">
|
|
<h1>Welcome, {{ user.nickname }}!</h1>
|
|
<p>This is your dashboard. Let's get those habits done!</p>
|
|
|
|
<div class="habits-section">
|
|
<h2>My Habits</h2>
|
|
<div v-if="habitsPending">Loading habits...</div>
|
|
<div v-else-if="habitsError">Could not load habits.</div>
|
|
<div v-else-if="habits && habits.length > 0">
|
|
<div v-for="habit in habits" :key="habit.id" class="habit-card">
|
|
<h3>{{ habit.name }}</h3>
|
|
<div class="history-grid">
|
|
<div v-for="day in last14Days" :key="day.toISOString()" class="day-cell" :class="{ 'completed': isCompleted(habit, day) }">
|
|
<span class="day-label">{{ day.getDate() }}</span>
|
|
</div>
|
|
</div>
|
|
<button @click="completeHabit(habit.id)" :disabled="isCompleted(habit, today)">
|
|
{{ isCompleted(habit, today) ? 'Completed Today' : 'Complete for Today' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div v-else>
|
|
<p>You have no habits yet. Go to the <NuxtLink to="/habits">My Habits</NuxtLink> page to create one.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="links">
|
|
<NuxtLink to="/habits" class="button">Manage Habits</NuxtLink>
|
|
<NuxtLink to="/village" class="button">My Village</NuxtLink>
|
|
<NuxtLink to="/leaderboard" class="button">Leaderboard</NuxtLink>
|
|
</div>
|
|
</div>
|
|
<div v-else class="welcome-content">
|
|
<h1>Добро пожаловать в SmurfHabits!</h1>
|
|
<p>Отслеживайте свои привычки и развивайте свою деревню.</p>
|
|
<div class="auth-buttons">
|
|
<NuxtLink to="/login" class="button primary">Войти</NuxtLink>
|
|
<NuxtLink to="/register" class="button secondary">Зарегистрироваться</NuxtLink>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue';
|
|
|
|
const { user, isAuthenticated } = useAuth();
|
|
const api = useApi();
|
|
|
|
// --- Habits Data ---
|
|
const { data: habits, pending: habitsPending, error: habitsError, refresh: refreshHabits } = await useFetch('/api/habits', {
|
|
lazy: true,
|
|
server: false,
|
|
});
|
|
|
|
// --- Date Logic ---
|
|
const today = new Date();
|
|
const last14Days = computed(() => {
|
|
const dates = [];
|
|
for (let i = 13; i >= 0; i--) {
|
|
const date = new Date();
|
|
date.setDate(date.getDate() - i);
|
|
dates.push(date);
|
|
}
|
|
return dates;
|
|
});
|
|
|
|
const isSameDay = (d1, d2) => {
|
|
d1 = new Date(d1);
|
|
d2 = new Date(d2);
|
|
return d1.getFullYear() === d2.getFullYear() &&
|
|
d1.getMonth() === d2.getMonth() &&
|
|
d1.getDate() === d2.getDate();
|
|
};
|
|
|
|
const isCompleted = (habit, date) => {
|
|
return habit.completions.some(c => isSameDay(c.date, date));
|
|
};
|
|
|
|
// --- Actions ---
|
|
const completeHabit = async (habitId) => {
|
|
try {
|
|
await api(`/api/habits/${habitId}/complete`, { method: 'POST' });
|
|
await refreshHabits(); // Refresh the habits data to show the new completion
|
|
} catch (err) {
|
|
alert(err.data?.message || 'Failed to complete habit.');
|
|
}
|
|
};
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
.home-page {
|
|
padding: 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
.habits-section {
|
|
margin-top: 40px;
|
|
margin-bottom: 40px;
|
|
}
|
|
|
|
.habit-card {
|
|
background: #fff;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin: 20px auto;
|
|
max-width: 800px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.history-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(14, 1fr);
|
|
gap: 5px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.day-cell {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 4px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
background-color: #f8fafc;
|
|
}
|
|
|
|
.day-cell.completed {
|
|
background-color: #4ade80;
|
|
color: white;
|
|
}
|
|
|
|
.day-label {
|
|
font-size: 0.8em;
|
|
}
|
|
|
|
.welcome-content {
|
|
margin-top: 50px;
|
|
}
|
|
|
|
.welcome-content h1 {
|
|
font-size: 2.5em;
|
|
margin-bottom: 20px;
|
|
color: #333;
|
|
}
|
|
|
|
.welcome-content p {
|
|
font-size: 1.2em;
|
|
color: #555;
|
|
margin-bottom: 40px;
|
|
}
|
|
|
|
.auth-buttons {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 20px;
|
|
}
|
|
|
|
.button {
|
|
display: inline-block;
|
|
padding: 12px 25px;
|
|
border-radius: 8px;
|
|
text-decoration: none;
|
|
font-weight: bold;
|
|
font-size: 1.1em;
|
|
transition: background-color 0.3s ease;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.button.primary {
|
|
background-color: #007bff;
|
|
color: white;
|
|
}
|
|
|
|
.button.primary:hover {
|
|
background-color: #0056b3;
|
|
}
|
|
|
|
.button.secondary {
|
|
background-color: #6c757d;
|
|
color: white;
|
|
}
|
|
|
|
.button.secondary:hover {
|
|
background-color: #5a6268;
|
|
}
|
|
|
|
.links {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 20px;
|
|
margin: 40px 0;
|
|
}
|
|
.links a.button {
|
|
background-color: #e9ecef;
|
|
color: #333;
|
|
}
|
|
.links a.button:hover {
|
|
background-color: #dee2e6;
|
|
}
|
|
</style> |