Skip to content

Commit

Permalink
Merge pull request #115 from jphacks/split-components
Browse files Browse the repository at this point in the history
コンポーネントを分割
  • Loading branch information
gohan5858 authored Nov 23, 2024
2 parents f8944f2 + 0de5cdc commit c39be6e
Show file tree
Hide file tree
Showing 10 changed files with 997 additions and 920 deletions.
969 changes: 49 additions & 920 deletions task_yell/src/app/home/page.tsx

Large diffs are not rendered by default.

167 changes: 167 additions & 0 deletions task_yell/src/components/calendar-renderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
"use client";

import {
eachDayOfInterval,
endOfMonth,
endOfWeek,
format,
isSameDay,
isSameMonth,
startOfMonth,
startOfWeek,
} from "date-fns";
import { motion } from "framer-motion";
import { Event, Todo, StickyNote } from "@/components/types";
import { priorityColors } from "@/components/priority-colors";

type Props = {
todos: Todo[];
events: Event[];
stickyNotes: StickyNote[];
currentMonth: Date;
selectedDate: Date;
handleDateSelect: (date: Date) => void;
isDarkMode: boolean;
draggedStickyNote: StickyNote | null;
deleteStickyNote: (id: string) => void;
setIsEventModalOpen: (isOpen: boolean) => void;
}

function getDaysInMonth(date: Date) {
const start = startOfWeek(startOfMonth(date), { weekStartsOn: 0 });
const end = endOfWeek(endOfMonth(date), { weekStartsOn: 0 });
return eachDayOfInterval({ start, end });
}

function getTodoCountForDay(todos: Todo[], day: Date) {
return todos.filter((todo) => isSameDay(todo.date, day)).length;
}

function getEventCountForDay(events: Event[], day: Date) {
return events.filter((event) => event.start && isSameDay(event.start, day))
.length;
}

function getTaskIndicatorStyle(isDarkMode: boolean, todoCount: number, eventCount: number) {
const count = todoCount + eventCount;
if (count === 0) return "";
const baseColor = isDarkMode ? "bg-red-" : "bg-red-";
const intensity = Math.min(count * 100, 900);
const colorClass = `${baseColor}${intensity}`;
return `${colorClass} ${count >= 3 ? "animate-pulse" : ""}`;
}

export function CalendarRenderer({
todos, events,
currentMonth, selectedDate, handleDateSelect,
isDarkMode,
draggedStickyNote, deleteStickyNote,
setIsEventModalOpen
}: Props
) {
const days = getDaysInMonth(currentMonth);
const weeks = Math.ceil(days.length / 7);

return (
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
<div className="grid grid-cols-7 gap-1">
{["日", "月", "火", "水", "木", "金", "土"].map((day) => (
<div
key={day}
className="text-center font-semibold text-gray-600 dark:text-gray-300 p-2"
>
{day}
</div>
))}
</div>
{Array.from({ length: weeks }).map((_, weekIndex) => {
const weekDays = days.slice(weekIndex * 7, (weekIndex + 1) * 7);
const maxEventsInWeek = Math.max(
...weekDays.map(
(day) => getTodoCountForDay(todos, day) + getEventCountForDay(events, day),
),
);
const weekHeight =
maxEventsInWeek > 2 ? Math.min(maxEventsInWeek * 20, 100) : "auto";

return (
<div
key={weekIndex}
className="grid grid-cols-7 gap-1"
style={{ minHeight: "100px", height: weekHeight }}
>
{weekDays.map((day) => {
const todoCount = getTodoCountForDay(todos, day);
const eventCount = getEventCountForDay(events, day);
const isSelected = isSameDay(day, selectedDate);
const isCurrentMonth = isSameMonth(day, currentMonth);
const dayItems = [
...todos.filter((todo) => isSameDay(todo.date, day)),
...events.filter(
(event) => event.start && isSameDay(event.start, day),
),
];

return (
<motion.div
key={day.toISOString()}
className={`p-1 border rounded-md cursor-pointer transition-all duration-300 overflow-hidden ${isSelected ? "border-blue-300 dark:border-blue-600" : ""} ${!isCurrentMonth
? "text-gray-400 dark:text-gray-600 bg-gray-100 dark:bg-gray-700"
: ""
} ${getTaskIndicatorStyle(isDarkMode, todoCount, eventCount)} hover:bg-gray-100 dark:hover:bg-gray-700`}
onClick={() => 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);
}
}}
>
<div className="text-right text-sm">{format(day, "d")}</div>
{(todoCount > 0 || eventCount > 0) && (
<div className="mt-1 space-y-1">
{dayItems.slice(0, 2).map((item, index) => (
<div
key={index}
className={`text-xs p-1 rounded ${"text" in item ? priorityColors[item.priority] : priorityColors[item.priority]}`}
>
{"text" in item ? item.text : item.title}
</div>
))}
{dayItems.length > 2 && (
<div className="text-xs text-center font-bold">
+{dayItems.length - 2} more
</div>
)}
</div>
)}
</motion.div>
);
})}
</div>
);
})}
</div>
);
};
61 changes: 61 additions & 0 deletions task_yell/src/components/create-event-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use client";

