Skip to content

Commit

Permalink
Feature/app router (#85)
Browse files Browse the repository at this point in the history
* Migrate to next app router

* Run next/font codemod

* Add month navigation with own pages to persist route

* Added user avatar handling, update user salary with dialog and many other UI changes

* Add work day detail editing

* Add yearly economic overview
  • Loading branch information
tommybarvaag authored Mar 17, 2023
1 parent 2e822b5 commit 5ebd581
Show file tree
Hide file tree
Showing 188 changed files with 5,605 additions and 8,120 deletions.
7 changes: 0 additions & 7 deletions .eslintrc

This file was deleted.

22 changes: 22 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://json.schemastore.org/eslintrc",
"root": true,
"extends": [
"next/core-web-vitals",
"prettier",
"plugin:tailwindcss/recommended"
],
"plugins": ["tailwindcss"],
"rules": {
"tailwindcss/no-custom-classname": "off",
"tailwindcss/classnames-order": "error"
},
"settings": {
"tailwindcss": {
"callees": ["cn"]
},
"next": {
"rootDir": ["apps/*/"]
}
}
}
9 changes: 8 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
.next
.vercel
.vercel
cache
.cache
package.json
package-lock.json
public
CHANGELOG.md
.yarn
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"semi": true,
"singleQuote": false,
"arrowParens": "avoid",
"trailingComma": "none"
"trailingComma": "none",
"plugins": ["prettier-plugin-tailwindcss"]
}
22 changes: 5 additions & 17 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
{
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/*.bk": true,
"**/node_modules": true,
"**/target": true
},
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"editor.wordWrapColumn": 100,
"javascript.updateImportsOnFileMove.enabled": "always",
"typescript.updateImportsOnFileMove.enabled": "always",
"javascript.preferences.importModuleSpecifier": "non-relative",
"typescript.tsdk": "node_modules/.pnpm/[email protected]/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.suggest.autoImports": true,
"typescript.format.enable": true,
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.formatOnSave": true
},
"typescript.tsdk": "node_modules/typescript/lib"
"tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]]
}
8 changes: 8 additions & 0 deletions app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
interface AuthLayoutProps {
children: React.ReactNode
}

export default function AuthLayout({ children }: AuthLayoutProps) {
return <div className="min-h-screen">{children}</div>
}

26 changes: 26 additions & 0 deletions app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { LoginButton } from "@/components/auth/login-button";
import { Icons } from "@/components/icons";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Login",
description: "Login to your account"
};

export default function LoginPage() {
return (
<div className="container flex h-screen w-screen flex-col items-center justify-center">
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<div className="flex flex-col space-y-2 text-center">
<Icons.Logo className="mx-auto" />
<h1 className="text-2xl font-semibold tracking-tight">Welcome back</h1>
<p className="mb-8 text-sm text-neutral-500 dark:text-neutral-400">
Click to login with your Knowit AD account.
</p>
</div>
<div className=""></div>
<LoginButton />
</div>
</div>
);
}
22 changes: 22 additions & 0 deletions app/(dashboard)/dashboard/_components/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Skeleton } from "@/components/ui/skeleton";
import { UserAvatar } from "@/components/user/user-avatar";
import { getCurrentUser } from "@/lib/session";
import { getUserAvatar } from "@/lib/user";

export default async function Avatar() {
const user = await getCurrentUser();

if (!user) {
return <div>Not logged in</div>;
}

const userAvatar = await getUserAvatar(user?.activeDirectoryId);

return <UserAvatar name={user.name} src={userAvatar} />;
}

function AvatarSkeleton() {
return <Skeleton className="h-12 w-12 rounded-full p-0"></Skeleton>;
}

export { AvatarSkeleton };
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { MonthSelect } from "@/components/calendar/month-select";
import { Icons } from "@/components/icons";
import { SalaryDetailsCard } from "@/components/salary/salary-details-card";
import { buttonVariants } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Show } from "@/components/ui/show";
import { UserEditSalaryDetailsDialog } from "@/components/user/user-edit-salary-details-dialog";
import { UserEarningsDetails, type CalendarMonth as CalendarMonthType } from "@/types";
import { Session } from "next-auth";
import Link from "next/link";
import { CalendarMonth } from "./calendar-month";

