mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
Merge Tag v0.2.20
This commit is contained in:
@@ -7,8 +7,7 @@ import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { currentUserAtom, settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { INITIAL_DUE, INITIAL_RECURRENCE_RULE, MAX_COIN_LIMIT, QUICK_DATES } from '@/lib/constants'
|
||||
import { Habit } from '@/lib/types'
|
||||
import { convertHumanReadableFrequencyToMachineReadable, convertMachineReadableFrequencyToHumanReadable, d2t, serializeRRule } from '@/lib/utils'
|
||||
@@ -20,6 +19,7 @@ import { useState } from 'react'
|
||||
import { RRule } from 'rrule'
|
||||
import EmojiPickerButton from './EmojiPickerButton'
|
||||
|
||||
|
||||
interface AddEditHabitModalProps {
|
||||
onClose: () => void
|
||||
onSave: (habit: Omit<Habit, 'id'>) => Promise<void>
|
||||
@@ -42,7 +42,7 @@ export default function AddEditHabitModal({ onClose, onSave, habit, isTask }: Ad
|
||||
timezone: settings.system.timezone
|
||||
}) : (isRecurRule ? INITIAL_RECURRENCE_RULE : INITIAL_DUE);
|
||||
const [ruleText, setRuleText] = useState<string>(initialRuleText)
|
||||
const { currentUser } = useHelpers()
|
||||
const [currentUser] = useAtom(currentUserAtom)
|
||||
const [isQuickDatesOpen, setIsQuickDatesOpen] = useState(false)
|
||||
const [selectedUserIds, setSelectedUserIds] = useState<string[]>((habit?.userIds || []).filter(id => id !== currentUser?.id))
|
||||
const [usersData] = useAtom(usersAtom)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { currentUserAtom, usersAtom } from '@/lib/atoms'
|
||||
import { MAX_COIN_LIMIT } from '@/lib/constants'
|
||||
import { WishlistItemType } from '@/lib/types'
|
||||
import { useAtom } from 'jotai'
|
||||
@@ -37,7 +35,7 @@ export default function AddEditWishlistItemModal({
|
||||
const [coinCost, setCoinCost] = useState(editingItem?.coinCost || 1)
|
||||
const [targetCompletions, setTargetCompletions] = useState<number | undefined>(editingItem?.targetCompletions)
|
||||
const [link, setLink] = useState(editingItem?.link || '')
|
||||
const { currentUser } = useHelpers()
|
||||
const [currentUser] = useAtom(currentUserAtom)
|
||||
const [selectedUserIds, setSelectedUserIds] = useState<string[]>((editingItem?.userIds || []).filter(id => id !== currentUser?.id))
|
||||
const [errors, setErrors] = useState<{ [key: string]: string }>({})
|
||||
const [usersData] = useAtom(usersAtom)
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import { ReactNode, Suspense, useEffect, useState } from 'react'
|
||||
import { useAtom } from 'jotai'
|
||||
import { aboutOpenAtom, pomodoroAtom, userSelectAtom } from '@/lib/atoms'
|
||||
import PomodoroTimer from './PomodoroTimer'
|
||||
import UserSelectModal from './UserSelectModal'
|
||||
import { aboutOpenAtom, currentUserIdAtom, pomodoroAtom, userSelectAtom } from '@/lib/atoms';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import { useSession } from 'next-auth/react'
|
||||
import { ReactNode, useEffect, useState } from 'react'
|
||||
import AboutModal from './AboutModal'
|
||||
import LoadingSpinner from './LoadingSpinner'
|
||||
import PomodoroTimer from './PomodoroTimer'
|
||||
import UserSelectModal from './UserSelectModal'
|
||||
|
||||
export default function ClientWrapper({ children }: { children: ReactNode }) {
|
||||
const [pomo] = useAtom(pomodoroAtom)
|
||||
const [userSelect, setUserSelect] = useAtom(userSelectAtom)
|
||||
const [aboutOpen, setAboutOpen] = useAtom(aboutOpenAtom)
|
||||
const setCurrentUserIdAtom = useSetAtom(currentUserIdAtom)
|
||||
const { data: session, status } = useSession()
|
||||
const currentUserId = session?.user.id
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
@@ -29,6 +30,10 @@ export default function ClientWrapper({ children }: { children: ReactNode }) {
|
||||
}
|
||||
}, [currentUserId, status, userSelect, setUserSelect])
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentUserIdAtom(currentUserId)
|
||||
}, [currentUserId, setCurrentUserIdAtom])
|
||||
|
||||
if (!isMounted) {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { useCoins } from '@/hooks/useCoins'
|
||||
import { settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { currentUserAtom, settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { MAX_COIN_LIMIT } from '@/lib/constants'
|
||||
import { TransactionType } from '@/lib/types'
|
||||
import { d2s, t2d } from '@/lib/utils'
|
||||
@@ -22,7 +21,7 @@ import { TransactionNoteEditor } from './TransactionNoteEditor'
|
||||
|
||||
export default function CoinsManager() {
|
||||
const t = useTranslations('CoinsManager')
|
||||
const { currentUser } = useHelpers()
|
||||
const [currentUser] = useAtom(currentUserAtom)
|
||||
const [selectedUser, setSelectedUser] = useState<string>()
|
||||
const {
|
||||
add,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { Habit } from '@/lib/types';
|
||||
import { Habit, User } from '@/lib/types';
|
||||
import { useHabits } from '@/hooks/useHabits';
|
||||
import { useAtom } from 'jotai';
|
||||
import { pomodoroAtom, settingsAtom } from '@/lib/atoms';
|
||||
import { d2t, getNow, isHabitDueToday } from '@/lib/utils';
|
||||
import { pomodoroAtom, settingsAtom, currentUserAtom } from '@/lib/atoms';
|
||||
import { d2t, getNow, isHabitDueToday, hasPermission } from '@/lib/utils';
|
||||
import { DropdownMenuItem, DropdownMenuSeparator } from '@/components/ui/dropdown-menu';
|
||||
import { ContextMenuItem, ContextMenuSeparator } from '@/components/ui/context-menu';
|
||||
import { Timer, Calendar, Pin, Edit, Archive, ArchiveRestore, Trash2 } from 'lucide-react';
|
||||
import { useHelpers } from '@/lib/client-helpers'; // For permission checks if needed, though useHabits handles most
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
interface HabitContextMenuItemsProps {
|
||||
@@ -28,10 +27,10 @@ export function HabitContextMenuItems({
|
||||
const { saveHabit, archiveHabit, unarchiveHabit } = useHabits();
|
||||
const [settings] = useAtom(settingsAtom);
|
||||
const [, setPomo] = useAtom(pomodoroAtom);
|
||||
const { hasPermission } = useHelpers(); // Assuming useHabits handles permissions for its actions
|
||||
const [currentUser] = useAtom(currentUserAtom);
|
||||
|
||||
const canWrite = hasPermission('habit', 'write'); // For UI disabling if not handled by useHabits' actions
|
||||
const canInteract = hasPermission('habit', 'interact');
|
||||
const canWrite = hasPermission(currentUser, 'habit', 'write'); // For UI disabling if not handled by useHabits' actions
|
||||
const canInteract = hasPermission(currentUser, 'habit', 'interact');
|
||||
|
||||
const MenuItemComponent = context === 'daily-overview' ? ContextMenuItem : DropdownMenuItem;
|
||||
const MenuSeparatorComponent = context === 'daily-overview' ? ContextMenuSeparator : DropdownMenuSeparator;
|
||||
|
||||
@@ -6,12 +6,11 @@ import {
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useHabits } from '@/hooks/useHabits'
|
||||
import { settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { currentUserAtom, settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { Habit, User } from '@/lib/types'
|
||||
import { convertMachineReadableFrequencyToHumanReadable, getCompletionsForToday, isTaskOverdue } from '@/lib/utils'
|
||||
import { convertMachineReadableFrequencyToHumanReadable, getCompletionsForToday, hasPermission, isTaskOverdue } from '@/lib/utils'
|
||||
import { useAtom } from 'jotai'
|
||||
import { Check, Coins, Edit, MoreVertical, Pin, Undo2 } from 'lucide-react'; // Removed unused icons
|
||||
import { Check, Coins, Edit, MoreVertical, Pin, Undo2 } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useEffect, useState } from 'react'
|
||||
@@ -45,7 +44,7 @@ const renderUserAvatars = (habit: Habit, currentUser: User | null, usersData: {
|
||||
|
||||
|
||||
export default function HabitItem({ habit, onEdit, onDelete }: HabitItemProps) {
|
||||
const { completeHabit, undoComplete, archiveHabit, unarchiveHabit, saveHabit } = useHabits()
|
||||
const { completeHabit, undoComplete } = useHabits()
|
||||
const [settings] = useAtom(settingsAtom)
|
||||
const completionsToday = getCompletionsForToday({ habit, timezone: settings.system.timezone })
|
||||
const target = habit.targetCompletions || 1
|
||||
@@ -53,10 +52,10 @@ export default function HabitItem({ habit, onEdit, onDelete }: HabitItemProps) {
|
||||
const [isHighlighted, setIsHighlighted] = useState(false)
|
||||
const t = useTranslations('HabitItem');
|
||||
const [usersData] = useAtom(usersAtom)
|
||||
const { currentUser, hasPermission } = useHelpers()
|
||||
const canWrite = hasPermission('habit', 'write')
|
||||
const canInteract = hasPermission('habit', 'interact')
|
||||
const pathname = usePathname();
|
||||
const [currentUser] = useAtom(currentUserAtom)
|
||||
const canWrite = hasPermission(currentUser, 'habit', 'write')
|
||||
const canInteract = hasPermission(currentUser, 'habit', 'interact')
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from 'next/link'
|
||||
import { NavDisplayProps, NavItemType } from './Navigation';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useHelpers } from '@/lib/client-helpers';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { NavDisplayProps } from './Navigation';
|
||||
|
||||
export default function MobileNavDisplay({ navItems }: NavDisplayProps) {
|
||||
const pathname = usePathname();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import { useAtom } from 'jotai'
|
||||
import { coinsAtom, habitsAtom, wishlistAtom, usersAtom } from '@/lib/atoms'
|
||||
import { coinsAtom, habitsAtom, wishlistAtom, usersAtom, currentUserAtom } from '@/lib/atoms'
|
||||
import { Bell } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useTranslations } from 'next-intl';
|
||||
@@ -14,12 +14,11 @@ import {
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { updateLastNotificationReadTimestamp } from '@/app/actions/data';
|
||||
import { d2t, getNow, t2d } from '@/lib/utils';
|
||||
import { useHelpers } from '@/lib/client-helpers';
|
||||
import { User, CoinTransaction } from '@/lib/types';
|
||||
|
||||
export default function NotificationBell() {
|
||||
const t = useTranslations('NotificationBell');
|
||||
const { currentUser } = useHelpers();
|
||||
const [currentUser] = useAtom(currentUserAtom);
|
||||
const [coinsData] = useAtom(coinsAtom)
|
||||
const [habitsData] = useAtom(habitsAtom)
|
||||
const [wishlistData] = useAtom(wishlistAtom)
|
||||
@@ -122,7 +121,7 @@ export default function NotificationBell() {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="p-0 w-80 md:w-96">
|
||||
<NotificationDropdown
|
||||
currentUser={currentUser as User | null} // Cast needed as useHelpers can return undefined initially
|
||||
currentUser={currentUser as User | null} // Cast needed as as currentUser can be undefined
|
||||
unreadNotifications={unreadNotifications}
|
||||
displayedReadNotifications={displayedReadNotifications}
|
||||
habitsData={habitsData} // Pass necessary data down
|
||||
|
||||
@@ -5,8 +5,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
import { aboutOpenAtom, settingsAtom, userSelectAtom } from "@/lib/atoms"
|
||||
import { useHelpers } from "@/lib/client-helpers"
|
||||
import { aboutOpenAtom, currentUserAtom, settingsAtom, userSelectAtom } from "@/lib/atoms"
|
||||
import { useAtom } from "jotai"
|
||||
import { ArrowRightLeft, Crown, Info, LogOut, Moon, Palette, Settings, Sun, User } from "lucide-react"
|
||||
import { useTranslations } from 'next-intl'
|
||||
@@ -23,7 +22,7 @@ export function Profile() {
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [aboutOpen, setAboutOpen] = useAtom(aboutOpenAtom)
|
||||
const { theme, setTheme } = useTheme()
|
||||
const { currentUser: user } = useHelpers()
|
||||
const [user] = useAtom(currentUserAtom)
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const handleSignOut = async () => {
|
||||
|
||||
@@ -13,8 +13,7 @@ import {
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { serverSettingsAtom, usersAtom } from '@/lib/atoms';
|
||||
import { useHelpers } from '@/lib/client-helpers';
|
||||
import { currentUserAtom, serverSettingsAtom, usersAtom } from '@/lib/atoms';
|
||||
import { Permission } from '@/lib/types';
|
||||
import { passwordSchema, usernameSchema } from '@/lib/zod';
|
||||
import { useAtom, useAtomValue } from 'jotai';
|
||||
@@ -29,6 +28,7 @@ import { Input } from './ui/input';
|
||||
import { Label } from './ui/label';
|
||||
import { Switch } from './ui/switch';
|
||||
|
||||
|
||||
interface UserFormProps {
|
||||
userId?: string; // if provided, we're editing; if not, we're creating
|
||||
onCancel: () => void;
|
||||
@@ -40,7 +40,7 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
|
||||
const [users, setUsersData] = useAtom(usersAtom);
|
||||
const serverSettings = useAtomValue(serverSettingsAtom)
|
||||
const user = userId ? users.users.find(u => u.id === userId) : undefined;
|
||||
const { currentUser } = useHelpers()
|
||||
const [currentUser] = useAtom(currentUserAtom)
|
||||
const getDefaultPermissions = (): Permission[] => [{
|
||||
habit: {
|
||||
write: true,
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
import { signIn } from '@/app/actions/user';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { usersAtom } from '@/lib/atoms';
|
||||
import { useHelpers } from '@/lib/client-helpers';
|
||||
import { currentUserAtom, usersAtom } from '@/lib/atoms';
|
||||
import { SafeUser, User } from '@/lib/types';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Description } from '@radix-ui/react-dialog';
|
||||
@@ -131,7 +130,7 @@ export default function UserSelectModal({ onClose }: { onClose: () => void }) {
|
||||
const [error, setError] = useState('');
|
||||
const [usersData, setUsersData] = useAtom(usersAtom);
|
||||
const users = usersData.users;
|
||||
const { currentUser } = useHelpers();
|
||||
const [currentUser] = useAtom(currentUserAtom);
|
||||
|
||||
|
||||
const handleUserSelect = (userId: string) => {
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { currentUserAtom, usersAtom } from '@/lib/atoms'
|
||||
import { User, WishlistItemType } from '@/lib/types'
|
||||
import { hasPermission } from '@/lib/utils'
|
||||
import { useAtom } from 'jotai'
|
||||
import { Archive, ArchiveRestore, Coins, Edit, Gift, MoreVertical, Trash2 } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
@@ -59,9 +59,9 @@ export default function WishlistItem({
|
||||
isRecentlyRedeemed
|
||||
}: WishlistItemProps) {
|
||||
const t = useTranslations('WishlistItem')
|
||||
const { currentUser, hasPermission } = useHelpers()
|
||||
const canWrite = hasPermission('wishlist', 'write')
|
||||
const canInteract = hasPermission('wishlist', 'interact')
|
||||
const [currentUser] = useAtom(currentUserAtom)
|
||||
const canWrite = hasPermission(currentUser, 'wishlist', 'write')
|
||||
const canInteract = hasPermission(currentUser, 'wishlist', 'interact')
|
||||
const [usersData] = useAtom(usersAtom)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user