diff --git a/task_yell/src/app/home/page.tsx b/task_yell/src/app/home/page.tsx index 7b4a524..16a701c 100644 --- a/task_yell/src/app/home/page.tsx +++ b/task_yell/src/app/home/page.tsx @@ -2,7 +2,6 @@ import { DateTimeInput } from "@/components/date-time-input"; import { Button } from "@/components/ui/button"; -import { Calendar } from "@/components/ui/calendar"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, @@ -12,11 +11,6 @@ import { } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; import { Select, SelectContent, @@ -43,13 +37,12 @@ import { addMonths, eachDayOfInterval, endOfMonth, + endOfWeek, format, - getDay, - isFuture, isSameDay, isSameMonth, - isToday, startOfMonth, + startOfWeek, subMonths, } from "date-fns"; import { ja } from "date-fns/locale"; @@ -96,90 +89,27 @@ type Event = { isLocked: boolean; }; +type StickyNote = { + id: number; + title: string; +}; + const priorityColors: Record = { low: "bg-blue-100 dark:bg-blue-900", medium: "bg-yellow-100 dark:bg-yellow-900", high: "bg-red-100 dark:bg-red-900", }; -const categoryIcons: Record = { - work: ( - - - - - ), - personal: ( - - - - ), - shopping: ( - - - - ), - health: ( - - - - ), - other: ( - - - - ), -}; - function EventCreator({ onSave, onCancel, + initialTitle = "", }: { onSave: (event: Event) => void; onCancel: () => void; + initialTitle?: string; }) { - const [title, setTitle] = useState(""); + const [title, setTitle] = useState(initialTitle); const [description, setDescription] = useState(""); const [date] = useState(new Date()); const [startTime] = useState("10:00"); @@ -231,7 +161,7 @@ function EventCreator({
-
+
-
+ +
@@ -335,34 +266,32 @@ function EventCreator({ ); } -export default function Page() { - const [todos, setTodos] = useState([]); +export default function Home() { + const [todos] = useState([]); const [events, setEvents] = useState([]); - const [newTodo, setNewTodo] = useState(""); + const [stickyNotes, setStickyNotes] = useState([]); + const [newStickyNote, setNewStickyNote] = useState(""); const [selectedDate, setSelectedDate] = useState(new Date()); - const [todoDate, setTodoDate] = useState(selectedDate); - const [selectedDateTodos, setSelectedDateTodos] = useState([]); - const [priority, setPriority] = useState("medium"); - const [category, setCategory] = useState("other"); const [currentMonth, setCurrentMonth] = useState(new Date()); const [isModalOpen, setIsModalOpen] = useState(false); const [modalState, setModalState] = useState< "minimized" | "partial" | "full" >("partial"); const [searchTerm, setSearchTerm] = useState(""); - const [editingTodo, setEditingTodo] = useState(null); + const [editingStickyNote, setEditingStickyNote] = useState( + null + ); const [isDarkMode, setIsDarkMode] = useState(false); const [isEventModalOpen, setIsEventModalOpen] = useState(false); + const [draggedStickyNote, setDraggedStickyNote] = useState( + null + ); + const [removedStickyNote, setRemovedStickyNote] = useState( + null + ); const dragControls = useDragControls(); const modalRef = useRef(null); - useEffect(() => { - const filteredTodos = todos.filter((todo) => - isSameDay(todo.date, selectedDate) - ); - setSelectedDateTodos(filteredTodos); - }, [selectedDate, todos]); - useEffect(() => { if (isDarkMode) { document.documentElement.classList.add("dark"); @@ -371,66 +300,46 @@ export default function Page() { } }, [isDarkMode]); - const addTodo = () => { - if (newTodo.trim() && todoDate) { - const newTodoItem = { - id: Date.now(), - text: newTodo, - completed: false, - date: todoDate, - priority, - category, - }; - setTodos([...todos, newTodoItem]); - setNewTodo(""); - setTodoDate(new Date()); - setPriority("medium"); - setCategory("other"); - if (isSameDay(todoDate, selectedDate)) { - setSelectedDateTodos([...selectedDateTodos, newTodoItem]); - } - } - }; - - const toggleTodo = (id: number) => { - const updatedTodos = todos.map((todo) => - todo.id === id ? { ...todo, completed: !todo.completed } : todo - ); - setTodos(updatedTodos); - if (selectedDate) { - const updatedSelectedDateTodos = selectedDateTodos.map((todo) => - todo.id === id ? { ...todo, completed: !todo.completed } : todo - ); - setSelectedDateTodos(updatedSelectedDateTodos); + const addStickyNote = () => { + if (newStickyNote.trim()) { + const newNote: StickyNote = { id: Date.now(), title: newStickyNote }; + setStickyNotes([...stickyNotes, newNote]); + setNewStickyNote(""); } }; - const editTodo = (todo: Todo) => { - setEditingTodo(todo); + const editStickyNote = (note: StickyNote) => { + setEditingStickyNote(note); }; - const updateTodo = (updatedTodo: Todo) => { - const updatedTodos = todos.map((todo) => - todo.id === updatedTodo.id ? updatedTodo : todo + const updateStickyNote = (updatedNote: StickyNote) => { + const updatedNotes = stickyNotes.map((note) => + note.id === updatedNote.id ? updatedNote : note ); - setTodos(updatedTodos); - setEditingTodo(null); + setStickyNotes(updatedNotes); + setEditingStickyNote(null); }; - const deleteTodo = (id: number) => { - const updatedTodos = todos.filter((todo) => todo.id !== id); - setTodos(updatedTodos); - setSelectedDateTodos(selectedDateTodos.filter((todo) => todo.id !== id)); + const deleteStickyNote = (id: number) => { + const noteToRemove = stickyNotes.find((note) => note.id === id); + if (noteToRemove) { + setRemovedStickyNote(noteToRemove); + setStickyNotes(stickyNotes.filter((note) => note.id !== id)); + } + if (draggedStickyNote && draggedStickyNote.id === id) { + setDraggedStickyNote(null); + } }; const addEvent = (newEvent: Event) => { setEvents([...events, newEvent]); setIsEventModalOpen(false); + setRemovedStickyNote(null); }; const getDaysInMonth = (date: Date) => { - const start = startOfMonth(date); - const end = endOfMonth(date); + const start = startOfWeek(startOfMonth(date), { weekStartsOn: 0 }); + const end = endOfWeek(endOfMonth(date), { weekStartsOn: 0 }); return eachDayOfInterval({ start, end }); }; @@ -453,8 +362,7 @@ export default function Page() { const renderCalendar = () => { const days = getDaysInMonth(currentMonth); - const firstDayOfMonth = getDay(days[0]); - const weeks = Math.ceil((days.length + firstDayOfMonth) / 7); + const weeks = Math.ceil(days.length / 7); return (
@@ -469,13 +377,10 @@ export default function Page() { ))}
{Array.from({ length: weeks }).map((_, weekIndex) => { - const weekDays = days.slice( - weekIndex * 7 - firstDayOfMonth, - (weekIndex + 1) * 7 - firstDayOfMonth - ); + const weekDays = days.slice(weekIndex * 7, (weekIndex + 1) * 7); const maxEventsInWeek = Math.max( - ...weekDays.map((day) => - day ? getTodoCountForDay(day) + getEventCountForDay(day) : 0 + ...weekDays.map( + (day) => getTodoCountForDay(day) + getEventCountForDay(day) ) ); const weekHeight = @@ -487,68 +392,80 @@ export default function Page() { className="grid grid-cols-7 gap-1" style={{ minHeight: "100px", height: weekHeight }} > - {Array(7) - .fill(null) - .map((_, dayIndex) => { - const day = weekDays[dayIndex]; - if (!day) - return ( -
- ); - - const todoCount = getTodoCountForDay(day); - const eventCount = getEventCountForDay(day); - const isSelected = isSameDay(day, selectedDate); - const isCurrentMonth = isSameMonth(day, currentMonth); - const dayItems = [ - ...todos.filter((todo) => isSameDay(todo.date, day)), - ...events.filter((event) => isSameDay(event.start, day)), - ]; - - return ( - handleDateSelect(day)} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > -
- {format(day, "d")} + {weekDays.map((day) => { + const todoCount = getTodoCountForDay(day); + const eventCount = getEventCountForDay(day); + const isSelected = isSameDay(day, selectedDate); + const isCurrentMonth = isSameMonth(day, currentMonth); + const dayItems = [ + ...todos.filter((todo) => isSameDay(todo.date, day)), + ...events.filter((event) => isSameDay(event.start, day)), + ]; + + return ( + handleDateSelect(day)} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + onDragOver={(e) => { + e.preventDefault(); + e.currentTarget.classList.add( + "bg-blue-100", + "dark:bg-blue-800" + ); + }} + onDragLeave={(e) => { + e.currentTarget.classList.remove( + "bg-blue-100", + "dark:bg-blue-800" + ); + }} + onDrop={(e) => { + e.preventDefault(); + e.currentTarget.classList.remove( + "bg-blue-100", + "dark:bg-blue-800" + ); + if (draggedStickyNote) { + handleDateSelect(day); + setIsEventModalOpen(true); + deleteStickyNote(draggedStickyNote.id); + } + }} + > +
{format(day, "d")}
+ {(todoCount > 0 || eventCount > 0) && ( +
+ {dayItems.slice(0, 2).map((item, index) => ( +
+ {"text" in item ? item.text : item.title} +
+ ))} + {dayItems.length > 2 && ( +
+ +{dayItems.length - 2} more +
+ )}
- {(todoCount > 0 || eventCount > 0) && ( -
- {dayItems.slice(0, 2).map((item, index) => ( -
- {"text" in item ? item.text : item.title} -
- ))} - {dayItems.length > 2 && ( -
- +{dayItems.length - 2} more -
- )} -
- )} -
- ); - })} + )} + + ); + })}
); })} @@ -558,112 +475,60 @@ export default function Page() { const handleDateSelect = (date: Date) => { setSelectedDate(date); - setTodoDate(date); setIsEventModalOpen(true); }; - const TodoItem = ({ todo }: { todo: Todo }) => ( + const StickyNoteItem = ({ note }: { note: StickyNote }) => ( { + setDraggedStickyNote(note); + const dragEvent = e as DragEvent; + if (dragEvent.dataTransfer) { + dragEvent.dataTransfer.effectAllowed = "move"; + dragEvent.dataTransfer.setData("text/plain", note.id.toString()); + } + if (e.currentTarget) { + (e.currentTarget as HTMLElement).style.opacity = "0.5"; + } + }} + onDragEnd={(e) => { + setDraggedStickyNote(null); + if (e.currentTarget) { + (e.currentTarget as HTMLElement).style.opacity = "1"; + } + }} > - toggleTodo(todo.id)} - /> - -
- {categoryIcons[todo.category]} - - {format(todo.date, "yyyy/MM/dd", { locale: ja })} - - -
); - const EventItem = ({ event }: { event: Event }) => ( - -
-

{event.title}

- {!event.isTask && ( - <> -

- {event.description} -

-
- {categoryIcons[event.category]} - - {format(event.start, "yyyy/MM/dd HH:mm", { locale: ja })} -{" "} - {format(event.end, "HH:mm", { locale: ja })} - -
- - )} -
-
- ); - - const filteredTodos = useMemo(() => { - return todos.filter( - (todo) => - todo.text.toLowerCase().includes(searchTerm.toLowerCase()) || - format(todo.date, "yyyy/MM/dd").includes(searchTerm) + const filteredStickyNotes = useMemo(() => { + return stickyNotes.filter((note) => + note.title.toLowerCase().includes(searchTerm.toLowerCase()) ); - }, [todos, searchTerm]); - - const filteredEvents = useMemo(() => { - return events.filter( - (event) => - event.title.toLowerCase().includes(searchTerm.toLowerCase()) || - event.description.toLowerCase().includes(searchTerm.toLowerCase()) || - format(event.start, "yyyy/MM/dd").includes(searchTerm) - ); - }, [events, searchTerm]); - - const sortedTodos = useMemo(() => { - return [...filteredTodos].sort((a, b) => { - if (a.completed === b.completed) { - return b.date.getTime() - a.date.getTime(); - } - return a.completed ? 1 : -1; - }); - }, [filteredTodos]); - - const sortedEvents = useMemo(() => { - return [...filteredEvents].sort( - (a, b) => b.start.getTime() - a.start.getTime() - ); - }, [filteredEvents]); - - const recentTodos = todos - .filter((todo) => isToday(todo.date) || isFuture(todo.date)) - .sort((a, b) => a.date.getTime() - b.date.getTime()) - .slice(0, 5); + }, [stickyNotes, searchTerm]); const handleStatusBarClick = () => { setModalState((prevState) => { @@ -693,7 +558,7 @@ export default function Page() { 設定
-
+

- TODO リスト + 付箋

-
+
setNewTodo(e.target.value)} - placeholder="新しいタスクを入力" + value={newStickyNote} + onChange={(e) => setNewStickyNote(e.target.value)} + placeholder="タイトルを入力" className="flex-grow" /> - - - - - - date && setTodoDate(date)} - initialFocus - /> - - - - -
setSearchTerm(e.target.value)} className="w-full" />
- - {sortedTodos.map((todo) => ( - - ))} - -

- イベント -

- - {sortedEvents.map((event) => ( - - ))} - -
- -
-

- 直近のTODO -

- {recentTodos.length > 0 ? ( -
    - {recentTodos.map((todo) => ( -
  • - -
  • +
    + + {filteredStickyNotes.map((note) => ( + ))} -
- ) : ( -

直近のTODOはありません。

- )} + +
@@ -907,11 +706,11 @@ export default function Page() { > - {format(selectedDate, "yyyy年MM月dd日", { locale: ja })}のTODO + {format(selectedDate, "yyyy年MM月dd日", { locale: ja })}の付箋 - {selectedDateTodos.length > 0 && ( + {filteredStickyNotes.length > 0 && ( - ({selectedDateTodos.length}件) + ({filteredStickyNotes.length}件) )}
@@ -922,7 +721,7 @@ export default function Page() {

{format(selectedDate, "yyyy年MM月dd日", { locale: ja })} - のTODO + の付箋

- - - date && setTodoDate(date)} - initialFocus - /> - - - -
- - - {selectedDateTodos.length > 0 ? ( -
    - {selectedDateTodos.map((todo) => ( -
  • - -
  • + {filteredStickyNotes.length > 0 ? ( +
    + {filteredStickyNotes.map((note) => ( + ))} -
+
) : (

- この日のTODOはありません。 + この日の付箋はありません。

)}
@@ -1008,71 +760,32 @@ export default function Page() { )} - setEditingTodo(null)}> + setEditingStickyNote(null)} + > - タスクを編集 + 付箋を編集 - {editingTodo && ( + {editingStickyNote && (
- setEditingTodo({ ...editingTodo, text: e.target.value }) + setEditingStickyNote({ + ...editingStickyNote, + title: e.target.value, + }) } - placeholder="タスクを入力" + placeholder="タイトルを入力" /> - - - - - - - date && setEditingTodo({ ...editingTodo, date }) - } - initialFocus - /> - - - - -
@@ -1089,7 +802,14 @@ export default function Page() { setIsEventModalOpen(false)} + onCancel={() => { + if (removedStickyNote) { + setStickyNotes([...stickyNotes, removedStickyNote]); + setRemovedStickyNote(null); + } + setIsEventModalOpen(false); + }} + initialTitle={draggedStickyNote ? draggedStickyNote.title : ""} />