diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 17dcc973..246da131 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -25,7 +25,6 @@ "@trpc/client": "11.0.0-next-beta.294", "@trpc/react-query": "11.0.0-next-beta.294", "@trpc/server": "11.0.0-next-beta.294", - "clsx": "^2.1.0", "date-fns": "^2.30.0", "emoji-picker-react": "^4.5.7", "framer-motion": "^10.16.4", @@ -41,7 +40,6 @@ "remark-gfm": "^4.0.0", "sass": "^1.71.0", "superjson": "2.2.1", - "tailwind-merge": "^2.2.1", "zod": "^3.22.4" }, "devDependencies": { diff --git a/apps/nextjs/src/app/_components/auth-showcase.tsx b/apps/nextjs/src/_components/auth-showcase.tsx similarity index 92% rename from apps/nextjs/src/app/_components/auth-showcase.tsx rename to apps/nextjs/src/_components/auth-showcase.tsx index b0c45f3f..1a93e964 100644 --- a/apps/nextjs/src/app/_components/auth-showcase.tsx +++ b/apps/nextjs/src/_components/auth-showcase.tsx @@ -1,7 +1,7 @@ import type { OAuthProviders } from "@acme/auth"; import { auth } from "@acme/auth"; -import { SignIn, SignOut } from "~/components/auth"; +import { SignIn, SignOut } from "~/_components/auth"; interface Props { provider: OAuthProviders; diff --git a/apps/nextjs/src/components/auth.tsx b/apps/nextjs/src/_components/auth.tsx similarity index 90% rename from apps/nextjs/src/components/auth.tsx rename to apps/nextjs/src/_components/auth.tsx index ec5c21c0..df5a136d 100644 --- a/apps/nextjs/src/components/auth.tsx +++ b/apps/nextjs/src/_components/auth.tsx @@ -12,6 +12,7 @@ export function SignIn({ return (
+ + + + + Create new workspace + + + { + createWorkspace.mutate(data); + })} + > + ( + + Name: + + + + + + )} + /> + + ( + + Picture: + + + + + + )} + /> + + + + + + +
+ + ); +} diff --git a/apps/nextjs/src/_components/modals/FillMyDayModal/FillMyDayModal.tsx b/apps/nextjs/src/_components/modals/FillMyDayModal/FillMyDayModal.tsx new file mode 100644 index 00000000..4d7916f7 --- /dev/null +++ b/apps/nextjs/src/_components/modals/FillMyDayModal/FillMyDayModal.tsx @@ -0,0 +1,233 @@ +"use client"; + +import type * as z from "zod"; +import React from "react"; +import { format } from "date-fns"; + +import type { RouterOutputs } from "@acme/api"; +import { DayType } from "@acme/db"; +import { Button } from "@acme/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@acme/ui/dialog"; +import { Form, useFieldArray, useForm } from "@acme/ui/form"; +import { Separator } from "@acme/ui/separator"; +import { toast } from "@acme/ui/toast"; +import { CreateFillMyDaySchema } from "@acme/validators"; + +import { isObjectEmpty } from "~/_utils/common"; +import { api } from "~/trpc/react"; +import { Feelings } from "./_components/Feelings"; +import { Projects } from "./_components/Projects"; +import { ProjectTabs } from "./_components/ProjectTabs"; +import { Status } from "./_components/Status"; +import { Tomorrows } from "./_components/Tomorrows"; + +/* Local constants & types +============================================================================= */ +export const INITIAL_FORM_VALUES = { + type: DayType[DayType.WORKING], + reports: { + description: "", + blockers: "", + hours: 8, + }, + tomorrowsDescription: "", + mood: "", +}; + +/* Props - +============================================================================= */ +interface Props { + projects: RouterOutputs["project"]["byWorkspaceId"]; + date: Date; + sprint?: RouterOutputs["sprint"]["byDateRange"][number]; + workspaceId: string; + userId: string; +} + +/* +============================================================================= */ +export function FillMyDayModal({ + projects, + date, + sprint, + workspaceId, + userId, +}: Props) { + const utils = api.useUtils(); + const updateSprint = api.sprint.update.useMutation({ + async onSuccess() { + toast.success("Your report day updated successfully!"); + + await utils.sprint.invalidate(); + await utils.report.invalidate(); + }, + onError: (err) => { + toast.error( + err?.data?.code === "UNAUTHORIZED" + ? "You must be logged in to update day report" + : "Failed to update day report", + ); + }, + }); + + const createSprint = api.sprint.create.useMutation({ + async onSuccess() { + toast.success("Your daily report successfully created!"); + + await utils.sprint.invalidate(); + await utils.report.invalidate(); + }, + onError: (err) => { + toast.error( + err?.data?.code === "UNAUTHORIZED" + ? "You must be logged in to create day report" + : "Failed to create day report", + ); + }, + }); + + const deleteReports = api.report.deleteMany.useMutation({ + async onSuccess() { + await utils.report.invalidate(); + }, + onError: (err) => { + toast.error( + err?.data?.code === "UNAUTHORIZED" + ? "You must be logged in to delete day report" + : "Failed to delete day report", + ); + }, + }); + + const form = useForm({ + schema: CreateFillMyDaySchema, + defaultValues: { + type: sprint?.type ?? INITIAL_FORM_VALUES.type, + tomorrowsDescription: + sprint?.tomorrowsDescription ?? + INITIAL_FORM_VALUES.tomorrowsDescription, + reports: + sprint?.reports?.map((report) => ({ + projectId: report.project.id, + reportId: report.id, + projectName: report.project.name, + description: + report.description ?? INITIAL_FORM_VALUES.reports.description, + blockers: report.blockers ?? INITIAL_FORM_VALUES.reports.blockers, + hours: report.hours ?? INITIAL_FORM_VALUES.reports.hours, + })) ?? [], + mood: sprint?.mood ?? INITIAL_FORM_VALUES.mood, + }, + }); + + const fieldReportArrays = useFieldArray({ + control: form.control, + name: "reports", + }); + + const onSubmit = async (data: z.infer) => { + console.log("data", data); + const reportIdsToBeDeleted = + sprint?.reports + .filter((report) => !data.reports.find((r) => r.reportId === report.id)) + .map((report) => report.id) ?? []; + + console.log("reportIdsToBeDeleted", reportIdsToBeDeleted); + + if (reportIdsToBeDeleted.length) { + deleteReports.mutate(reportIdsToBeDeleted); + } + + try { + if (isObjectEmpty(sprint)) { + return createSprint.mutate({ + ...data, + userId, + workspaceId, + date, + }); + } + + updateSprint.mutate({ + ...data, + id: sprint!.id, + userId, + workspaceId, + date, + }); + } catch (error) { + // noop + console.log(error); + } + }; + + return ( + + + + + + + + + + +
+ +
+ + +   + + +
+ + + +
+
+ + + + + form.reset()}> + + + + +
+ +
+
+ ); +} diff --git a/apps/nextjs/src/_components/modals/FillMyDayModal/_components/Blockers.tsx b/apps/nextjs/src/_components/modals/FillMyDayModal/_components/Blockers.tsx new file mode 100644 index 00000000..3633cd2e --- /dev/null +++ b/apps/nextjs/src/_components/modals/FillMyDayModal/_components/Blockers.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, + useFormField, +} from "@acme/ui/form"; +import { Icons } from "@acme/ui/icons"; +import { Textarea } from "@acme/ui/textarea"; + +/* Props - +============================================================================= */ +interface Props { + index: number; +} + +/* +============================================================================= */ +export function Blockers({ index }: Props) { + const form = useFormField(); + + return ( + ( + + + + Are you blocked by anything? + + +