diff --git a/src/app/timely/_component/TimeSheet.tsx b/src/app/timely/_component/TimeSheet.tsx index 0994608..b14b12b 100644 --- a/src/app/timely/_component/TimeSheet.tsx +++ b/src/app/timely/_component/TimeSheet.tsx @@ -1,622 +1,10 @@ -<<<<<<< HEAD -"use client"; - -import { useEffect, useState } from "react"; -import { z } from "zod"; -import { useFieldArray, useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { api } from "@/trpc/react"; -import { Clock, Plus, Trash2, X } from "lucide-react"; - -// Define the form schema with Zod -const formSchema = z.object({ - entries: z.array( - z.object({ - projectId: z.string().optional(), - newProjectName: z.string().min(1, "Project name is required").optional(), - about: z.string().min(1, "Activity description is required"), - hoursWorked: z.coerce - .number() - .positive("Hours must be a positive number"), - remark: z.string().optional(), - }), - ), -}); - -type FormValues = z.infer; - -export default function TimesheetUI() { - const utils = api.useUtils(); - const { data: projects, isLoading: projectsLoading } = - api.project.getToday.useQuery(); - - // Sort activities by descending creation date (newest first) - const sortedProjects = projects?.map((project) => ({ - ...project, - activities: [...project.activities].sort( - (a, b) => - new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), - ), - })); - - const [isAdding, setIsAdding] = useState(false); - const [isDarkMode, setIsDarkMode] = useState(false); - - const { - register, - handleSubmit, - control, - watch, - reset, - formState: { errors, isSubmitting }, - } = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { - entries: [ - { - about: "", - hoursWorked: 1, - remark: "", - }, - ], - }, - }); - - useEffect(() => { - setIsDarkMode(true); - }, [isDarkMode]); - - const { fields, append, remove } = useFieldArray({ - control, - name: "entries", - }); - - const createProject = api.project.create.useMutation({ - onSuccess: async () => { - await utils.project.getAll.invalidate(); - }, - }); - - const createActivity = api.activity.create.useMutation({ - onSuccess: async () => { - await utils.project.getAll.invalidate(); - await utils.project.getToday.invalidate(); - setIsAdding(false); - }, - }); - - const deleteActivity = api.activity.delete.useMutation({ - onSuccess: async () => { - await utils.project.getAll.invalidate(); - await utils.project.getToday.invalidate(); - }, - }); - - const onSubmit = async (data: FormValues) => { - try { - for (const entry of data.entries) { - let projectId = entry.projectId; - - if (entry.projectId === "new" && entry.newProjectName) { - const newProject = await createProject.mutateAsync({ - name: entry.newProjectName, - }); - projectId = newProject.id; - } - - if (projectId) { - await createActivity.mutateAsync({ - projectId, - about: entry.about, - hoursWorked: entry.hoursWorked, - remark: entry.remark ?? undefined, - }); - } - } - - await utils.project.getToday.invalidate(); - const refreshedData = await utils.project.getToday.fetch(); - reset(); - } catch (error) { - console.error("Error submitting form:", error); - } - }; - - const addNewRow = () => { - append({ - about: "", - hoursWorked: 1, - remark: "", - }); - }; - - const handleDelete = async (activityId: string) => { - if (confirm("Are you sure you want to delete this activity?")) { - await deleteActivity.mutateAsync({ id: activityId }); - } - }; - - const formatDate = (date: Date) => { - return new Date(date).toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - }); - }; - - return ( -
- {/* Header */} -
-
-

- Track and manage your daily activities -

-
- -
- - {/* Add Entry Form */} - {isAdding && ( -
-

- New Time Entry -

-
-
- {fields.map((field, index) => { - const isCreatingNewProject = - watch(`entries.${index}.projectId`) === "new"; - return ( -
-
- {/* Project */} -
- - - {isCreatingNewProject && ( -
- - {errors.entries?.[index]?.newProjectName && ( -

- {errors.entries[index]?.newProjectName?.message} -

- )} -
- )} - {errors.entries?.[index]?.projectId && ( -

- {errors.entries[index]?.projectId?.message} -

- )} -
- - {/* Activity */} -
- - - {errors.entries?.[index]?.about && ( -

- {errors.entries[index]?.about?.message} -

- )} -
- - {/* Hours */} -
- - - {errors.entries?.[index]?.hoursWorked && ( -

- {errors.entries[index]?.hoursWorked?.message} -

- )} -
- - {/* Remarks */} -
-
- - -
- -
-
-
- ); - })} -
- -
- - -
-
-
- )} - - {/* Timesheet Records */} -
-
-

- Today's Activities -

- - {projectsLoading ? ( -
-
- Loading your timesheet data... -
-
- ) : !sortedProjects || sortedProjects.length === 0 ? ( -
-
-

No activities recorded today

-

- Add your first time entry to get started -

-
-
- ) : ( -
- - - - - - - - - - - - - {sortedProjects.flatMap((project) => - project.activities.map((activity) => ( - - - - - - - - - )), - )} - -
- Project - - Activity - - Hours - - Time - - Notes - - Actions -
-
- {project.name} -
-
-
- {activity.about} -
-
-
- {activity.hoursWorked}h -
-
-
- {new Date(activity.createdAt).toLocaleTimeString( - [], - { - hour: "2-digit", - minute: "2-digit", - }, - )} -
-
-
- {activity.remark ?? "—"} -
-
- -
-
- )} -
- - {/* Summary Section */} - {sortedProjects && sortedProjects.length > 0 && ( -
-

- Daily Summary -

-
-
-

- Projects Worked On -

-

- {projects?.length ?? 0} -

-
-
-

- Total Activities -

-

- {sortedProjects.reduce( - (acc, project) => acc + project.activities.length, - 0, - )} -

-
-
-

- Total Hours -

-

- {sortedProjects - .reduce( - (acc, project) => - acc + - project.activities.reduce( - (sum, activity) => sum + activity.hoursWorked, - 0, - ), - 0, - ) - .toFixed(2)} - h -

-
-
-
- )} -
-
-); -} -======= "use client"; - -import { api } from "@/trpc/react"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Plus, Trash2, X } from "lucide-react"; import { useEffect, useState } from "react"; -import { useFieldArray, useForm } from "react-hook-form"; import { z } from "zod"; +import { useFieldArray, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { api } from "@/trpc/react"; +import { Clock, Plus, Trash2, X } from "lucide-react"; // Define the form schema with Zod const formSchema = z.object({ @@ -754,507 +142,467 @@ export default function TimesheetUI() { }); }; - return ( -
- {/* Header */} -
-
-

- Time Logger -

-

- Track and manage your daily activities -

-
- + return ( +
+ {/* Header */} +
+
+

+ Track and manage your daily activities +

+ +
- {/* Add Entry Form */} - {isAdding && ( -
-

- New Time Entry -

-
-
- {fields.map((field, index) => { - const isCreatingNewProject = - watch(`entries.${index}.projectId`) === "new"; - return ( -
-
- {/* Project */} -
- - - {isCreatingNewProject && ( -
- - {errors.entries?.[index]?.newProjectName && ( -

- {errors.entries[index]?.newProjectName?.message} -

- )} -
- )} - {errors.entries?.[index]?.projectId && ( -

- {errors.entries[index]?.projectId?.message} -

- )} -
+ {/* Add Entry Form */} + {isAdding && ( +
+

+ New Time Entry +

+ +
+ {fields.map((field, index) => { + const isCreatingNewProject = + watch(`entries.${index}.projectId`) === "new"; + return ( +
+
+ {/* Project */} +
+ + + {isCreatingNewProject && ( +
+ + {errors.entries?.[index]?.newProjectName && ( +

+ {errors.entries[index]?.newProjectName?.message} +

+ )} +
+ )} + {errors.entries?.[index]?.projectId && ( +

+ {errors.entries[index]?.projectId?.message} +

+ )} +
- {/* Activity */} -
- - - {errors.entries?.[index]?.about && ( -

- {errors.entries[index]?.about?.message} -

- )} -
+ {/* Activity */} +
+ + + {errors.entries?.[index]?.about && ( +

+ {errors.entries[index]?.about?.message} +

+ )} +
- {/* Hours */} -
+ {/* Hours */} +
+ + + {errors.entries?.[index]?.hoursWorked && ( +

+ {errors.entries[index]?.hoursWorked?.message} +

+ )} +
+ + {/* Remarks */} +
+
- {errors.entries?.[index]?.hoursWorked && ( -

- {errors.entries[index]?.hoursWorked?.message} -

- )}
+ +
+
+
+ ); + })} +
- {/* Remarks */} -
-
- - +
+ + +
+ +
+ )} + + {/* Timesheet Records */} +
+
+

+ Today's Activities +

+ + {projectsLoading ? ( +
+
+ Loading your timesheet data... +
+
+ ) : !sortedProjects || sortedProjects.length === 0 ? ( +
+
+

No activities recorded today

+

+ Add your first time entry to get started +

+
+
+ ) : ( +
+ + + + + + + + + + + + + {sortedProjects.flatMap((project) => + project.activities.map((activity) => ( + + + + + + + + + )), + )} + +
+ Project + + Activity + + Hours + + Time + + Notes + + Actions +
+
+ {project.name} +
+
+
+ {activity.about} +
+
+
+ {activity.hoursWorked}h +
+
+
+ {new Date(activity.createdAt).toLocaleTimeString( + [], + { + hour: "2-digit", + minute: "2-digit", + }, + )}
+
+
+ {activity.remark ?? "—"} +
+
- - - - ); - })} - +
+
+ )} +
-
- - -
- -
- )} - - {/* Timesheet Records */} -
-
-

- Today's Activities -

- - {projectsLoading ? ( -
-
+

- Loading your timesheet data... -

+ {projects?.length ?? 0} +

- ) : !sortedProjects || sortedProjects.length === 0 ? (
-
-

No activities recorded today

-

- Add your first time entry to get started -

-
-
- ) : ( -
- - - - - - - - - - - - - {sortedProjects.flatMap((project) => - project.activities.map((activity) => ( - - - - - - - - - )), - )} - -
- Project - - Activity - - Hours - - Time - - Notes - - Actions -
-
- {project.name} -
-
-
- {activity.about} -
-
-
- {activity.hoursWorked}h -
-
-
- {new Date(activity.createdAt).toLocaleTimeString( - [], - { - hour: "2-digit", - minute: "2-digit", - }, - )} -
-
-
- {activity.remark ?? "—"} -
-
- -
-
- )} -
- - {/* Summary Section */} - {sortedProjects && sortedProjects.length > 0 && ( -
-

- Daily Summary -

-
-
+

-

- Projects Worked On -

-

- {projects?.length ?? 0} -

-
-
acc + project.activities.length, + 0, + )} +

+
+
+

-

- Total Activities -

-

- {sortedProjects.reduce( - (acc, project) => acc + project.activities.length, - 0, - )} -

-
-
+

-

- Total Hours -

-

- {sortedProjects - .reduce( - (acc, project) => - acc + - project.activities.reduce( - (sum, activity) => sum + activity.hoursWorked, - 0, - ), - 0, - ) - .toFixed(2)} - h -

-
+ {sortedProjects + .reduce( + (acc, project) => + acc + + project.activities.reduce( + (sum, activity) => sum + activity.hoursWorked, + 0, + ), + 0, + ) + .toFixed(2)} + h +

- )} -
+
+ )}
- ); +
+); } ->>>>>>> upstream/main diff --git a/src/app/timely/_component/TimelyApp.tsx b/src/app/timely/_component/TimelyApp.tsx index 74d639e..a39746b 100644 --- a/src/app/timely/_component/TimelyApp.tsx +++ b/src/app/timely/_component/TimelyApp.tsx @@ -1,20 +1,40 @@ "use client"; -import { useMemo } from "react"; -import { Calendar, Clock, LogOut, PieChart, Moon, Sun } from "lucide-react"; import { useState } from "react"; +import { Calendar, LogOut, PieChart, Moon, Sun, Bell, Settings, FileText, CheckSquare } from "lucide-react"; import TimesheetUI from "./TimeSheet"; -import { Bell, Settings } from "lucide-react"; -import CalendarComponent from 'react-calendar'; +import CalendarComponent from "react-calendar"; type AppProps = { developer?: { name: string | null; email: string | null }; }; -// Main Timely App with enhanced premium layout export default function TimelyApp({ developer }: AppProps) { - const [activeView, setActiveView] = useState("daily"); // Options: "daily" or "weekly" + const [activeView, setActiveView] = useState("daily"); const [isDarkMode, setIsDarkMode] = useState(true); + const [calendarDate, setCalendarDate] = useState(new Date()); + const [tasks, setTasks] = useState<{ id: number; text: string; completed: boolean }[]>([]); + const [newTask, setNewTask] = useState(""); + const [notes, setNotes] = useState<{ id: number; title: string; content: string; timestamp: string }[]>([]); + const [newNoteTitle, setNewNoteTitle] = useState(""); + const [newNoteContent, setNewNoteContent] = useState(""); + // Add Note + const addNote = () => { + if (newNoteTitle.trim() && newNoteContent.trim()) { + const timestamp = new Date().toLocaleString(); + setNotes([ + ...notes, + { id: Date.now(), title: newNoteTitle, content: newNoteContent, timestamp }, + ]); + setNewNoteTitle(""); + setNewNoteContent(""); + } + }; + + // Delete Note + const deleteNote = (id: number) => { + setNotes(notes.filter((note) => note.id !== id)); + }; // Get current date information const currentDate = new Date(); const formattedDate = currentDate.toLocaleDateString("en-US", { @@ -23,6 +43,24 @@ export default function TimelyApp({ developer }: AppProps) { month: "long", day: "numeric", }); + const addTask = () => { + if (newTask.trim()) { + setTasks([...tasks, { id: Date.now(), text: newTask, completed: false }]); + setNewTask(""); + } + }; + + const deleteTask = (id: number) => { + setTasks(tasks.filter((task) => task.id !== id)); + }; + + const toggleTaskCompletion = (id: number) => { + setTasks( + tasks.map((task) => + task.id === id ? { ...task, completed: !task.completed } : task + ) + ); + }; return (
- - - - + + + +