import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
format,
} from "date-fns";
import { ja } from "date-fns/locale";
import { EventCreator } from "@/components/event-creator";
import { Event, StickyNote } from "@/components/types";

type Props = {
stickyNotes: StickyNote[];
setStickyNotes: (notes: StickyNote[]) => void;
isEventModalOpen: boolean;
setIsEventModalOpen: (isOpen: boolean) => void;
selectedDate: Date;
events: Event[];
addEvent: (newEvent: Event, notification: { date: Date | null; type: "call" | "push" }) => void;
removedStickyNote: StickyNote | null;
setRemovedStickyNote: (note: StickyNote | null) => void;
draggedStickyNote: StickyNote | null;
};

export function CreateEventDialog({
stickyNotes, setStickyNotes,
isEventModalOpen, setIsEventModalOpen,
selectedDate,
events, addEvent,
removedStickyNote, setRemovedStickyNote,
draggedStickyNote,
}: Props) {
return (
<Dialog open={isEventModalOpen} onOpenChange={setIsEventModalOpen}>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle>
{format(selectedDate, "yyyy年MM月dd日", { locale: ja })}の予定
</DialogTitle>
</DialogHeader>
<EventCreator
events={events}
onSave={addEvent}
onCancel={() => {
if (removedStickyNote) {
setStickyNotes([...stickyNotes, removedStickyNote]);
setRemovedStickyNote(null);
}
setIsEventModalOpen(false);
}}
initialTitle={draggedStickyNote ? draggedStickyNote.title : ""}
targetDate={selectedDate}
/>
</DialogContent>
</Dialog>
)
}
54 changes: 54 additions & 0 deletions task_yell/src/components/edit-wantodo-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use client";

import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { StickyNote } from "./types";

type Props = {
editingStickyNote: StickyNote | null;
setEditingStickyNote: (stickyNote: StickyNote | null) => void;
updateStickyNote: (stickyNote: StickyNote) => void;
}

export function EditWantodoDialog({ editingStickyNote, setEditingStickyNote, updateStickyNote }: Props) {
return (
<Dialog
open={!!editingStickyNote}
onOpenChange={() => setEditingStickyNote(null)}
>
<DialogContent>
<DialogHeader>
<DialogTitle>wanTODOを編集</DialogTitle>
</DialogHeader>
{editingStickyNote && (
<div className="space-y-4">
<Input
type="text"
value={editingStickyNote.title}
onChange={(e) =>
setEditingStickyNote({
...editingStickyNote,
title: e.target.value,
})
}
placeholder="タイトルを入力"
/>
<Button
onClick={() =>
editingStickyNote && updateStickyNote(editingStickyNote)
}
>
更新
</Button>
</div>
)}
</DialogContent>
</Dialog>
)
}
Loading

0 comments on commit c39be6e

Please sign in to comment.