diff --git a/src/App.jsx b/src/App.jsx index a35c5c9..ac3b3c6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,6 +10,7 @@ import VerifyEmail from "./pages/VerifyEmail" import ResetPassword from "./pages/ResetPassword" import ForgotPassword from "./pages/ForgotPassword" import Stats from "./pages/Stats" +import Settings from "./pages/Settings" function ProtectedRoute({ children }) { const { isAuthenticated, isLoading } = useAuthStore() @@ -114,6 +115,14 @@ export default function App() { } /> + + + + } + /> } /> ) diff --git a/src/api/profile.js b/src/api/profile.js new file mode 100644 index 0000000..c62583c --- /dev/null +++ b/src/api/profile.js @@ -0,0 +1,14 @@ +import api from "./client" + +export const profileApi = { + get: async () => { + const { data } = await api.get("/profile") + return data + }, + update: async (profileData) => { + const { data } = await api.put("/profile", profileData) + return data + }, +} + +export default profileApi diff --git a/src/components/CreateHabitModal.jsx b/src/components/CreateHabitModal.jsx index 423275a..3096d63 100644 --- a/src/components/CreateHabitModal.jsx +++ b/src/components/CreateHabitModal.jsx @@ -1,45 +1,46 @@ -import { useState } from 'react' -import { motion, AnimatePresence } from 'framer-motion' -import { X, ChevronDown, ChevronUp } from 'lucide-react' -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { habitsApi } from '../api/habits' -import clsx from 'clsx' +import { useState } from "react" +import { motion, AnimatePresence } from "framer-motion" +import { X, ChevronDown, ChevronUp, Clock } from "lucide-react" +import { useMutation, useQueryClient } from "@tanstack/react-query" +import { habitsApi } from "../api/habits" +import clsx from "clsx" const COLORS = [ - '#6366f1', '#8b5cf6', '#d946ef', '#ec4899', '#f43f5e', - '#f97316', '#eab308', '#22c55e', '#14b8a6', '#0ea5e9', + "#6366f1", "#8b5cf6", "#d946ef", "#ec4899", "#f43f5e", + "#f97316", "#eab308", "#22c55e", "#14b8a6", "#0ea5e9", ] const ICON_CATEGORIES = [ - { name: 'Спорт', icons: ['💪', '🏃', '🚴', '🏊', '🧘', '⚽', '🏀', '🎾'] }, - { name: 'Здоровье', icons: ['💊', '💉', '🩺', '🧠', '😴', '💤', '🦷', '👁️'] }, - { name: 'Продуктивность', icons: ['📚', '📖', '✏️', '💻', '🎯', '📝', '📅', '⏰'] }, - { name: 'Дом', icons: ['🏠', '🧹', '🧺', '🍳', '🛒', '🔧', '🪴'] }, - { name: 'Финансы', icons: ['💰', '💳', '📊', '💵', '🏦'] }, - { name: 'Социальное', icons: ['👥', '💬', '📞', '👨‍👩‍👧‍👦', '❤️'] }, - { name: 'Хобби', icons: ['🎨', '🎵', '🎸', '🎮', '📷', '✈️', '🚗'] }, - { name: 'Еда/вода', icons: ['🥗', '🍎', '🥤', '☕', '🍽️', '💧'] }, - { name: 'Разное', icons: ['⭐', '🎉', '✨', '🔥', '🌟', '💎', '🎁'] }, + { name: "Спорт", icons: ["💪", "🏃", "🚴", "🏊", "🧘", "⚽", "🏀", "🎾"] }, + { name: "Здоровье", icons: ["💊", "💉", "🩺", "🧠", "😴", "💤", "🦷", "👁️"] }, + { name: "Продуктивность", icons: ["📚", "📖", "✏️", "💻", "🎯", "📝", "📅", "⏰"] }, + { name: "Дом", icons: ["🏠", "🧹", "🧺", "🍳", "🛒", "🔧", "🪴"] }, + { name: "Финансы", icons: ["💰", "💳", "📊", "💵", "🏦"] }, + { name: "Социальное", icons: ["👥", "💬", "📞", "👨👩👧👦", "❤️"] }, + { name: "Хобби", icons: ["🎨", "🎵", "🎸", "🎮", "📷", "✈️", "🚗"] }, + { name: "Еда/вода", icons: ["🥗", "🍎", "🥤", "☕", "🍽️", "💧"] }, + { name: "Разное", icons: ["⭐", "🎉", "✨", "🔥", "🌟", "💎", "🎁"] }, ] const DAYS = [ - { id: 1, short: 'Пн', full: 'Понедельник' }, - { id: 2, short: 'Вт', full: 'Вторник' }, - { id: 3, short: 'Ср', full: 'Среда' }, - { id: 4, short: 'Чт', full: 'Четверг' }, - { id: 5, short: 'Пт', full: 'Пятница' }, - { id: 6, short: 'Сб', full: 'Суббота' }, - { id: 7, short: 'Вс', full: 'Воскресенье' }, + { id: 1, short: "Пн", full: "Понедельник" }, + { id: 2, short: "Вт", full: "Вторник" }, + { id: 3, short: "Ср", full: "Среда" }, + { id: 4, short: "Чт", full: "Четверг" }, + { id: 5, short: "Пт", full: "Пятница" }, + { id: 6, short: "Сб", full: "Суббота" }, + { id: 7, short: "Вс", full: "Воскресенье" }, ] export default function CreateHabitModal({ open, onClose }) { - const [name, setName] = useState('') - const [description, setDescription] = useState('') + const [name, setName] = useState("") + const [description, setDescription] = useState("") const [color, setColor] = useState(COLORS[0]) - const [icon, setIcon] = useState('✨') - const [frequency, setFrequency] = useState('daily') + const [icon, setIcon] = useState("✨") + const [frequency, setFrequency] = useState("daily") const [targetDays, setTargetDays] = useState([1, 2, 3, 4, 5, 6, 7]) - const [error, setError] = useState('') + const [reminderTime, setReminderTime] = useState("") + const [error, setError] = useState("") const [showAllIcons, setShowAllIcons] = useState(false) const queryClient = useQueryClient() @@ -47,23 +48,24 @@ export default function CreateHabitModal({ open, onClose }) { const mutation = useMutation({ mutationFn: (data) => habitsApi.create(data), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['habits'] }) - queryClient.invalidateQueries({ queryKey: ['stats'] }) + queryClient.invalidateQueries({ queryKey: ["habits"] }) + queryClient.invalidateQueries({ queryKey: ["stats"] }) handleClose() }, onError: (err) => { - setError(err.response?.data?.error || 'Ошибка создания') + setError(err.response?.data?.error || "Ошибка создания") }, }) const handleClose = () => { - setName('') - setDescription('') + setName("") + setDescription("") setColor(COLORS[0]) - setIcon('✨') - setFrequency('daily') + setIcon("✨") + setFrequency("daily") setTargetDays([1, 2, 3, 4, 5, 6, 7]) - setError('') + setReminderTime("") + setError("") setShowAllIcons(false) onClose() } @@ -71,18 +73,21 @@ export default function CreateHabitModal({ open, onClose }) { const handleSubmit = (e) => { e.preventDefault() if (!name.trim()) { - setError('Введи название привычки') + setError("Введи название привычки") return } - if (frequency === 'weekly' && targetDays.length === 0) { - setError('Выбери хотя бы один день недели') + if (frequency === "weekly" && targetDays.length === 0) { + setError("Выбери хотя бы один день недели") return } const data = { name, description, color, icon, frequency } - if (frequency === 'weekly') { + if (frequency === "weekly") { data.target_days = targetDays } + if (reminderTime) { + data.reminder_time = reminderTime + } mutation.mutate(data) } @@ -95,8 +100,7 @@ export default function CreateHabitModal({ open, onClose }) { ) } - // Популярные иконки для быстрого выбора - const popularIcons = ['✨', '💪', '📚', '🏃', '💧', '🧘', '💤', '🎯', '✏️', '🍎'] + const popularIcons = ["✨", "💪", "📚", "🏃", "💧", "🧘", "💤", "🎯", "✏️", "🍎"] return ( @@ -167,24 +171,24 @@ export default function CreateHabitModal({ open, onClose }) {
- {frequency === 'weekly' && ( + {frequency === "weekly" && ( )} +
+ +
+ + setReminderTime(e.target.value)} + className="input pl-10" + /> +
+

+ Получишь напоминание в Telegram в указанное время +

+
+
diff --git a/src/components/CreateTaskModal.jsx b/src/components/CreateTaskModal.jsx index 5d4a622..4040bc4 100644 --- a/src/components/CreateTaskModal.jsx +++ b/src/components/CreateTaskModal.jsx @@ -1,43 +1,44 @@ -import { useState } from 'react' -import { motion, AnimatePresence } from 'framer-motion' -import { X, ChevronDown, ChevronUp, Calendar } from 'lucide-react' -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { tasksApi } from '../api/tasks' -import clsx from 'clsx' -import { format, addDays } from 'date-fns' +import { useState } from "react" +import { motion, AnimatePresence } from "framer-motion" +import { X, ChevronDown, ChevronUp, Calendar, Clock } from "lucide-react" +import { useMutation, useQueryClient } from "@tanstack/react-query" +import { tasksApi } from "../api/tasks" +import clsx from "clsx" +import { format, addDays } from "date-fns" const COLORS = [ - '#6B7280', '#6366f1', '#8b5cf6', '#d946ef', '#ec4899', - '#f43f5e', '#f97316', '#eab308', '#22c55e', '#0ea5e9', + "#6B7280", "#6366f1", "#8b5cf6", "#d946ef", "#ec4899", + "#f43f5e", "#f97316", "#eab308", "#22c55e", "#0ea5e9", ] const ICON_CATEGORIES = [ - { name: 'Продуктивность', icons: ['📋', '📝', '✅', '📌', '🎯', '💡', '📅', '⏰'] }, - { name: 'Работа', icons: ['💼', '💻', '📧', '📞', '📊', '📈', '🖥️', '⌨️'] }, - { name: 'Дом', icons: ['🏠', '🧹', '🧺', '🍳', '🛒', '🔧', '🪴', '🛋️'] }, - { name: 'Финансы', icons: ['💰', '💳', '📊', '💵', '🏦', '🧾'] }, - { name: 'Здоровье', icons: ['💊', '🏃', '🧘', '💪', '🩺', '🦷'] }, - { name: 'Разное', icons: ['⭐', '🎁', '📦', '✈️', '🚗', '📷', '🎉'] }, + { name: "Продуктивность", icons: ["📋", "📝", "✅", "📌", "🎯", "💡", "📅", "⏰"] }, + { name: "Работа", icons: ["💼", "💻", "📧", "📞", "📊", "📈", "🖥️", "⌨️"] }, + { name: "Дом", icons: ["🏠", "🧹", "🧺", "🍳", "🛒", "🔧", "🪴", "🛋️"] }, + { name: "Финансы", icons: ["💰", "💳", "📊", "💵", "🏦", "🧾"] }, + { name: "Здоровье", icons: ["💊", "🏃", "🧘", "💪", "🩺", "🦷"] }, + { name: "Разное", icons: ["⭐", "🎁", "📦", "✈️", "🚗", "📷", "🎉"] }, ] const PRIORITIES = [ - { value: 0, label: 'Без приоритета', color: 'bg-gray-100 text-gray-600' }, - { value: 1, label: 'Низкий', color: 'bg-blue-100 text-blue-700' }, - { value: 2, label: 'Средний', color: 'bg-yellow-100 text-yellow-700' }, - { value: 3, label: 'Высокий', color: 'bg-red-100 text-red-700' }, + { value: 0, label: "Без приоритета", color: "bg-gray-100 text-gray-600" }, + { value: 1, label: "Низкий", color: "bg-blue-100 text-blue-700" }, + { value: 2, label: "Средний", color: "bg-yellow-100 text-yellow-700" }, + { value: 3, label: "Высокий", color: "bg-red-100 text-red-700" }, ] export default function CreateTaskModal({ open, onClose, defaultDueDate = null }) { - const today = format(new Date(), 'yyyy-MM-dd') - const tomorrow = format(addDays(new Date(), 1), 'yyyy-MM-dd') + const today = format(new Date(), "yyyy-MM-dd") + const tomorrow = format(addDays(new Date(), 1), "yyyy-MM-dd") - const [title, setTitle] = useState('') - const [description, setDescription] = useState('') + const [title, setTitle] = useState("") + const [description, setDescription] = useState("") const [color, setColor] = useState(COLORS[0]) - const [icon, setIcon] = useState('📋') + const [icon, setIcon] = useState("📋") const [dueDate, setDueDate] = useState(defaultDueDate || today) const [priority, setPriority] = useState(0) - const [error, setError] = useState('') + const [reminderTime, setReminderTime] = useState("") + const [error, setError] = useState("") const [showAllIcons, setShowAllIcons] = useState(false) const queryClient = useQueryClient() @@ -45,23 +46,24 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } const mutation = useMutation({ mutationFn: (data) => tasksApi.create(data), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['tasks'] }) - queryClient.invalidateQueries({ queryKey: ['tasks-today'] }) + queryClient.invalidateQueries({ queryKey: ["tasks"] }) + queryClient.invalidateQueries({ queryKey: ["tasks-today"] }) handleClose() }, onError: (err) => { - setError(err.response?.data?.error || 'Ошибка создания') + setError(err.response?.data?.error || "Ошибка создания") }, }) const handleClose = () => { - setTitle('') - setDescription('') + setTitle("") + setDescription("") setColor(COLORS[0]) - setIcon('📋') + setIcon("📋") setDueDate(defaultDueDate || today) setPriority(0) - setError('') + setReminderTime("") + setError("") setShowAllIcons(false) onClose() } @@ -69,7 +71,7 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } const handleSubmit = (e) => { e.preventDefault() if (!title.trim()) { - setError('Введи название задачи') + setError("Введи название задачи") return } @@ -80,10 +82,11 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } icon, due_date: dueDate || null, priority, + reminder_time: reminderTime || null, }) } - const popularIcons = ['📋', '📝', '✅', '🎯', '💼', '🏠', '💰', '📞'] + const popularIcons = ["📋", "📝", "✅", "🎯", "💼", "🏠", "💰", "📞"] return ( @@ -155,10 +158,10 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } type="button" onClick={() => setDueDate(today)} className={clsx( - 'px-3 py-1.5 rounded-lg text-sm font-medium transition-all', + "px-3 py-1.5 rounded-lg text-sm font-medium transition-all", dueDate === today - ? 'bg-primary-500 text-white' - : 'bg-gray-100 text-gray-600 hover:bg-gray-200' + ? "bg-primary-500 text-white" + : "bg-gray-100 text-gray-600 hover:bg-gray-200" )} > Сегодня @@ -167,22 +170,22 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } type="button" onClick={() => setDueDate(tomorrow)} className={clsx( - 'px-3 py-1.5 rounded-lg text-sm font-medium transition-all', + "px-3 py-1.5 rounded-lg text-sm font-medium transition-all", dueDate === tomorrow - ? 'bg-primary-500 text-white' - : 'bg-gray-100 text-gray-600 hover:bg-gray-200' + ? "bg-primary-500 text-white" + : "bg-gray-100 text-gray-600 hover:bg-gray-200" )} > Завтра {showAllIcons && ( @@ -271,10 +292,10 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } type="button" onClick={() => setIcon(ic)} className={clsx( - 'w-9 h-9 rounded-lg flex items-center justify-center text-lg transition-all', + "w-9 h-9 rounded-lg flex items-center justify-center text-lg transition-all", icon === ic - ? 'bg-primary-100 ring-2 ring-primary-500' - : 'bg-gray-100 hover:bg-gray-200' + ? "bg-primary-100 ring-2 ring-primary-500" + : "bg-gray-100 hover:bg-gray-200" )} > {ic} @@ -299,8 +320,8 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } type="button" onClick={() => setColor(c)} className={clsx( - 'w-8 h-8 rounded-full transition-all', - color === c ? 'ring-2 ring-offset-2 ring-gray-400 scale-110' : '' + "w-8 h-8 rounded-full transition-all", + color === c ? "ring-2 ring-offset-2 ring-gray-400 scale-110" : "" )} style={{ backgroundColor: c }} /> @@ -314,7 +335,7 @@ export default function CreateTaskModal({ open, onClose, defaultDueDate = null } disabled={mutation.isPending} className="btn btn-primary w-full" > - {mutation.isPending ? 'Создаём...' : 'Создать задачу'} + {mutation.isPending ? "Создаём..." : "Создать задачу"} diff --git a/src/components/EditHabitModal.jsx b/src/components/EditHabitModal.jsx index c08c924..8970580 100644 --- a/src/components/EditHabitModal.jsx +++ b/src/components/EditHabitModal.jsx @@ -1,45 +1,46 @@ -import { useState, useEffect } from 'react' -import { motion, AnimatePresence } from 'framer-motion' -import { X, Trash2, ChevronDown, ChevronUp } from 'lucide-react' -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { habitsApi } from '../api/habits' -import clsx from 'clsx' +import { useState, useEffect } from "react" +import { motion, AnimatePresence } from "framer-motion" +import { X, Trash2, ChevronDown, ChevronUp, Clock } from "lucide-react" +import { useMutation, useQueryClient } from "@tanstack/react-query" +import { habitsApi } from "../api/habits" +import clsx from "clsx" const COLORS = [ - '#6366f1', '#8b5cf6', '#d946ef', '#ec4899', '#f43f5e', - '#f97316', '#eab308', '#22c55e', '#14b8a6', '#0ea5e9', + "#6366f1", "#8b5cf6", "#d946ef", "#ec4899", "#f43f5e", + "#f97316", "#eab308", "#22c55e", "#14b8a6", "#0ea5e9", ] const ICON_CATEGORIES = [ - { name: 'Спорт', icons: ['💪', '🏃', '🚴', '🏊', '🧘', '⚽', '🏀', '🎾'] }, - { name: 'Здоровье', icons: ['💊', '💉', '🩺', '🧠', '😴', '💤', '🦷', '👁️'] }, - { name: 'Продуктивность', icons: ['📚', '📖', '✏️', '💻', '🎯', '📝', '📅', '⏰'] }, - { name: 'Дом', icons: ['🏠', '🧹', '🧺', '🍳', '🛒', '🔧', '🪴'] }, - { name: 'Финансы', icons: ['💰', '💳', '📊', '💵', '🏦'] }, - { name: 'Социальное', icons: ['👥', '💬', '📞', '👨‍👩‍👧‍👦', '❤️'] }, - { name: 'Хобби', icons: ['🎨', '🎵', '🎸', '🎮', '📷', '✈️', '🚗'] }, - { name: 'Еда/вода', icons: ['🥗', '🍎', '🥤', '☕', '🍽️', '💧'] }, - { name: 'Разное', icons: ['⭐', '🎉', '✨', '🔥', '🌟', '💎', '🎁'] }, + { name: "Спорт", icons: ["💪", "🏃", "🚴", "🏊", "🧘", "⚽", "🏀", "🎾"] }, + { name: "Здоровье", icons: ["💊", "💉", "🩺", "🧠", "😴", "💤", "🦷", "👁️"] }, + { name: "Продуктивность", icons: ["📚", "📖", "✏️", "💻", "🎯", "📝", "📅", "⏰"] }, + { name: "Дом", icons: ["🏠", "🧹", "🧺", "🍳", "🛒", "🔧", "🪴"] }, + { name: "Финансы", icons: ["💰", "💳", "📊", "💵", "🏦"] }, + { name: "Социальное", icons: ["👥", "💬", "📞", "👨👩👧👦", "❤️"] }, + { name: "Хобби", icons: ["🎨", "🎵", "🎸", "🎮", "📷", "✈️", "🚗"] }, + { name: "Еда/вода", icons: ["🥗", "🍎", "🥤", "☕", "🍽️", "💧"] }, + { name: "Разное", icons: ["⭐", "🎉", "✨", "🔥", "🌟", "💎", "🎁"] }, ] const DAYS = [ - { id: 1, short: 'Пн' }, - { id: 2, short: 'Вт' }, - { id: 3, short: 'Ср' }, - { id: 4, short: 'Чт' }, - { id: 5, short: 'Пт' }, - { id: 6, short: 'Сб' }, - { id: 7, short: 'Вс' }, + { id: 1, short: "Пн" }, + { id: 2, short: "Вт" }, + { id: 3, short: "Ср" }, + { id: 4, short: "Чт" }, + { id: 5, short: "Пт" }, + { id: 6, short: "Сб" }, + { id: 7, short: "Вс" }, ] export default function EditHabitModal({ open, onClose, habit }) { - const [name, setName] = useState('') - const [description, setDescription] = useState('') + const [name, setName] = useState("") + const [description, setDescription] = useState("") const [color, setColor] = useState(COLORS[0]) - const [icon, setIcon] = useState('✨') - const [frequency, setFrequency] = useState('daily') + const [icon, setIcon] = useState("✨") + const [frequency, setFrequency] = useState("daily") const [targetDays, setTargetDays] = useState([1, 2, 3, 4, 5, 6, 7]) - const [error, setError] = useState('') + const [reminderTime, setReminderTime] = useState("") + const [error, setError] = useState("") const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) const [showAllIcons, setShowAllIcons] = useState(false) @@ -47,13 +48,14 @@ export default function EditHabitModal({ open, onClose, habit }) { useEffect(() => { if (habit && open) { - setName(habit.name || '') - setDescription(habit.description || '') + setName(habit.name || "") + setDescription(habit.description || "") setColor(habit.color || COLORS[0]) - setIcon(habit.icon || '✨') - setFrequency(habit.frequency || 'daily') + setIcon(habit.icon || "✨") + setFrequency(habit.frequency || "daily") setTargetDays(habit.target_days || [1, 2, 3, 4, 5, 6, 7]) - setError('') + setReminderTime(habit.reminder_time || "") + setError("") setShowDeleteConfirm(false) setShowAllIcons(false) } @@ -62,29 +64,29 @@ export default function EditHabitModal({ open, onClose, habit }) { const updateMutation = useMutation({ mutationFn: (data) => habitsApi.update(habit.id, data), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['habits'] }) - queryClient.invalidateQueries({ queryKey: ['stats'] }) + queryClient.invalidateQueries({ queryKey: ["habits"] }) + queryClient.invalidateQueries({ queryKey: ["stats"] }) onClose() }, onError: (err) => { - setError(err.response?.data?.error || 'Ошибка сохранения') + setError(err.response?.data?.error || "Ошибка сохранения") }, }) const deleteMutation = useMutation({ mutationFn: () => habitsApi.delete(habit.id), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['habits'] }) - queryClient.invalidateQueries({ queryKey: ['stats'] }) + queryClient.invalidateQueries({ queryKey: ["habits"] }) + queryClient.invalidateQueries({ queryKey: ["stats"] }) onClose() }, onError: (err) => { - setError(err.response?.data?.error || 'Ошибка удаления') + setError(err.response?.data?.error || "Ошибка удаления") }, }) const handleClose = () => { - setError('') + setError("") setShowDeleteConfirm(false) setShowAllIcons(false) onClose() @@ -93,18 +95,19 @@ export default function EditHabitModal({ open, onClose, habit }) { const handleSubmit = (e) => { e.preventDefault() if (!name.trim()) { - setError('Введи название привычки') + setError("Введи название привычки") return } - if (frequency === 'weekly' && targetDays.length === 0) { - setError('Выбери хотя бы один день недели') + if (frequency === "weekly" && targetDays.length === 0) { + setError("Выбери хотя бы один день недели") return } const data = { name, description, color, icon, frequency } - if (frequency === 'weekly') { + if (frequency === "weekly") { data.target_days = targetDays } + data.reminder_time = reminderTime || null updateMutation.mutate(data) } @@ -121,7 +124,7 @@ export default function EditHabitModal({ open, onClose, habit }) { ) } - const popularIcons = ['✨', '💪', '📚', '🏃', '💧', '🧘', '💤', '🎯', '✏️', '🍎'] + const popularIcons = ["✨", "💪", "📚", "🏃", "💧", "🧘", "💤", "🎯", "✏️", "🍎"] if (!habit) return null @@ -174,7 +177,7 @@ export default function EditHabitModal({ open, onClose, habit }) { disabled={deleteMutation.isPending} className="flex-1 btn bg-red-500 text-white hover:bg-red-600" > - {deleteMutation.isPending ? 'Удаляем...' : 'Удалить'} + {deleteMutation.isPending ? "Удаляем..." : "Удалить"} @@ -219,24 +222,24 @@ export default function EditHabitModal({ open, onClose, habit }) {
- {frequency === 'weekly' && ( + {frequency === "weekly" && ( )} +
+ +
+ + setReminderTime(e.target.value)} + className="input pl-10" + /> +
+

+ Получишь напоминание в Telegram в указанное время +

+
+
@@ -208,10 +211,10 @@ export default function EditTaskModal({ open, onClose, task }) { type="button" onClick={() => setDueDate(today)} className={clsx( - 'px-3 py-1.5 rounded-lg text-sm font-medium transition-all', + "px-3 py-1.5 rounded-lg text-sm font-medium transition-all", dueDate === today - ? 'bg-primary-500 text-white' - : 'bg-gray-100 text-gray-600 hover:bg-gray-200' + ? "bg-primary-500 text-white" + : "bg-gray-100 text-gray-600 hover:bg-gray-200" )} > Сегодня @@ -220,22 +223,22 @@ export default function EditTaskModal({ open, onClose, task }) { type="button" onClick={() => setDueDate(tomorrow)} className={clsx( - 'px-3 py-1.5 rounded-lg text-sm font-medium transition-all', + "px-3 py-1.5 rounded-lg text-sm font-medium transition-all", dueDate === tomorrow - ? 'bg-primary-500 text-white' - : 'bg-gray-100 text-gray-600 hover:bg-gray-200' + ? "bg-primary-500 text-white" + : "bg-gray-100 text-gray-600 hover:bg-gray-200" )} > Завтра {showAllIcons && ( @@ -324,10 +345,10 @@ export default function EditTaskModal({ open, onClose, task }) { type="button" onClick={() => setIcon(ic)} className={clsx( - 'w-9 h-9 rounded-lg flex items-center justify-center text-lg transition-all', + "w-9 h-9 rounded-lg flex items-center justify-center text-lg transition-all", icon === ic - ? 'bg-primary-100 ring-2 ring-primary-500' - : 'bg-gray-100 hover:bg-gray-200' + ? "bg-primary-100 ring-2 ring-primary-500" + : "bg-gray-100 hover:bg-gray-200" )} > {ic} @@ -352,8 +373,8 @@ export default function EditTaskModal({ open, onClose, task }) { type="button" onClick={() => setColor(c)} className={clsx( - 'w-8 h-8 rounded-full transition-all', - color === c ? 'ring-2 ring-offset-2 ring-gray-400 scale-110' : '' + "w-8 h-8 rounded-full transition-all", + color === c ? "ring-2 ring-offset-2 ring-gray-400 scale-110" : "" )} style={{ backgroundColor: c }} /> @@ -367,7 +388,7 @@ export default function EditTaskModal({ open, onClose, task }) { disabled={updateMutation.isPending} className="btn btn-primary w-full" > - {updateMutation.isPending ? 'Сохраняем...' : 'Сохранить изменения'} + {updateMutation.isPending ? "Сохраняем..." : "Сохранить изменения"} +

+ 2. Скопируй Chat ID из ответа бота и вставь ниже +

+ + +
+ + setChatId(e.target.value.replace(/\D/g, ""))} + placeholder="Например: 123456789" + className="input" + /> +
+ + + + {/* Notifications Section */} +
+
+
+ +
+
+

Уведомления

+

Настрой push-уведомления

+
+
+ + +
+ + {/* Timezone Section */} +
+
+
+ +
+
+

Часовой пояс

+

Для корректных напоминаний

+
+
+ + +
+ + {/* Save Button */} + {hasChanges && ( + + )} + + {mutation.isSuccess && !hasChanges && ( +
+ ✅ Настройки сохранены +
+ )} + + + + + ) +}