@@ -80,41 +118,21 @@ export default function TimelyApp({ developer }: AppProps) { Calendar -

- - {/* User Profile & Logout */} -
-
-
- {developer?.name?.slice(0, 2).toUpperCase() || "NA"} -
-
-
{developer?.name || "User"}
-
- {developer?.email || "user@example.com"} -
-
-
- setActiveView("todo")} > - - Logout - -
+ + To-Do + + +
@@ -146,7 +164,7 @@ export default function TimelyApp({ developer }: AppProps) { )} - {/* User Avatar with dropdown */} + {/* User Avatar with dropdown */}
+

@@ -174,46 +193,168 @@ export default function TimelyApp({ developer }: AppProps) {

- - {/* Main Content Switch */} - {activeView === "calendar" ? ( + {/* To-Do List */} +{activeView === "todo" && (
-<} - nextLabel={>} - navigationLabel={({ label }) => ( - {label} - )} - formatShortWeekday={(locale, date) => - date.toLocaleDateString(locale, { weekday: 'short' }).toUpperCase() - } - tileClassName={({ date, view }) => { - if (view === "month") { - const isSunday = date.getDay() === 0; - const isToday = - date.getDate() === new Date().getDate() && - date.getMonth() === new Date().getMonth() && - date.getFullYear() === new Date().getFullYear(); - return [ - "text-base text-center", - isSunday ? "text-red-500 font-bold" : isDarkMode ? "text-gray-100" : "text-gray-700", - isToday ? "bg-indigo-600 text-white rounded-full font-bold" : "", - ].join(" "); - } - return ""; - }} - tileContent={({ date, view }) => null} +

To-Do List

+
+ setNewTask(e.target.value)} + placeholder="Enter a new task" + className="todo-input" + /> + +
+
    + {tasks.map((task) => ( +
  • + + {task.text} + +
    + + +
    +
  • + ))} +
+
+)} + +{/* Notes Feature */} +{activeView === "notes" && ( +
+

Notes

+
+ setNewNoteTitle(e.target.value)} + placeholder="Enter note title" + className="notes-input" /> +