export default function CalendarMonthWithSalary({
user,
calendarMonth,
userEarnings
}: {
user: Session["user"];
calendarMonth: CalendarMonthType;
userEarnings?: UserEarningsDetails;
}) {
return (
<div className="flex flex-col gap-12 lg:flex-row">
<div className="flex max-w-[380px] flex-col gap-3 lg:min-w-[380px]">
<h2 className="font-bold">
Salary details for {calendarMonth.month} {calendarMonth.year}
</h2>
<div className="grid grid-cols-2 gap-3">
<SalaryDetailsCard heading="Work days">
{userEarnings?.activeCalendarMonthStatistics.workDays.length.toString()}
</SalaryDetailsCard>
<SalaryDetailsCard heading="Work hours">
{userEarnings?.activeCalendarMonthStatistics.workHours.toString()}
</SalaryDetailsCard>
<SalaryDetailsCard heading="Gross salary">
{userEarnings?.activeCalendarMonthStatistics.grossFormatted}
</SalaryDetailsCard>
<SalaryDetailsCard heading="Net salary">
{userEarnings?.activeCalendarMonthStatistics.netFormatted}
</SalaryDetailsCard>
</div>
<Show when={calendarMonth.halfTax}>
<Card className="flex items-center">
<Card.Content className="flex items-center gap-3 p-4">
<div className="flex h-6 w-6 min-w-[1.5rem] items-center justify-center rounded-full border border-emerald-500 text-emerald-500">
<Icons.Check />
</div>
<span className="text-emerald-500">
Salary for {calendarMonth.month} paid with{" "}
<span className="underline">half tax</span> at{" "}
{userEarnings?.nextMonthStatistics?.payDay}
</span>
</Card.Content>
</Card>
</Show>
<UserEditSalaryDetailsDialog user={user} />
</div>
<div className="grow">
<div className="flex items-start justify-between">
<div className="">
<h2 className="text-xs">{calendarMonth.year}</h2>
<MonthSelect
className="max-w-[110px]"
year={calendarMonth.year}
month={calendarMonth.monthNumber}
/>
</div>
<div className="flex items-center gap-2">
{/* Go to previous month if month.MonthNumber is 0, go to previous year and month 11 */}
<Link
className={buttonVariants({ variant: "outline" })}
href={`/dashboard/year/${
calendarMonth.monthNumber === 0 ? calendarMonth.year - 1 : calendarMonth.year
}/month/${calendarMonth.monthNumber === 0 ? 11 : calendarMonth.monthNumber - 1}`}
>
<span className="sr-only">Forrige måned</span>
<Icons.ChevronLeft />
</Link>
{/* Go to current month */}
<Link className={buttonVariants({ variant: "outline" })} href="/dashboard">
<span className="">I dag</span>
</Link>
{/* Go to next month if month.MonthNumber is 11, go to next year and month 0 */}
<Link
className={buttonVariants({ variant: "outline" })}
href={`/dashboard/year/${
calendarMonth.monthNumber === 11 ? calendarMonth.year + 1 : calendarMonth.year
}/month/${calendarMonth.monthNumber === 11 ? 0 : calendarMonth.monthNumber + 1}`}
>
<span className="sr-only">Neste måned</span>
<Icons.ChevronRight />
</Link>
</div>
</div>
<CalendarMonth month={calendarMonth} workDayDetails={userEarnings?.workDayDetails} />
</div>
</div>
);
}
84 changes: 84 additions & 0 deletions app/(dashboard)/dashboard/_components/calendar-month.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { CalendarDay } from "@/components/calendar/calendar-day";
import { Show } from "@/components/ui/show";
import { UserEditWorkDayDetailDialog } from "@/components/user/user-edit-work-day-detail-dialog";
import { getRequestDateNow } from "@/lib/date";
import { cn } from "@/lib/utils";
import { CalendarMonth, UserWorkDayDetail } from "@/types";
import { getCalendarMonthEntries } from "@/utils/calendar-utils";
import * as React from "react";

const CalendarMonth: React.FC<{
month: CalendarMonth;
big?: boolean;
workDayDetails?: UserWorkDayDetail[];
}> = ({ month, big = false, workDayDetails = [], ...other }) => {
const currentDate = getRequestDateNow();
const showWeeks = true && !big;
const holidayInfos = month.days.filter(day => day.holidayInformation);

const calendarEntries = getCalendarMonthEntries(month, currentDate, showWeeks, workDayDetails);

return (
<div className="min-h-[240px] lg:min-h-[410px]">
<div className="flex items-center justify-center gap-3">
<div className="flex items-center justify-center gap-1">
<div className="h-2 w-2 rounded-full border-neutral-50 bg-neutral-50" />
<span className="text-xs">Off work</span>
</div>
<div className="flex items-center justify-center gap-1">
<div className="h-2 w-2 rounded-full border-emerald-500 bg-emerald-500" />
<span className="text-xs">Work</span>
</div>
<div className="flex items-center justify-center gap-1">
<div className="h-2 w-2 rounded-full border-red-500 bg-red-500" />
<span className="text-xs">Non commissioned</span>
</div>
</div>
<div
className={cn("grid", {
"grid-cols-8": showWeeks,
"grid-cols-7": !showWeeks
})}
>
{calendarEntries.map((calendarDay, index) => {
switch (calendarDay.type) {
case "day":
return (
<UserEditWorkDayDetailDialog
key={`calendar-day-${index}`}
calendarDay={calendarDay}
big={big}
holidayInfos={holidayInfos}
/>
);
default:
return (
<CalendarDay
key={`calendar-day-${index}`}
calendarDay={calendarDay}
big={big}
holidayInfos={holidayInfos}
/>
);
}
})}
</div>
<Show when={holidayInfos.length > 0}>
<div className="mt-4">
{holidayInfos.map((day, index) => (
<div
key={`holiday-info-${index}`}
className="mr-1 inline-flex text-xs leading-tight dark:text-zinc-400"
>
{`${day.date.getDate()}.${day.date.getMonth() + 1}: ${day.holidayInformation?.name}${
holidayInfos.length === index + 1 ? "" : ","
}`}
</div>
))}
</div>
</Show>
</div>
);
};

export { CalendarMonth };
Loading

0 comments on commit 5ebd581

Please sign in to comment.